2 * Copyright (c) 2019 Apple Computer, 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@
30 #include <kern/queue.h>
31 #include <kern/sched_prim.h>
32 #include <machine/machine_routines.h>
35 #include <IOKit/IOLib.h>
36 #include <IOKit/IOPlatformExpert.h>
37 #include <IOKit/IOKitKeysPrivate.h>
38 #include <IOKit/IOPlatformActions.h>
39 #include "IOKitKernelInternal.h"
41 static IOLock
*gIOPlatformActionsLock
;
43 typedef kern_return_t (*iocpu_platform_action_t
)(void * refcon0
, void * refcon1
, uint32_t priority
,
44 void * param1
, void * param2
, void * param3
,
47 struct iocpu_platform_action_entry
{
49 iocpu_platform_action_t action
;
54 boolean_t callout_in_progress
;
55 struct iocpu_platform_action_entry
* alloc_list
;
57 typedef struct iocpu_platform_action_entry iocpu_platform_action_entry_t
;
64 kQueueHaltRestart
= 4,
69 const OSSymbol
* gIOPlatformSleepActionKey
;
70 const OSSymbol
* gIOPlatformWakeActionKey
;
71 const OSSymbol
* gIOPlatformQuiesceActionKey
;
72 const OSSymbol
* gIOPlatformActiveActionKey
;
73 const OSSymbol
* gIOPlatformHaltRestartActionKey
;
74 const OSSymbol
* gIOPlatformPanicActionKey
;
76 static queue_head_t gActionQueues
[kQueueCount
];
77 static const OSSymbol
* gActionSymbols
[kQueueCount
];
80 IOInstallServicePlatformAction(IOService
* service
, uint32_t qidx
);
83 iocpu_add_platform_action(queue_head_t
* queue
, iocpu_platform_action_entry_t
* entry
)
85 iocpu_platform_action_entry_t
* next
;
87 queue_iterate(queue
, next
, iocpu_platform_action_entry_t
*, link
)
89 if (next
->priority
> entry
->priority
) {
90 queue_insert_before(queue
, entry
, next
, iocpu_platform_action_entry_t
*, link
);
94 queue_enter(queue
, entry
, iocpu_platform_action_entry_t
*, link
); // at tail
98 iocpu_remove_platform_action(iocpu_platform_action_entry_t
* entry
)
100 remque(&entry
->link
);
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
)
107 kern_return_t ret
= KERN_SUCCESS
;
108 kern_return_t result
= KERN_SUCCESS
;
109 iocpu_platform_action_entry_t
* next
;
111 queue_iterate(queue
, next
, iocpu_platform_action_entry_t
*, link
)
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
);
124 if (KERN_SUCCESS
== result
) {
131 extern "C" kern_return_t
132 IOCPURunPlatformQuiesceActions(void)
134 assert(preemption_enabled() == false);
135 return iocpu_run_platform_actions(&gActionQueues
[kQueueQuiesce
], 0, 0U - 1,
136 NULL
, NULL
, NULL
, TRUE
);
139 extern "C" kern_return_t
140 IOCPURunPlatformActiveActions(void)
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();
150 extern "C" kern_return_t
151 IOCPURunPlatformHaltRestartActions(uint32_t message
)
153 if (!gActionQueues
[kQueueHaltRestart
].next
) {
154 return kIOReturnNotReady
;
156 return iocpu_run_platform_actions(&gActionQueues
[kQueueHaltRestart
], 0, 0U - 1,
157 (void *)(uintptr_t) message
, NULL
, NULL
, TRUE
);
160 extern "C" kern_return_t
161 IOCPURunPlatformPanicActions(uint32_t message
, uint32_t details
)
163 // Don't allow nested calls of panic actions
164 if (!gActionQueues
[kQueuePanic
].next
) {
165 return kIOReturnNotReady
;
167 return iocpu_run_platform_actions(&gActionQueues
[kQueuePanic
], 0, 0U - 1,
168 (void *)(uintptr_t) message
, (void *)(uintptr_t) details
, NULL
, FALSE
);
171 extern "C" kern_return_t
172 IOCPURunPlatformPanicSyncAction(void *addr
, uint32_t offset
, uint32_t len
)
174 PE_panic_save_context_t context
= {
176 .psc_offset
= offset
,
180 // Don't allow nested calls of panic actions
181 if (!gActionQueues
[kQueuePanic
].next
) {
182 return kIOReturnNotReady
;
184 return iocpu_run_platform_actions(&gActionQueues
[kQueuePanic
], 0, 0U - 1,
185 (void *)(uintptr_t)(kPEPanicSync
), &context
, NULL
, FALSE
);
189 IOPlatformActionsPreSleep(void)
191 iocpu_run_platform_actions(&gActionQueues
[kQueueSleep
], 0, 0U - 1,
192 NULL
, NULL
, NULL
, TRUE
);
196 IOPlatformActionsPostResume(void)
198 iocpu_run_platform_actions(&gActionQueues
[kQueueWake
], 0, 0U - 1,
199 NULL
, NULL
, NULL
, TRUE
);
203 IOPlatformActionsInitialize(void)
205 gIOPlatformActionsLock
= IOLockAlloc();
207 for (uint32_t qidx
= kQueueSleep
; qidx
< kQueueCount
; qidx
++) {
208 queue_init(&gActionQueues
[qidx
]);
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
);
226 IOServicePlatformAction(void * refcon0
, void * refcon1
, uint32_t priority
,
227 void * param1
, void * param2
, void * param3
,
228 const char * service_name
)
231 IOService
* service
= (IOService
*) refcon0
;
232 const OSSymbol
* function
= (const OSSymbol
*) refcon1
;
234 IOLog("%s -> %s\n", function
->getCStringNoCopy(), service_name
);
236 ret
= service
->callPlatformFunction(function
, false,
237 (void *)(uintptr_t) priority
, param1
, param2
, param3
);
243 IOInstallServicePlatformAction(IOService
* service
, uint32_t qidx
)
245 iocpu_platform_action_entry_t
* entry
;
248 const OSSymbol
* key
= gActionSymbols
[qidx
];
249 queue_head_t
* queue
= &gActionQueues
[qidx
];
252 num
= OSDynamicCast(OSNumber
, service
->getProperty(key
));
264 queue_iterate(queue
, entry
, iocpu_platform_action_entry_t
*, link
)
266 if (service
== entry
->refcon0
) {
271 entry
= IONew(iocpu_platform_action_entry_t
, 1);
272 entry
->action
= &IOServicePlatformAction
;
273 entry
->name
= service
->getName();
274 priority
= num
->unsigned32BitValue();
276 entry
->priority
= -priority
;
278 entry
->priority
= priority
;
280 entry
->refcon0
= service
;
281 entry
->refcon1
= (void *) key
;
282 entry
->callout_in_progress
= FALSE
;
284 iocpu_add_platform_action(queue
, entry
);
290 IOInstallServicePlatformActions(IOService
* service
)
292 IOLockLock(gIOPlatformActionsLock
);
294 IOInstallServicePlatformAction(service
, kQueueHaltRestart
);
295 IOInstallServicePlatformAction(service
, kQueuePanic
);
297 IOLockUnlock(gIOPlatformActionsLock
);
299 return kIOReturnSuccess
;
303 IOInstallServiceSleepPlatformActions(IOService
* service
)
305 IOLockLock(gIOPlatformActionsLock
);
307 for (uint32_t qidx
= kQueueSleep
; qidx
<= kQueueActive
; qidx
++) {
308 IOInstallServicePlatformAction(service
, qidx
);
311 IOLockUnlock(gIOPlatformActionsLock
);
313 return kIOReturnSuccess
;
317 IORemoveServicePlatformActions(IOService
* service
)
319 iocpu_platform_action_entry_t
* entry
;
320 iocpu_platform_action_entry_t
* next
;
322 IOLockLock(gIOPlatformActionsLock
);
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
)) {
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);
336 IOLockUnlock(gIOPlatformActionsLock
);
338 return kIOReturnSuccess
;