2 * Copyright (c) 2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
39 #include "hv_io_notifier.h"
41 static LCK_GRP_DECLARE(ion_lock_grp
, "io notifier");
43 typedef struct hv_ion_entry
{
44 LIST_ENTRY(hv_ion_entry
) list
;
52 mach_port_name_t port_name
;
55 LIST_HEAD(io_notifier_list
, hv_ion_entry
);
58 struct io_notifier_list list
;
63 * Lookup a matching notifier and return it.
65 static hv_ion_entry_t
*
66 hv_io_notifier_grp_lookup(const hv_ion_grp_t
*grp
, const hv_ion_entry_t
*key
)
68 hv_ion_entry_t
*ion
= NULL
;
70 LIST_FOREACH(ion
, &grp
->list
, list
) {
71 if (ion
->addr
!= key
->addr
) {
75 if (!(ion
->flags
& kHV_ION_ANY_SIZE
) && ion
->size
!= key
->size
) {
79 if (!(ion
->flags
& kHV_ION_ANY_VALUE
) && ion
->value
!= key
->value
) {
83 if (ion
->port_name
!= key
->port_name
) {
87 if (ion
->flags
!= key
->flags
) {
99 * Return KERN_SUCCESS if the notifier was added, an error otherwise.
102 hv_io_notifier_grp_add(hv_ion_grp_t
*grp
, const hv_ion_t
*notifier
)
104 hv_ion_entry_t
*ion
= NULL
;
106 ion
= kalloc(sizeof(*ion
));
108 return KERN_RESOURCE_SHORTAGE
;
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
;
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
));
125 lck_rw_lock_exclusive(&grp
->lock
);
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
));
134 LIST_INSERT_HEAD(&grp
->list
, ion
, list
);
136 lck_rw_done(&grp
->lock
);
142 * Remove and free a notifier.
143 * Return KERN_SUCCESS if the notifier was removed, an error otherwise.
146 hv_io_notifier_grp_remove(hv_ion_grp_t
*grp
, const hv_ion_t
*notifier
)
148 hv_ion_entry_t ion
= {};
149 hv_ion_entry_t
*entry
= NULL
;
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
;
157 lck_rw_lock_exclusive(&grp
->lock
);
159 entry
= hv_io_notifier_grp_lookup(grp
, &ion
);
161 lck_rw_done(&grp
->lock
);
165 LIST_REMOVE(entry
, list
);
167 lck_rw_done(&grp
->lock
);
169 ipc_port_release_send(entry
->port
);
170 kfree(entry
, sizeof(*entry
));
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.
181 hv_io_notifier_grp_fire(hv_ion_grp_t
*grp
, uint64_t addr
, size_t size
,
184 kern_return_t kr
= KERN_FAILURE
;
185 hv_ion_entry_t
*ion
= NULL
;
188 lck_rw_lock_shared(&grp
->lock
);
190 LIST_FOREACH(ion
, &grp
->list
, list
) {
191 if (ion
->addr
!= addr
) {
195 if (!(ion
->flags
& kHV_ION_ANY_SIZE
) && ion
->size
!= size
) {
199 if (!(ion
->flags
& kHV_ION_ANY_VALUE
) && ion
->value
!= value
) {
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
,
216 kr
= mach_msg_send_from_kernel_with_options(&msg
.header
, sizeof(msg
),
217 MACH_SEND_TIMEOUT
, MACH_MSG_TIMEOUT_NONE
);
220 * A timeout will occur when the queue is full. Ignore it if so
223 if (kr
== MACH_SEND_TIMED_OUT
&& !(ion
->flags
& kHV_ION_EXIT_FULL
)) {
224 kr
= MACH_MSG_SUCCESS
;
227 if (kr
!= MACH_MSG_SUCCESS
) {
235 lck_rw_done(&grp
->lock
);
236 return fired
? KERN_SUCCESS
: KERN_FAILURE
;
240 hv_io_notifier_grp_alloc(hv_ion_grp_t
**grp_p
)
242 hv_ion_grp_t
*grp
= kalloc(sizeof(*grp
));
245 return KERN_RESOURCE_SHORTAGE
;
247 bzero(grp
, sizeof(*grp
));
249 lck_rw_init(&grp
->lock
, &ion_lock_grp
, LCK_ATTR_NULL
);
256 hv_io_notifier_grp_free(hv_ion_grp_t
**grp_p
)
258 hv_ion_grp_t
*grp
= *grp_p
;
260 while (!LIST_EMPTY(&grp
->list
)) {
261 hv_ion_entry_t
*ion
= LIST_FIRST(&grp
->list
);
263 LIST_REMOVE(ion
, list
);
265 ipc_port_release_send(ion
->port
);
266 kfree(ion
, sizeof(*ion
));
269 lck_rw_destroy(&grp
->lock
, &ion_lock_grp
);
271 kfree(grp
, sizeof(*grp
));