]>
Commit | Line | Data |
---|---|---|
f427ee49 A |
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 | } |