+
+extern "C" kern_return_t
+IOCPURunPlatformPanicSyncAction(void *addr, uint32_t offset, uint32_t len)
+{
+ PE_panic_save_context_t context = {
+ .psc_buffer = addr,
+ .psc_offset = offset,
+ .psc_length = len
+ };
+
+ // Don't allow nested calls of panic actions
+ if (!gActionQueues[kQueuePanic].next) {
+ return kIOReturnNotReady;
+ }
+ return iocpu_run_platform_actions(&gActionQueues[kQueuePanic], 0, 0U - 1,
+ (void *)(uintptr_t)(kPEPanicSync), &context, NULL, FALSE);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static kern_return_t
+IOServicePlatformAction(void * refcon0, void * refcon1, uint32_t priority,
+ void * param1, void * param2, void * param3,
+ const char * service_name)
+{
+ IOReturn ret;
+ IOService * service = (IOService *) refcon0;
+ const OSSymbol * function = (const OSSymbol *) refcon1;
+
+ kprintf("%s -> %s\n", function->getCStringNoCopy(), service_name);
+
+ ret = service->callPlatformFunction(function, false,
+ (void *)(uintptr_t) priority, param1, param2, param3);
+
+ return ret;
+}
+
+static void
+IOInstallServicePlatformAction(IOService * service, uint32_t qidx)
+{
+ iocpu_platform_action_entry_t * entry;
+ OSNumber * num;
+ uint32_t priority;
+ const OSSymbol * key = gActionSymbols[qidx];
+ queue_head_t * queue = &gActionQueues[qidx];
+ bool reverse;
+ bool uniq;
+
+ num = OSDynamicCast(OSNumber, service->getProperty(key));
+ if (!num) {
+ return;
+ }
+
+ reverse = false;
+ uniq = false;
+ switch (qidx) {
+ case kQueueWake:
+ case kQueueActive:
+ reverse = true;
+ break;
+ case kQueueHaltRestart:
+ case kQueuePanic:
+ uniq = true;
+ break;
+ }
+ if (uniq) {
+ queue_iterate(queue, entry, iocpu_platform_action_entry_t *, link)
+ {
+ if (service == entry->refcon0) {
+ return;
+ }
+ }
+ }
+
+ entry = IONew(iocpu_platform_action_entry_t, 1);
+ entry->action = &IOServicePlatformAction;
+ entry->name = service->getName();
+ priority = num->unsigned32BitValue();
+ if (reverse) {
+ entry->priority = -priority;
+ } else {
+ entry->priority = priority;
+ }
+ entry->refcon0 = service;
+ entry->refcon1 = (void *) key;
+ entry->callout_in_progress = FALSE;
+
+ iocpu_add_platform_action(queue, entry);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+void
+IOCPUInitialize(void)
+{
+ gIOCPUsLock = IOLockAlloc();
+ gIOCPUs = OSArray::withCapacity(1);
+
+ for (uint32_t qidx = kQueueSleep; qidx < kQueueCount; qidx++) {
+ queue_init(&gActionQueues[qidx]);
+ }
+
+ gIOCPUStateKey = OSSymbol::withCStringNoCopy("IOCPUState");
+
+ gIOCPUStateNames[kIOCPUStateUnregistered] =
+ OSString::withCStringNoCopy("Unregistered");
+ gIOCPUStateNames[kIOCPUStateUninitalized] =
+ OSString::withCStringNoCopy("Uninitalized");
+ gIOCPUStateNames[kIOCPUStateStopped] =
+ OSString::withCStringNoCopy("Stopped");
+ gIOCPUStateNames[kIOCPUStateRunning] =
+ OSString::withCStringNoCopy("Running");
+
+ gIOPlatformSleepActionKey = gActionSymbols[kQueueSleep]
+ = OSSymbol::withCStringNoCopy(kIOPlatformSleepActionKey);
+ gIOPlatformWakeActionKey = gActionSymbols[kQueueWake]
+ = OSSymbol::withCStringNoCopy(kIOPlatformWakeActionKey);
+ gIOPlatformQuiesceActionKey = gActionSymbols[kQueueQuiesce]
+ = OSSymbol::withCStringNoCopy(kIOPlatformQuiesceActionKey);
+ gIOPlatformActiveActionKey = gActionSymbols[kQueueActive]
+ = OSSymbol::withCStringNoCopy(kIOPlatformActiveActionKey);
+ gIOPlatformHaltRestartActionKey = gActionSymbols[kQueueHaltRestart]
+ = OSSymbol::withCStringNoCopy(kIOPlatformHaltRestartActionKey);
+ gIOPlatformPanicActionKey = gActionSymbols[kQueuePanic]
+ = OSSymbol::withCStringNoCopy(kIOPlatformPanicActionKey);
+}
+
+IOReturn
+IOInstallServicePlatformActions(IOService * service)
+{
+ IOLockLock(gIOCPUsLock);
+
+ IOInstallServicePlatformAction(service, kQueueHaltRestart);
+ IOInstallServicePlatformAction(service, kQueuePanic);
+
+ IOLockUnlock(gIOCPUsLock);
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IORemoveServicePlatformActions(IOService * service)
+{
+ iocpu_platform_action_entry_t * entry;
+ iocpu_platform_action_entry_t * next;
+
+ IOLockLock(gIOCPUsLock);
+
+ for (uint32_t qidx = kQueueSleep; qidx < kQueueCount; qidx++) {
+ next = (typeof(entry))queue_first(&gActionQueues[qidx]);
+ while (!queue_end(&gActionQueues[qidx], &next->link)) {
+ entry = next;
+ next = (typeof(entry))queue_next(&entry->link);
+ if (service == entry->refcon0) {
+ iocpu_remove_platform_action(entry);
+ IODelete(entry, iocpu_platform_action_entry_t, 1);
+ }
+ }
+ }
+
+ IOLockUnlock(gIOCPUsLock);
+
+ return kIOReturnSuccess;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+PE_cpu_start(cpu_id_t target,
+ vm_offset_t start_paddr, vm_offset_t arg_paddr)
+{
+ IOCPU *targetCPU = (IOCPU *)target;
+
+ if (targetCPU == NULL) {
+ return KERN_FAILURE;
+ }
+ return targetCPU->startCPU(start_paddr, arg_paddr);
+}
+
+void
+PE_cpu_halt(cpu_id_t target)
+{
+ IOCPU *targetCPU = (IOCPU *)target;
+
+ targetCPU->haltCPU();
+}
+
+void
+PE_cpu_signal(cpu_id_t source, cpu_id_t target)
+{
+ IOCPU *sourceCPU = (IOCPU *)source;
+ IOCPU *targetCPU = (IOCPU *)target;
+
+ sourceCPU->signalCPU(targetCPU);
+}
+
+void
+PE_cpu_signal_deferred(cpu_id_t source, cpu_id_t target)
+{
+ IOCPU *sourceCPU = (IOCPU *)source;
+ IOCPU *targetCPU = (IOCPU *)target;
+
+ sourceCPU->signalCPUDeferred(targetCPU);
+}
+
+void
+PE_cpu_signal_cancel(cpu_id_t source, cpu_id_t target)
+{
+ IOCPU *sourceCPU = (IOCPU *)source;
+ IOCPU *targetCPU = (IOCPU *)target;
+
+ sourceCPU->signalCPUCancel(targetCPU);
+}
+
+void
+PE_cpu_machine_init(cpu_id_t target, boolean_t bootb)
+{
+ IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target);
+
+ if (targetCPU == NULL) {
+ panic("%s: invalid target CPU %p", __func__, target);
+ }
+
+ targetCPU->initCPU(bootb);
+#if defined(__arm__) || defined(__arm64__)
+ if (!bootb && (targetCPU->getCPUNumber() == (UInt32)master_cpu)) {
+ ml_set_is_quiescing(false);
+ }
+#endif /* defined(__arm__) || defined(__arm64__) */
+}
+
+void
+PE_cpu_machine_quiesce(cpu_id_t target)
+{
+ IOCPU *targetCPU = (IOCPU*)target;
+#if defined(__arm__) || defined(__arm64__)
+ if (targetCPU->getCPUNumber() == (UInt32)master_cpu) {
+ ml_set_is_quiescing(true);
+ }
+#endif /* defined(__arm__) || defined(__arm64__) */
+ targetCPU->quiesceCPU();
+}
+
+#if defined(__arm__) || defined(__arm64__)
+static perfmon_interrupt_handler_func pmi_handler = NULL;
+
+kern_return_t
+PE_cpu_perfmon_interrupt_install_handler(perfmon_interrupt_handler_func handler)
+{
+ pmi_handler = handler;
+
+ return KERN_SUCCESS;
+}
+
+void
+PE_cpu_perfmon_interrupt_enable(cpu_id_t target, boolean_t enable)
+{
+ IOCPU *targetCPU = (IOCPU*)target;
+
+ if (targetCPU == nullptr) {
+ return;
+ }
+
+ if (enable) {
+ targetCPU->getProvider()->registerInterrupt(1, targetCPU, (IOInterruptAction)pmi_handler, NULL);
+ targetCPU->getProvider()->enableInterrupt(1);
+ } else {
+ targetCPU->getProvider()->disableInterrupt(1);
+ }
+}
+#endif
+