xref: /xnu-12377.61.12/osfmk/kern/hv_io_notifier.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /*
2  * Copyright (c) 2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <kern/hv_support.h>
30 #include <kern/ipc_kobject.h>
31 #include <kern/ipc_mig.h>
32 #include <kern/kalloc.h>
33 #include <kern/locks.h>
34 #include <kern/task.h>
35 #include <mach/port.h>
36 #include <sys/queue.h>
37 
38 #include <stdbool.h>
39 
40 #include "hv_io_notifier.h"
41 
42 static LCK_GRP_DECLARE(ion_lock_grp, "io notifier");
43 
44 typedef struct hv_ion_entry {
45 	LIST_ENTRY(hv_ion_entry) list;
46 
47 	uint64_t           addr;
48 	size_t             size;
49 	uint64_t           value;
50 	uint32_t           flags;
51 
52 	mach_port_t        port;
53 	mach_port_name_t   port_name;
54 } hv_ion_entry_t;
55 
56 LIST_HEAD(io_notifier_list, hv_ion_entry);
57 
58 struct hv_ion_grp {
59 	struct io_notifier_list list;
60 	lck_rw_t lock;
61 };
62 
63 /*
64  * Lookup a matching notifier and return it.
65  */
66 static hv_ion_entry_t *
hv_io_notifier_grp_lookup(const hv_ion_grp_t * grp,const hv_ion_entry_t * key)67 hv_io_notifier_grp_lookup(const hv_ion_grp_t *grp, const hv_ion_entry_t *key)
68 {
69 	hv_ion_entry_t *ion = NULL;
70 
71 	LIST_FOREACH(ion, &grp->list, list) {
72 		if (ion->addr != key->addr) {
73 			continue;
74 		}
75 
76 		if (!(ion->flags & kHV_ION_ANY_SIZE) && ion->size != key->size) {
77 			continue;
78 		}
79 
80 		if (!(ion->flags & kHV_ION_ANY_VALUE) && ion->value != key->value) {
81 			continue;
82 		}
83 
84 		if (ion->port_name != key->port_name) {
85 			continue;
86 		}
87 
88 		if (ion->flags != key->flags) {
89 			continue;
90 		}
91 
92 		return ion;
93 	}
94 
95 	return NULL;
96 }
97 
98 /*
99  * Add a new notifier.
100  * Return KERN_SUCCESS if the notifier was added, an error otherwise.
101  */
102 kern_return_t
hv_io_notifier_grp_add(hv_ion_grp_t * grp,const hv_ion_t * notifier)103 hv_io_notifier_grp_add(hv_ion_grp_t *grp, const hv_ion_t *notifier)
104 {
105 	hv_ion_entry_t *ion = NULL;
106 
107 	ion = kalloc_type(hv_ion_entry_t, Z_WAITOK | Z_NOFAIL);
108 
109 	ion->addr = notifier->addr;
110 	ion->size = notifier->size;
111 	ion->value = notifier->value;
112 	ion->flags = notifier->flags;
113 	ion->port_name = notifier->port_name;
114 
115 	kern_return_t ret = ipc_typed_port_copyin_send(current_task()->itk_space,
116 	    ion->port_name, IOT_ANY, &ion->port);
117 
118 	if (!IP_VALID(ion->port)) {
119 		ret = KERN_FAILURE;
120 	}
121 
122 	if (ret != KERN_SUCCESS) {
123 		kfree_type(hv_ion_entry_t, ion);
124 		return ret;
125 	}
126 
127 	lck_rw_lock_exclusive(&grp->lock);
128 
129 	if (hv_io_notifier_grp_lookup(grp, ion) != NULL) {
130 		lck_rw_done(&grp->lock);
131 		ipc_typed_port_release_send(ion->port, IOT_ANY);
132 		kfree_type(hv_ion_entry_t, ion);
133 		return KERN_FAILURE;
134 	}
135 
136 	LIST_INSERT_HEAD(&grp->list, ion, list);
137 
138 	lck_rw_done(&grp->lock);
139 
140 	return KERN_SUCCESS;
141 }
142 
143 /*
144  * Remove and free a notifier.
145  * Return KERN_SUCCESS if the notifier was removed, an error otherwise.
146  */
147 kern_return_t
hv_io_notifier_grp_remove(hv_ion_grp_t * grp,const hv_ion_t * notifier)148 hv_io_notifier_grp_remove(hv_ion_grp_t *grp, const hv_ion_t *notifier)
149 {
150 	hv_ion_entry_t ion = {};
151 	hv_ion_entry_t *entry = NULL;
152 
153 	ion.addr = notifier->addr;
154 	ion.size = notifier->size;
155 	ion.value = notifier->value;
156 	ion.flags = notifier->flags;
157 	ion.port_name = notifier->port_name;
158 
159 	lck_rw_lock_exclusive(&grp->lock);
160 
161 	entry = hv_io_notifier_grp_lookup(grp, &ion);
162 	if (entry == NULL) {
163 		lck_rw_done(&grp->lock);
164 		return KERN_FAILURE;
165 	}
166 
167 	LIST_REMOVE(entry, list);
168 
169 	lck_rw_done(&grp->lock);
170 
171 	ipc_typed_port_release_send(entry->port, IOT_ANY);
172 	kfree_type(hv_ion_entry_t, entry);
173 
174 	return KERN_SUCCESS;
175 }
176 
177 /*
178  * Find matching notifiers and notify the port.
179  * Returns KERN_SUCCESS if no errors occurred when sending notifications and at
180  * least one notification was sent.
181  */
182 kern_return_t
hv_io_notifier_grp_fire(hv_ion_grp_t * grp,uint64_t addr,size_t size,uint64_t value)183 hv_io_notifier_grp_fire(hv_ion_grp_t *grp, uint64_t addr, size_t size,
184     uint64_t value)
185 {
186 	kern_return_t kr = KERN_FAILURE;
187 	hv_ion_entry_t *ion = NULL;
188 	bool fired = false;
189 
190 	lck_rw_lock_shared(&grp->lock);
191 
192 	LIST_FOREACH(ion, &grp->list, list) {
193 		if (ion->addr != addr) {
194 			continue;
195 		}
196 
197 		if (!(ion->flags & kHV_ION_ANY_SIZE) && ion->size != size) {
198 			continue;
199 		}
200 
201 		if (!(ion->flags & kHV_ION_ANY_VALUE) && ion->value != value) {
202 			continue;
203 		}
204 
205 		hv_ion_message_t msg = {
206 			.header.msgh_bits         = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
207 			.header.msgh_size         = sizeof(msg),
208 			.header.msgh_remote_port  = ion->port,
209 			.header.msgh_local_port   = MACH_PORT_NULL,
210 			.header.msgh_voucher_port = MACH_PORT_NULL,
211 			.header.msgh_id           = 0,
212 
213 			.addr = addr,
214 			.size = size,
215 			.value = value,
216 		};
217 
218 		kr = mach_msg_send_from_kernel_with_options(&msg.header, sizeof(msg),
219 		    MACH_SEND_TIMEOUT, MACH_MSG_TIMEOUT_NONE);
220 
221 		/*
222 		 * A timeout will occur when the queue is full. Ignore it if so
223 		 * configured.
224 		 */
225 		if (kr == MACH_SEND_TIMED_OUT && !(ion->flags & kHV_ION_EXIT_FULL)) {
226 			kr = MACH_MSG_SUCCESS;
227 		}
228 
229 		if (kr != MACH_MSG_SUCCESS) {
230 			fired = false;
231 			break;
232 		}
233 
234 		fired = true;
235 	}
236 
237 	lck_rw_done(&grp->lock);
238 	return fired ? KERN_SUCCESS : KERN_FAILURE;
239 }
240 
241 kern_return_t
hv_io_notifier_grp_alloc(hv_ion_grp_t ** grp_p)242 hv_io_notifier_grp_alloc(hv_ion_grp_t **grp_p )
243 {
244 	hv_ion_grp_t *grp = kalloc_type(hv_ion_grp_t, Z_WAITOK | Z_ZERO);
245 
246 	if (grp == NULL) {
247 		return KERN_RESOURCE_SHORTAGE;
248 	}
249 
250 	lck_rw_init(&grp->lock, &ion_lock_grp, LCK_ATTR_NULL);
251 
252 	*grp_p = grp;
253 	return KERN_SUCCESS;
254 }
255 
256 void
hv_io_notifier_grp_free(hv_ion_grp_t ** grp_p)257 hv_io_notifier_grp_free(hv_ion_grp_t **grp_p)
258 {
259 	hv_ion_grp_t *grp = *grp_p;
260 
261 	while (!LIST_EMPTY(&grp->list)) {
262 		hv_ion_entry_t *ion = LIST_FIRST(&grp->list);
263 
264 		LIST_REMOVE(ion, list);
265 
266 		ipc_typed_port_release_send(ion->port, IOT_ANY);
267 		kfree_type(hv_ion_entry_t, ion);
268 	}
269 
270 	lck_rw_destroy(&grp->lock, &ion_lock_grp);
271 
272 	kfree_type(struct hv_ion_grp, grp);
273 
274 	*grp_p = NULL;
275 }
276