xref: /xnu-8792.61.2/osfmk/kern/hv_io_notifier.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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_mig.h>
31 #include <kern/kalloc.h>
32 #include <kern/locks.h>
33 #include <mach/port.h>
34 #include <sys/queue.h>
35 #include <ipc/ipc_port.h>
36 
37 #include <stdbool.h>
38 
39 #include "hv_io_notifier.h"
40 
41 static LCK_GRP_DECLARE(ion_lock_grp, "io notifier");
42 
43 typedef struct hv_ion_entry {
44 	LIST_ENTRY(hv_ion_entry) list;
45 
46 	uint64_t           addr;
47 	size_t             size;
48 	uint64_t           value;
49 	uint32_t           flags;
50 
51 	mach_port_t        port;
52 	mach_port_name_t   port_name;
53 } hv_ion_entry_t;
54 
55 LIST_HEAD(io_notifier_list, hv_ion_entry);
56 
57 struct hv_ion_grp {
58 	struct io_notifier_list list;
59 	lck_rw_t lock;
60 };
61 
62 /*
63  * Lookup a matching notifier and return it.
64  */
65 static hv_ion_entry_t *
hv_io_notifier_grp_lookup(const hv_ion_grp_t * grp,const hv_ion_entry_t * key)66 hv_io_notifier_grp_lookup(const hv_ion_grp_t *grp, const hv_ion_entry_t *key)
67 {
68 	hv_ion_entry_t *ion = NULL;
69 
70 	LIST_FOREACH(ion, &grp->list, list) {
71 		if (ion->addr != key->addr) {
72 			continue;
73 		}
74 
75 		if (!(ion->flags & kHV_ION_ANY_SIZE) && ion->size != key->size) {
76 			continue;
77 		}
78 
79 		if (!(ion->flags & kHV_ION_ANY_VALUE) && ion->value != key->value) {
80 			continue;
81 		}
82 
83 		if (ion->port_name != key->port_name) {
84 			continue;
85 		}
86 
87 		if (ion->flags != key->flags) {
88 			continue;
89 		}
90 
91 		return ion;
92 	}
93 
94 	return NULL;
95 }
96 
97 /*
98  * Add a new notifier.
99  * Return KERN_SUCCESS if the notifier was added, an error otherwise.
100  */
101 kern_return_t
hv_io_notifier_grp_add(hv_ion_grp_t * grp,const hv_ion_t * notifier)102 hv_io_notifier_grp_add(hv_ion_grp_t *grp, const hv_ion_t *notifier)
103 {
104 	hv_ion_entry_t *ion = NULL;
105 
106 	ion = kalloc_type(hv_ion_entry_t, Z_WAITOK | Z_NOFAIL);
107 
108 	ion->addr = notifier->addr;
109 	ion->size = notifier->size;
110 	ion->value = notifier->value;
111 	ion->flags = notifier->flags;
112 	ion->port_name = notifier->port_name;
113 
114 	kern_return_t ret = ipc_object_copyin(current_task()->itk_space,
115 	    ion->port_name, MACH_MSG_TYPE_COPY_SEND, (ipc_object_t *)&ion->port, 0,
116 	    NULL, IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND);
117 	if (ret != KERN_SUCCESS) {
118 		kfree_type(hv_ion_entry_t, ion);
119 		return ret;
120 	}
121 
122 	lck_rw_lock_exclusive(&grp->lock);
123 
124 	if (hv_io_notifier_grp_lookup(grp, ion) != NULL) {
125 		lck_rw_done(&grp->lock);
126 		ipc_port_release_send(ion->port);
127 		kfree_type(hv_ion_entry_t, ion);
128 		return KERN_FAILURE;
129 	}
130 
131 	LIST_INSERT_HEAD(&grp->list, ion, list);
132 
133 	lck_rw_done(&grp->lock);
134 
135 	return KERN_SUCCESS;
136 }
137 
138 /*
139  * Remove and free a notifier.
140  * Return KERN_SUCCESS if the notifier was removed, an error otherwise.
141  */
142 kern_return_t
hv_io_notifier_grp_remove(hv_ion_grp_t * grp,const hv_ion_t * notifier)143 hv_io_notifier_grp_remove(hv_ion_grp_t *grp, const hv_ion_t *notifier)
144 {
145 	hv_ion_entry_t ion = {};
146 	hv_ion_entry_t *entry = NULL;
147 
148 	ion.addr = notifier->addr;
149 	ion.size = notifier->size;
150 	ion.value = notifier->value;
151 	ion.flags = notifier->flags;
152 	ion.port_name = notifier->port_name;
153 
154 	lck_rw_lock_exclusive(&grp->lock);
155 
156 	entry = hv_io_notifier_grp_lookup(grp, &ion);
157 	if (entry == NULL) {
158 		lck_rw_done(&grp->lock);
159 		return KERN_FAILURE;
160 	}
161 
162 	LIST_REMOVE(entry, list);
163 
164 	lck_rw_done(&grp->lock);
165 
166 	ipc_port_release_send(entry->port);
167 	kfree_type(hv_ion_entry_t, entry);
168 
169 	return KERN_SUCCESS;
170 }
171 
172 /*
173  * Find matching notifiers and notify the port.
174  * Returns KERN_SUCCESS if no errors occurred when sending notifications and at
175  * least one notification was sent.
176  */
177 kern_return_t
hv_io_notifier_grp_fire(hv_ion_grp_t * grp,uint64_t addr,size_t size,uint64_t value)178 hv_io_notifier_grp_fire(hv_ion_grp_t *grp, uint64_t addr, size_t size,
179     uint64_t value)
180 {
181 	kern_return_t kr = KERN_FAILURE;
182 	hv_ion_entry_t *ion = NULL;
183 	bool fired = false;
184 
185 	lck_rw_lock_shared(&grp->lock);
186 
187 	LIST_FOREACH(ion, &grp->list, list) {
188 		if (ion->addr != addr) {
189 			continue;
190 		}
191 
192 		if (!(ion->flags & kHV_ION_ANY_SIZE) && ion->size != size) {
193 			continue;
194 		}
195 
196 		if (!(ion->flags & kHV_ION_ANY_VALUE) && ion->value != value) {
197 			continue;
198 		}
199 
200 		hv_ion_message_t msg = {
201 			.header.msgh_bits         = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
202 			.header.msgh_size         = sizeof(msg),
203 			.header.msgh_remote_port  = ion->port,
204 			.header.msgh_local_port   = MACH_PORT_NULL,
205 			.header.msgh_voucher_port = MACH_PORT_NULL,
206 			.header.msgh_id           = 0,
207 
208 			.addr = addr,
209 			.size = size,
210 			.value = value,
211 		};
212 
213 		kr = mach_msg_send_from_kernel_with_options(&msg.header, sizeof(msg),
214 		    MACH_SEND_TIMEOUT, MACH_MSG_TIMEOUT_NONE);
215 
216 		/*
217 		 * A timeout will occur when the queue is full. Ignore it if so
218 		 * configured.
219 		 */
220 		if (kr == MACH_SEND_TIMED_OUT && !(ion->flags & kHV_ION_EXIT_FULL)) {
221 			kr = MACH_MSG_SUCCESS;
222 		}
223 
224 		if (kr != MACH_MSG_SUCCESS) {
225 			fired = false;
226 			break;
227 		}
228 
229 		fired = true;
230 	}
231 
232 	lck_rw_done(&grp->lock);
233 	return fired ? KERN_SUCCESS : KERN_FAILURE;
234 }
235 
236 kern_return_t
hv_io_notifier_grp_alloc(hv_ion_grp_t ** grp_p)237 hv_io_notifier_grp_alloc(hv_ion_grp_t **grp_p )
238 {
239 	hv_ion_grp_t *grp = kalloc_type(hv_ion_grp_t, Z_WAITOK | Z_ZERO);
240 
241 	if (grp == NULL) {
242 		return KERN_RESOURCE_SHORTAGE;
243 	}
244 
245 	lck_rw_init(&grp->lock, &ion_lock_grp, LCK_ATTR_NULL);
246 
247 	*grp_p = grp;
248 	return KERN_SUCCESS;
249 }
250 
251 void
hv_io_notifier_grp_free(hv_ion_grp_t ** grp_p)252 hv_io_notifier_grp_free(hv_ion_grp_t **grp_p)
253 {
254 	hv_ion_grp_t *grp = *grp_p;
255 
256 	while (!LIST_EMPTY(&grp->list)) {
257 		hv_ion_entry_t *ion = LIST_FIRST(&grp->list);
258 
259 		LIST_REMOVE(ion, list);
260 
261 		ipc_port_release_send(ion->port);
262 		kfree_type(hv_ion_entry_t, ion);
263 	}
264 
265 	lck_rw_destroy(&grp->lock, &ion_lock_grp);
266 
267 	kfree_type(struct hv_ion_grp, grp);
268 
269 	*grp_p = NULL;
270 }
271