xref: /xnu-8792.61.2/osfmk/kern/host_notify.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
1 /*
2  * Copyright (c) 2003-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 <mach/mach_types.h>
30 #include <mach/mach_host.h>
31 
32 #include <kern/kern_types.h>
33 #include <kern/ipc_kobject.h>
34 #include <kern/host_notify.h>
35 
36 #include <kern/queue.h>
37 
38 #include "mach/host_notify_reply.h"
39 
40 struct host_notify_entry {
41 	queue_chain_t                   entries;
42 	ipc_port_t                      port;
43 	ipc_port_request_index_t        index;
44 };
45 
46 LCK_GRP_DECLARE(host_notify_lock_grp, "host_notify");
47 LCK_MTX_DECLARE(host_notify_lock, &host_notify_lock_grp);
48 
49 static KALLOC_TYPE_DEFINE(host_notify_zone,
50     struct host_notify_entry, KT_DEFAULT);
51 
52 static queue_head_t     host_notify_queue[HOST_NOTIFY_TYPE_MAX + 1] = {
53 	QUEUE_HEAD_INITIALIZER(host_notify_queue[HOST_NOTIFY_CALENDAR_CHANGE]),
54 	QUEUE_HEAD_INITIALIZER(host_notify_queue[HOST_NOTIFY_CALENDAR_SET]),
55 };
56 
57 static mach_msg_id_t    host_notify_replyid[HOST_NOTIFY_TYPE_MAX + 1] = {
58 	HOST_CALENDAR_CHANGED_REPLYID,
59 	HOST_CALENDAR_SET_REPLYID,
60 };
61 
62 kern_return_t
host_request_notification(host_t host,host_flavor_t notify_type,ipc_port_t port)63 host_request_notification(
64 	host_t          host,
65 	host_flavor_t   notify_type,
66 	ipc_port_t      port)
67 {
68 	host_notify_t entry;
69 	kern_return_t kr;
70 
71 	if (host == HOST_NULL) {
72 		return KERN_INVALID_ARGUMENT;
73 	}
74 
75 	if (!IP_VALID(port)) {
76 		return KERN_INVALID_CAPABILITY;
77 	}
78 
79 	if (notify_type > HOST_NOTIFY_TYPE_MAX || notify_type < 0) {
80 		return KERN_INVALID_ARGUMENT;
81 	}
82 
83 	entry = zalloc_flags(host_notify_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
84 	entry->port = port;
85 
86 again:
87 	lck_mtx_lock(&host_notify_lock);
88 
89 	ip_mq_lock(port);
90 	if (ip_active(port)) {
91 		kr = ipc_port_request_hnotify_alloc(port, entry, &entry->index);
92 	} else {
93 		kr = KERN_INVALID_CAPABILITY;
94 	}
95 
96 	if (kr == KERN_SUCCESS) {
97 		/*
98 		 * Preserve original ABI of host-notify ports being immovable
99 		 * as a side effect of being a kobject.
100 		 *
101 		 * Unlike the original ABI, multiple registrations
102 		 * for the same port are now allowed.
103 		 */
104 		port->ip_immovable_receive = true;
105 		enqueue_tail(&host_notify_queue[notify_type], &entry->entries);
106 	}
107 
108 	lck_mtx_unlock(&host_notify_lock);
109 
110 	if (kr == KERN_NO_SPACE) {
111 		kr = ipc_port_request_grow(port);
112 		/* port unlocked */
113 		if (kr == KERN_SUCCESS) {
114 			goto again;
115 		}
116 	} else {
117 		ip_mq_unlock(port);
118 	}
119 
120 	if (kr != KERN_SUCCESS) {
121 		zfree(host_notify_zone, entry);
122 	}
123 
124 	return kr;
125 }
126 
127 void
host_notify_cancel(host_notify_t entry)128 host_notify_cancel(host_notify_t entry)
129 {
130 	ipc_port_t port;
131 
132 	lck_mtx_lock(&host_notify_lock);
133 	remqueue((queue_entry_t)entry);
134 	port = entry->port;
135 	lck_mtx_unlock(&host_notify_lock);
136 
137 	zfree(host_notify_zone, entry);
138 	ipc_port_release_sonce(port);
139 }
140 
141 static void
host_notify_all(host_flavor_t notify_type,mach_msg_header_t * msg,mach_msg_size_t msg_size)142 host_notify_all(
143 	host_flavor_t           notify_type,
144 	mach_msg_header_t       *msg,
145 	mach_msg_size_t         msg_size)
146 {
147 	queue_head_t  send_queue = QUEUE_HEAD_INITIALIZER(send_queue);
148 	queue_entry_t e;
149 	host_notify_t entry;
150 	ipc_port_t    port;
151 
152 	lck_mtx_lock(&host_notify_lock);
153 
154 	qe_foreach_safe(e, &host_notify_queue[notify_type]) {
155 		entry = (host_notify_t)e;
156 		port  = entry->port;
157 
158 		ip_mq_lock(port);
159 		if (ip_active(port)) {
160 			ipc_port_request_cancel(port, IPR_HOST_NOTIFY,
161 			    entry->index);
162 			remqueue(e);
163 			enqueue_tail(&send_queue, e);
164 		} else {
165 			/*
166 			 * leave the entry in place,
167 			 * we're racing with ipc_port_dnnotify()
168 			 * which will call host_notify_cancel().
169 			 */
170 		}
171 		ip_mq_unlock(port);
172 	}
173 
174 	lck_mtx_unlock(&host_notify_lock);
175 
176 	if (queue_empty(&send_queue)) {
177 		return;
178 	}
179 
180 	msg->msgh_bits =
181 	    MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, 0);
182 	msg->msgh_local_port = MACH_PORT_NULL;
183 	msg->msgh_voucher_port = MACH_PORT_NULL;
184 	msg->msgh_id = host_notify_replyid[notify_type];
185 
186 	qe_foreach_safe(e, &send_queue) {
187 		entry = (host_notify_t)e;
188 		port  = entry->port;
189 
190 		zfree(host_notify_zone, entry);
191 
192 		msg->msgh_remote_port = port;
193 		(void)mach_msg_send_from_kernel_proper(msg, msg_size);
194 	}
195 }
196 
197 void
host_notify_calendar_change(void)198 host_notify_calendar_change(void)
199 {
200 	__Request__host_calendar_changed_t      msg;
201 
202 	host_notify_all(HOST_NOTIFY_CALENDAR_CHANGE, &msg.Head, sizeof(msg));
203 }
204 
205 void
host_notify_calendar_set(void)206 host_notify_calendar_set(void)
207 {
208 	__Request__host_calendar_set_t  msg;
209 
210 	host_notify_all(HOST_NOTIFY_CALENDAR_SET, &msg.Head, sizeof(msg));
211 }
212