]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/hv_io_notifier.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / hv_io_notifier.c
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 *
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
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(sizeof(*ion));
107 if (ion == NULL) {
108 return KERN_RESOURCE_SHORTAGE;
109 }
110
111 ion->addr = notifier->addr;
112 ion->size = notifier->size;
113 ion->value = notifier->value;
114 ion->flags = notifier->flags;
115 ion->port_name = notifier->port_name;
116
117 kern_return_t ret = ipc_object_copyin(current_task()->itk_space,
118 ion->port_name, MACH_MSG_TYPE_COPY_SEND, (ipc_object_t *)&ion->port, 0,
119 NULL, IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND);
120 if (ret != KERN_SUCCESS) {
121 kfree(ion, sizeof(*ion));
122 return ret;
123 }
124
125 lck_rw_lock_exclusive(&grp->lock);
126
127 if (hv_io_notifier_grp_lookup(grp, ion) != NULL) {
128 lck_rw_done(&grp->lock);
129 ipc_port_release_send(ion->port);
130 kfree(ion, sizeof(*ion));
131 return KERN_FAILURE;
132 }
133
134 LIST_INSERT_HEAD(&grp->list, ion, list);
135
136 lck_rw_done(&grp->lock);
137
138 return KERN_SUCCESS;
139 }
140
141 /*
142 * Remove and free a notifier.
143 * Return KERN_SUCCESS if the notifier was removed, an error otherwise.
144 */
145 kern_return_t
146 hv_io_notifier_grp_remove(hv_ion_grp_t *grp, const hv_ion_t *notifier)
147 {
148 hv_ion_entry_t ion = {};
149 hv_ion_entry_t *entry = NULL;
150
151 ion.addr = notifier->addr;
152 ion.size = notifier->size;
153 ion.value = notifier->value;
154 ion.flags = notifier->flags;
155 ion.port_name = notifier->port_name;
156
157 lck_rw_lock_exclusive(&grp->lock);
158
159 entry = hv_io_notifier_grp_lookup(grp, &ion);
160 if (entry == NULL) {
161 lck_rw_done(&grp->lock);
162 return KERN_FAILURE;
163 }
164
165 LIST_REMOVE(entry, list);
166
167 lck_rw_done(&grp->lock);
168
169 ipc_port_release_send(entry->port);
170 kfree(entry, sizeof(*entry));
171
172 return KERN_SUCCESS;
173 }
174
175 /*
176 * Find matching notifiers and notify the port.
177 * Returns KERN_SUCCESS if no errors occurred when sending notifications and at
178 * least one notification was sent.
179 */
180 kern_return_t
181 hv_io_notifier_grp_fire(hv_ion_grp_t *grp, uint64_t addr, size_t size,
182 uint64_t value)
183 {
184 kern_return_t kr = KERN_FAILURE;
185 hv_ion_entry_t *ion = NULL;
186 bool fired = false;
187
188 lck_rw_lock_shared(&grp->lock);
189
190 LIST_FOREACH(ion, &grp->list, list) {
191 if (ion->addr != addr) {
192 continue;
193 }
194
195 if (!(ion->flags & kHV_ION_ANY_SIZE) && ion->size != size) {
196 continue;
197 }
198
199 if (!(ion->flags & kHV_ION_ANY_VALUE) && ion->value != value) {
200 continue;
201 }
202
203 hv_ion_message_t msg = {
204 .header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
205 .header.msgh_size = sizeof(msg),
206 .header.msgh_remote_port = ion->port,
207 .header.msgh_local_port = MACH_PORT_NULL,
208 .header.msgh_voucher_port = MACH_PORT_NULL,
209 .header.msgh_id = 0,
210
211 .addr = addr,
212 .size = size,
213 .value = value,
214 };
215
216 kr = mach_msg_send_from_kernel_with_options(&msg.header, sizeof(msg),
217 MACH_SEND_TIMEOUT, MACH_MSG_TIMEOUT_NONE);
218
219 /*
220 * A timeout will occur when the queue is full. Ignore it if so
221 * configured.
222 */
223 if (kr == MACH_SEND_TIMED_OUT && !(ion->flags & kHV_ION_EXIT_FULL)) {
224 kr = MACH_MSG_SUCCESS;
225 }
226
227 if (kr != MACH_MSG_SUCCESS) {
228 fired = false;
229 break;
230 }
231
232 fired = true;
233 }
234
235 lck_rw_done(&grp->lock);
236 return fired ? KERN_SUCCESS : KERN_FAILURE;
237 }
238
239 kern_return_t
240 hv_io_notifier_grp_alloc(hv_ion_grp_t **grp_p )
241 {
242 hv_ion_grp_t *grp = kalloc(sizeof(*grp));
243
244 if (grp == NULL) {
245 return KERN_RESOURCE_SHORTAGE;
246 }
247 bzero(grp, sizeof(*grp));
248
249 lck_rw_init(&grp->lock, &ion_lock_grp, LCK_ATTR_NULL);
250
251 *grp_p = grp;
252 return KERN_SUCCESS;
253 }
254
255 void
256 hv_io_notifier_grp_free(hv_ion_grp_t **grp_p)
257 {
258 hv_ion_grp_t *grp = *grp_p;
259
260 while (!LIST_EMPTY(&grp->list)) {
261 hv_ion_entry_t *ion = LIST_FIRST(&grp->list);
262
263 LIST_REMOVE(ion, list);
264
265 ipc_port_release_send(ion->port);
266 kfree(ion, sizeof(*ion));
267 }
268
269 lck_rw_destroy(&grp->lock, &ion_lock_grp);
270
271 kfree(grp, sizeof(*grp));
272
273 *grp_p = NULL;
274 }