]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOPlatformActions.cpp
xnu-7195.101.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPlatformActions.cpp
1 /*
2 * Copyright (c) 2019 Apple Computer, 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 extern "C" {
30 #include <kern/queue.h>
31 #include <kern/sched_prim.h>
32 #include <machine/machine_routines.h>
33 }
34
35 #include <IOKit/IOLib.h>
36 #include <IOKit/IOPlatformExpert.h>
37 #include <IOKit/IOKitKeysPrivate.h>
38 #include <IOKit/IOPlatformActions.h>
39 #include "IOKitKernelInternal.h"
40
41 static IOLock *gIOPlatformActionsLock;
42
43 typedef kern_return_t (*iocpu_platform_action_t)(void * refcon0, void * refcon1, uint32_t priority,
44 void * param1, void * param2, void * param3,
45 const char * name);
46
47 struct iocpu_platform_action_entry {
48 queue_chain_t link;
49 iocpu_platform_action_t action;
50 int32_t priority;
51 const char * name;
52 void * refcon0;
53 void * refcon1;
54 boolean_t callout_in_progress;
55 struct iocpu_platform_action_entry * alloc_list;
56 };
57 typedef struct iocpu_platform_action_entry iocpu_platform_action_entry_t;
58
59 enum {
60 kQueueSleep = 0,
61 kQueueWake = 1,
62 kQueueQuiesce = 2,
63 kQueueActive = 3,
64 kQueueHaltRestart = 4,
65 kQueuePanic = 5,
66 kQueueCount = 6
67 };
68
69 const OSSymbol * gIOPlatformSleepActionKey;
70 const OSSymbol * gIOPlatformWakeActionKey;
71 const OSSymbol * gIOPlatformQuiesceActionKey;
72 const OSSymbol * gIOPlatformActiveActionKey;
73 const OSSymbol * gIOPlatformHaltRestartActionKey;
74 const OSSymbol * gIOPlatformPanicActionKey;
75
76 static queue_head_t gActionQueues[kQueueCount];
77 static const OSSymbol * gActionSymbols[kQueueCount];
78
79 static bool
80 IOInstallServicePlatformAction(IOService * service, uint32_t qidx);
81
82 static void
83 iocpu_add_platform_action(queue_head_t * queue, iocpu_platform_action_entry_t * entry)
84 {
85 iocpu_platform_action_entry_t * next;
86
87 queue_iterate(queue, next, iocpu_platform_action_entry_t *, link)
88 {
89 if (next->priority > entry->priority) {
90 queue_insert_before(queue, entry, next, iocpu_platform_action_entry_t *, link);
91 return;
92 }
93 }
94 queue_enter(queue, entry, iocpu_platform_action_entry_t *, link); // at tail
95 }
96
97 static void
98 iocpu_remove_platform_action(iocpu_platform_action_entry_t * entry)
99 {
100 remque(&entry->link);
101 }
102
103 static kern_return_t
104 iocpu_run_platform_actions(queue_head_t * queue, uint32_t first_priority, uint32_t last_priority,
105 void * param1, void * param2, void * param3, boolean_t allow_nested_callouts)
106 {
107 kern_return_t ret = KERN_SUCCESS;
108 kern_return_t result = KERN_SUCCESS;
109 iocpu_platform_action_entry_t * next;
110
111 queue_iterate(queue, next, iocpu_platform_action_entry_t *, link)
112 {
113 uint32_t pri = (next->priority < 0) ? -next->priority : next->priority;
114 if ((pri >= first_priority) && (pri <= last_priority)) {
115 //kprintf("[%p]", next->action);
116 if (!allow_nested_callouts && !next->callout_in_progress) {
117 next->callout_in_progress = TRUE;
118 ret = (*next->action)(next->refcon0, next->refcon1, pri, param1, param2, param3, next->name);
119 next->callout_in_progress = FALSE;
120 } else if (allow_nested_callouts) {
121 ret = (*next->action)(next->refcon0, next->refcon1, pri, param1, param2, param3, next->name);
122 }
123 }
124 if (KERN_SUCCESS == result) {
125 result = ret;
126 }
127 }
128 return result;
129 }
130
131 extern "C" kern_return_t
132 IOCPURunPlatformQuiesceActions(void)
133 {
134 assert(preemption_enabled() == false);
135 return iocpu_run_platform_actions(&gActionQueues[kQueueQuiesce], 0, 0U - 1,
136 NULL, NULL, NULL, TRUE);
137 }
138
139 extern "C" kern_return_t
140 IOCPURunPlatformActiveActions(void)
141 {
142 assert(preemption_enabled() == false);
143 ml_hibernate_active_pre();
144 kern_return_t result = iocpu_run_platform_actions(&gActionQueues[kQueueActive], 0, 0U - 1,
145 NULL, NULL, NULL, TRUE);
146 ml_hibernate_active_post();
147 return result;
148 }
149
150 extern "C" kern_return_t
151 IOCPURunPlatformHaltRestartActions(uint32_t message)
152 {
153 if (!gActionQueues[kQueueHaltRestart].next) {
154 return kIOReturnNotReady;
155 }
156 return iocpu_run_platform_actions(&gActionQueues[kQueueHaltRestart], 0, 0U - 1,
157 (void *)(uintptr_t) message, NULL, NULL, TRUE);
158 }
159
160 extern "C" kern_return_t
161 IOCPURunPlatformPanicActions(uint32_t message, uint32_t details)
162 {
163 // Don't allow nested calls of panic actions
164 if (!gActionQueues[kQueuePanic].next) {
165 return kIOReturnNotReady;
166 }
167 return iocpu_run_platform_actions(&gActionQueues[kQueuePanic], 0, 0U - 1,
168 (void *)(uintptr_t) message, (void *)(uintptr_t) details, NULL, FALSE);
169 }
170
171 extern "C" kern_return_t
172 IOCPURunPlatformPanicSyncAction(void *addr, uint32_t offset, uint32_t len)
173 {
174 PE_panic_save_context_t context = {
175 .psc_buffer = addr,
176 .psc_offset = offset,
177 .psc_length = len
178 };
179
180 // Don't allow nested calls of panic actions
181 if (!gActionQueues[kQueuePanic].next) {
182 return kIOReturnNotReady;
183 }
184 return iocpu_run_platform_actions(&gActionQueues[kQueuePanic], 0, 0U - 1,
185 (void *)(uintptr_t)(kPEPanicSync), &context, NULL, FALSE);
186 }
187
188 void
189 IOPlatformActionsPreSleep(void)
190 {
191 iocpu_run_platform_actions(&gActionQueues[kQueueSleep], 0, 0U - 1,
192 NULL, NULL, NULL, TRUE);
193 }
194
195 void
196 IOPlatformActionsPostResume(void)
197 {
198 iocpu_run_platform_actions(&gActionQueues[kQueueWake], 0, 0U - 1,
199 NULL, NULL, NULL, TRUE);
200 }
201
202 void
203 IOPlatformActionsInitialize(void)
204 {
205 gIOPlatformActionsLock = IOLockAlloc();
206
207 for (uint32_t qidx = kQueueSleep; qidx < kQueueCount; qidx++) {
208 queue_init(&gActionQueues[qidx]);
209 }
210
211 gIOPlatformSleepActionKey = gActionSymbols[kQueueSleep]
212 = OSSymbol::withCStringNoCopy(kIOPlatformSleepActionKey);
213 gIOPlatformWakeActionKey = gActionSymbols[kQueueWake]
214 = OSSymbol::withCStringNoCopy(kIOPlatformWakeActionKey);
215 gIOPlatformQuiesceActionKey = gActionSymbols[kQueueQuiesce]
216 = OSSymbol::withCStringNoCopy(kIOPlatformQuiesceActionKey);
217 gIOPlatformActiveActionKey = gActionSymbols[kQueueActive]
218 = OSSymbol::withCStringNoCopy(kIOPlatformActiveActionKey);
219 gIOPlatformHaltRestartActionKey = gActionSymbols[kQueueHaltRestart]
220 = OSSymbol::withCStringNoCopy(kIOPlatformHaltRestartActionKey);
221 gIOPlatformPanicActionKey = gActionSymbols[kQueuePanic]
222 = OSSymbol::withCStringNoCopy(kIOPlatformPanicActionKey);
223 }
224
225 static kern_return_t
226 IOServicePlatformAction(void * refcon0, void * refcon1, uint32_t priority,
227 void * param1, void * param2, void * param3,
228 const char * service_name)
229 {
230 IOReturn ret;
231 IOService * service = (IOService *) refcon0;
232 const OSSymbol * function = (const OSSymbol *) refcon1;
233
234 IOLog("%s -> %s\n", function->getCStringNoCopy(), service_name);
235
236 ret = service->callPlatformFunction(function, false,
237 (void *)(uintptr_t) priority, param1, param2, param3);
238
239 return ret;
240 }
241
242 static bool
243 IOInstallServicePlatformAction(IOService * service, uint32_t qidx)
244 {
245 iocpu_platform_action_entry_t * entry;
246 OSNumber * num;
247 uint32_t priority;
248 const OSSymbol * key = gActionSymbols[qidx];
249 queue_head_t * queue = &gActionQueues[qidx];
250 bool reverse;
251
252 num = OSDynamicCast(OSNumber, service->getProperty(key));
253 if (!num) {
254 return true;
255 }
256
257 reverse = false;
258 switch (qidx) {
259 case kQueueWake:
260 case kQueueActive:
261 reverse = true;
262 break;
263 }
264 queue_iterate(queue, entry, iocpu_platform_action_entry_t *, link)
265 {
266 if (service == entry->refcon0) {
267 return true;
268 }
269 }
270
271 entry = IONew(iocpu_platform_action_entry_t, 1);
272 entry->action = &IOServicePlatformAction;
273 entry->name = service->getName();
274 priority = num->unsigned32BitValue();
275 if (reverse) {
276 entry->priority = -priority;
277 } else {
278 entry->priority = priority;
279 }
280 entry->refcon0 = service;
281 entry->refcon1 = (void *) key;
282 entry->callout_in_progress = FALSE;
283
284 iocpu_add_platform_action(queue, entry);
285 return false;
286 }
287
288
289 IOReturn
290 IOInstallServicePlatformActions(IOService * service)
291 {
292 IOLockLock(gIOPlatformActionsLock);
293
294 IOInstallServicePlatformAction(service, kQueueHaltRestart);
295 IOInstallServicePlatformAction(service, kQueuePanic);
296
297 IOLockUnlock(gIOPlatformActionsLock);
298
299 return kIOReturnSuccess;
300 }
301
302 IOReturn
303 IOInstallServiceSleepPlatformActions(IOService * service)
304 {
305 IOLockLock(gIOPlatformActionsLock);
306
307 for (uint32_t qidx = kQueueSleep; qidx <= kQueueActive; qidx++) {
308 IOInstallServicePlatformAction(service, qidx);
309 }
310
311 IOLockUnlock(gIOPlatformActionsLock);
312
313 return kIOReturnSuccess;
314 }
315
316 IOReturn
317 IORemoveServicePlatformActions(IOService * service)
318 {
319 iocpu_platform_action_entry_t * entry;
320 iocpu_platform_action_entry_t * next;
321
322 IOLockLock(gIOPlatformActionsLock);
323
324 for (uint32_t qidx = kQueueSleep; qidx < kQueueCount; qidx++) {
325 next = (typeof(entry))queue_first(&gActionQueues[qidx]);
326 while (!queue_end(&gActionQueues[qidx], &next->link)) {
327 entry = next;
328 next = (typeof(entry))queue_next(&entry->link);
329 if (service == entry->refcon0) {
330 iocpu_remove_platform_action(entry);
331 IODelete(entry, iocpu_platform_action_entry_t, 1);
332 }
333 }
334 }
335
336 IOLockUnlock(gIOPlatformActionsLock);
337
338 return kIOReturnSuccess;
339 }