xref: /xnu-8019.80.24/osfmk/kern/host_notify.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
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 };
44 typedef struct host_notify_entry        *host_notify_t;
45 
46 LCK_GRP_DECLARE(host_notify_lock_grp, "host_notify");
47 LCK_MTX_EARLY_DECLARE(host_notify_lock, &host_notify_lock_grp);
48 
49 static ZONE_DECLARE(host_notify_zone, "host_notify",
50     sizeof(struct host_notify_entry), ZC_NONE);
51 
52 static queue_head_t     host_notify_queue[HOST_NOTIFY_TYPE_MAX + 1];
53 
54 static mach_msg_id_t    host_notify_replyid[HOST_NOTIFY_TYPE_MAX + 1] =
55 { HOST_CALENDAR_CHANGED_REPLYID,
56   HOST_CALENDAR_SET_REPLYID };
57 
58 __startup_func
59 static void
host_notify_init(void)60 host_notify_init(void)
61 {
62 	for (int i = 0; i <= HOST_NOTIFY_TYPE_MAX; i++) {
63 		queue_init(&host_notify_queue[i]);
64 	}
65 }
66 STARTUP(MACH_IPC, STARTUP_RANK_FIRST, host_notify_init);
67 
68 kern_return_t
host_request_notification(host_t host,host_flavor_t notify_type,ipc_port_t port)69 host_request_notification(
70 	host_t          host,
71 	host_flavor_t   notify_type,
72 	ipc_port_t      port)
73 {
74 	host_notify_t entry;
75 	kern_return_t kr;
76 
77 	if (host == HOST_NULL) {
78 		return KERN_INVALID_ARGUMENT;
79 	}
80 
81 	if (!IP_VALID(port)) {
82 		return KERN_INVALID_CAPABILITY;
83 	}
84 
85 	if (notify_type > HOST_NOTIFY_TYPE_MAX || notify_type < 0) {
86 		return KERN_INVALID_ARGUMENT;
87 	}
88 
89 	entry = zalloc_flags(host_notify_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
90 	entry->port = port;
91 
92 	lck_mtx_lock(&host_notify_lock);
93 	kr = ipc_kobject_upgrade(port, entry, IKOT_HOST_NOTIFY);
94 	if (kr == KERN_SUCCESS) {
95 		enqueue_tail(&host_notify_queue[notify_type], (queue_entry_t)entry);
96 	}
97 	lck_mtx_unlock(&host_notify_lock);
98 
99 	if (kr != KERN_SUCCESS) {
100 		zfree(host_notify_zone, entry);
101 	}
102 
103 	return kr;
104 }
105 
106 static void
host_notify_port_destroy(ipc_port_t port)107 host_notify_port_destroy(
108 	ipc_port_t      port)
109 {
110 	host_notify_t entry;
111 
112 	lck_mtx_lock(&host_notify_lock);
113 
114 	entry = ipc_kobject_downgrade_host_notify(port);
115 	if (entry) {
116 		assert(entry->port == port);
117 		remqueue((queue_entry_t)entry);
118 	}
119 
120 	lck_mtx_unlock(&host_notify_lock);
121 
122 	if (entry) {
123 		zfree(host_notify_zone, entry);
124 		ipc_port_release_sonce(port);
125 	}
126 }
127 
128 IPC_KOBJECT_DEFINE(IKOT_HOST_NOTIFY,
129     .iko_op_allow_upgrade   = true,
130     .iko_op_destroy         = host_notify_port_destroy);
131 
132 static void
host_notify_all(host_flavor_t notify_type,mach_msg_header_t * msg,mach_msg_size_t msg_size)133 host_notify_all(
134 	host_flavor_t           notify_type,
135 	mach_msg_header_t       *msg,
136 	mach_msg_size_t         msg_size)
137 {
138 	queue_t notify_queue = &host_notify_queue[notify_type];
139 
140 	lck_mtx_lock(&host_notify_lock);
141 
142 	if (!queue_empty(notify_queue)) {
143 		queue_head_t  send_queue;
144 		host_notify_t entry;
145 
146 		send_queue = *notify_queue;
147 		queue_init(notify_queue);
148 
149 		send_queue.next->prev = &send_queue;
150 		send_queue.prev->next = &send_queue;
151 
152 		msg->msgh_bits =
153 		    MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, 0);
154 		msg->msgh_local_port = MACH_PORT_NULL;
155 		msg->msgh_voucher_port = MACH_PORT_NULL;
156 		msg->msgh_id = host_notify_replyid[notify_type];
157 
158 		while ((entry = (host_notify_t)dequeue(&send_queue)) != NULL) {
159 			ipc_port_t port = entry->port;
160 
161 			ipc_kobject_downgrade_host_notify(port);
162 
163 			lck_mtx_unlock(&host_notify_lock);
164 
165 			zfree(host_notify_zone, entry);
166 
167 			msg->msgh_remote_port = port;
168 
169 			(void)mach_msg_send_from_kernel_proper(msg, msg_size);
170 
171 			lck_mtx_lock(&host_notify_lock);
172 		}
173 	}
174 
175 	lck_mtx_unlock(&host_notify_lock);
176 }
177 
178 void
host_notify_calendar_change(void)179 host_notify_calendar_change(void)
180 {
181 	__Request__host_calendar_changed_t      msg;
182 
183 	host_notify_all(HOST_NOTIFY_CALENDAR_CHANGE, &msg.Head, sizeof(msg));
184 }
185 
186 void
host_notify_calendar_set(void)187 host_notify_calendar_set(void)
188 {
189 	__Request__host_calendar_set_t  msg;
190 
191 	host_notify_all(HOST_NOTIFY_CALENDAR_SET, &msg.Head, sizeof(msg));
192 }
193