X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..bb59bff194111743b33cc36712410b5656329d3c:/iokit/Kernel/IOCPU.cpp diff --git a/iokit/Kernel/IOCPU.cpp b/iokit/Kernel/IOCPU.cpp index b74cb79a5..00504e9eb 100644 --- a/iokit/Kernel/IOCPU.cpp +++ b/iokit/Kernel/IOCPU.cpp @@ -1,16 +1,19 @@ /* * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER @@ -20,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. @@ -34,11 +37,250 @@ extern "C" { #include } +#include + #include #include +#include +#include #include +#include #include +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include + +typedef kern_return_t (*iocpu_platform_action_t)(void * refcon0, void * refcon1, uint32_t priority, + void * param1, void * param2, void * param3, + const char * name); + +struct iocpu_platform_action_entry +{ + queue_chain_t link; + iocpu_platform_action_t action; + int32_t priority; + const char * name; + void * refcon0; + void * refcon1; + struct iocpu_platform_action_entry * alloc_list; +}; +typedef struct iocpu_platform_action_entry iocpu_platform_action_entry_t; + +queue_head_t * +iocpu_get_platform_quiesce_queue(void); + +queue_head_t * +iocpu_get_platform_active_queue(void); + +void +iocpu_platform_cpu_action_init(queue_head_t * quiesce_queue, queue_head_t * init_queue); + +void +iocpu_add_platform_action(queue_head_t * queue, iocpu_platform_action_entry_t * entry); + +void +iocpu_remove_platform_action(iocpu_platform_action_entry_t * entry); + +kern_return_t +iocpu_run_platform_actions(queue_head_t * queue, uint32_t first_priority, uint32_t last_priority, + void * param1, void * param2, void * param3); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define kBootCPUNumber 0 + +static iocpu_platform_action_entry_t * gIOAllActionsQueue; +static queue_head_t gIOSleepActionQueue; +static queue_head_t gIOWakeActionQueue; + +static queue_head_t iocpu_quiesce_queue; +static queue_head_t iocpu_active_queue; + +static queue_head_t gIOHaltRestartActionQueue; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +iocpu_platform_cpu_action_init(queue_head_t * quiesce_queue, __unused queue_head_t * init_queue) +{ +#if 0 + enum { kNumQuiesceActions = 2 }; + static iocpu_platform_action_entry_t quiesce_actions[kNumQuiesceActions] = + { + { { NULL, NULL }, (iocpu_platform_action_t) &clean_mmu_dcache, 97000, 0, 0, NULL }, + { { NULL, NULL }, (iocpu_platform_action_t) &arm_sleep, 99000, 0, 0, NULL }, + }; + unsigned int idx; + + for (idx = 0; idx < kNumQuiesceActions; idx++) + iocpu_add_platform_action(quiesce_queue, &quiesce_actions[idx]); +#endif +} + +queue_head_t * iocpu_get_platform_quiesce_queue(void) +{ + if (!iocpu_quiesce_queue.next) + { + queue_init(&iocpu_quiesce_queue); + queue_init(&iocpu_active_queue); + iocpu_platform_cpu_action_init(&iocpu_quiesce_queue, &iocpu_active_queue); + } + return (&iocpu_quiesce_queue); +} + +queue_head_t * iocpu_get_platform_active_queue(void) +{ + if (!iocpu_active_queue.next) + { + queue_init(&iocpu_quiesce_queue); + queue_init(&iocpu_active_queue); + iocpu_platform_cpu_action_init(&iocpu_quiesce_queue, &iocpu_active_queue); + } + return (&iocpu_active_queue); +} + +void iocpu_add_platform_action(queue_head_t * queue, iocpu_platform_action_entry_t * entry) +{ + iocpu_platform_action_entry_t * next; + + queue_iterate(queue, next, iocpu_platform_action_entry_t *, link) + { + if (next->priority > entry->priority) + { + queue_insert_before(queue, entry, next, iocpu_platform_action_entry_t *, link); + return; + } + } + queue_enter(queue, entry, iocpu_platform_action_entry_t *, link); // at tail +} + +void iocpu_remove_platform_action(iocpu_platform_action_entry_t * entry) +{ + remque(&entry->link); +} + +kern_return_t +iocpu_run_platform_actions(queue_head_t * queue, uint32_t first_priority, uint32_t last_priority, + void * param1, void * param2, void * param3) +{ + kern_return_t ret = KERN_SUCCESS; + kern_return_t result = KERN_SUCCESS; + iocpu_platform_action_entry_t * next; + + queue_iterate(queue, next, iocpu_platform_action_entry_t *, link) + { + uint32_t pri = (next->priority < 0) ? -next->priority : next->priority; + if ((pri >= first_priority) && (pri <= last_priority)) + { + //kprintf("[%p]", next->action); + ret = (*next->action)(next->refcon0, next->refcon1, pri, param1, param2, param3, next->name); + } + if (KERN_SUCCESS == result) + result = ret; + } + return (result); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern "C" kern_return_t +IOCPURunPlatformQuiesceActions(void) +{ + return (iocpu_run_platform_actions(iocpu_get_platform_quiesce_queue(), 0, 0U-1, + NULL, NULL, NULL)); +} + +extern "C" kern_return_t +IOCPURunPlatformActiveActions(void) +{ + return (iocpu_run_platform_actions(iocpu_get_platform_active_queue(), 0, 0U-1, + NULL, NULL, NULL)); +} + +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, + const OSSymbol * key, queue_head_t * queue, + bool reverse) +{ + OSNumber * num; + iocpu_platform_action_entry_t * entry; + uint32_t priority; + + num = OSDynamicCast(OSNumber, service->getProperty(key)); + if (!num) + 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; + + iocpu_add_platform_action(queue, entry); + entry->alloc_list = gIOAllActionsQueue; + gIOAllActionsQueue = entry; +} + +extern "C" kern_return_t +IOCPURunPlatformHaltRestartActions(uint32_t message) +{ + kern_return_t ret; + IORegistryIterator * iter; + OSOrderedSet * all; + IOService * service; + + if (!gIOHaltRestartActionQueue.next) + { + queue_init(&gIOHaltRestartActionQueue); + iter = IORegistryIterator::iterateOver(gIOServicePlane, + kIORegistryIterateRecursively); + if (iter) + { + all = 0; + do + { + if (all) all->release(); + all = iter->iterateAll(); + } + while (!iter->isValid()); + iter->release(); + if (all) + { + while((service = (IOService *) all->getFirstObject())) + { + IOInstallServicePlatformAction(service, gIOPlatformHaltRestartActionKey, &gIOHaltRestartActionQueue, false); + all->removeObject(service); + } + all->release(); + } + } + } + ret = iocpu_run_platform_actions(&gIOHaltRestartActionQueue, 0, 0U-1, + (void *)(uintptr_t) message, NULL, NULL); + return (ret); +} /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -66,20 +308,21 @@ void PE_cpu_signal(cpu_id_t source, cpu_id_t target) if (sourceCPU && targetCPU) sourceCPU->signalCPU(targetCPU); } -void PE_cpu_machine_init(cpu_id_t target, boolean_t boot) +void PE_cpu_machine_init(cpu_id_t target, boolean_t bootb) { IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target); - if (targetCPU) targetCPU->initCPU(boot); + if (targetCPU) targetCPU->initCPU(bootb); } void PE_cpu_machine_quiesce(cpu_id_t target) { IOCPU *targetCPU = OSDynamicCast(IOCPU, (OSObject *)target); - + if (targetCPU) targetCPU->quiesceCPU(); } + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super IOService @@ -102,27 +345,114 @@ static OSString *gIOCPUStateNames[kIOCPUStateCount]; void IOCPUSleepKernel(void) { - long cnt, numCPUs; - IOCPU *target; - - numCPUs = gIOCPUs->getCount(); - - // Sleep the CPUs. - cnt = numCPUs; - while (cnt--) { - target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt)); - if (target->getCPUState() == kIOCPUStateRunning) { - target->haltCPU(); + long cnt, numCPUs; + IOCPU *target; + IOCPU *bootCPU = NULL; + IOPMrootDomain *rootDomain = IOService::getPMRootDomain(); + + kprintf("IOCPUSleepKernel\n"); + + IORegistryIterator * iter; + OSOrderedSet * all; + IOService * service; + + rootDomain->tracePoint( kIOPMTracePointSleepPlatformActions ); + + queue_init(&gIOSleepActionQueue); + queue_init(&gIOWakeActionQueue); + queue_init(&gIOHaltRestartActionQueue); + + iter = IORegistryIterator::iterateOver( gIOServicePlane, + kIORegistryIterateRecursively ); + if( iter) + { + all = 0; + do + { + if (all) + all->release(); + all = iter->iterateAll(); + } + while (!iter->isValid()); + iter->release(); + + if (all) + { + while((service = (IOService *) all->getFirstObject())) + { + IOInstallServicePlatformAction(service, gIOPlatformSleepActionKey, &gIOSleepActionQueue, false); + IOInstallServicePlatformAction(service, gIOPlatformWakeActionKey, &gIOWakeActionQueue, true); + IOInstallServicePlatformAction(service, gIOPlatformQuiesceActionKey, iocpu_get_platform_quiesce_queue(), false); + IOInstallServicePlatformAction(service, gIOPlatformActiveActionKey, iocpu_get_platform_active_queue(), true); + IOInstallServicePlatformAction(service, gIOPlatformHaltRestartActionKey, &gIOHaltRestartActionQueue, false); + all->removeObject(service); + } + all->release(); + } } - } - - // Wake the other CPUs. - for (cnt = 1; cnt < numCPUs; cnt++) { - target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt)); - if (target->getCPUState() == kIOCPUStateStopped) { - processor_start(target->getMachProcessor()); + + iocpu_run_platform_actions(&gIOSleepActionQueue, 0, 0U-1, + NULL, NULL, NULL); + + rootDomain->tracePoint( kIOPMTracePointSleepCPUs ); + + numCPUs = gIOCPUs->getCount(); + // Sleep the CPUs. + cnt = numCPUs; + while (cnt--) + { + target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt)); + + // We make certain that the bootCPU is the last to sleep + // We'll skip it for now, and halt it after finishing the + // non-boot CPU's. + if (target->getCPUNumber() == kBootCPUNumber) + { + bootCPU = target; + } else if (target->getCPUState() == kIOCPUStateRunning) + { + target->haltCPU(); + } + } + + rootDomain->tracePoint( kIOPMTracePointSleepPlatformDriver ); + + // Now sleep the boot CPU. + if (bootCPU) + bootCPU->haltCPU(); + + rootDomain->tracePoint( kIOPMTracePointWakePlatformActions ); + + iocpu_run_platform_actions(&gIOWakeActionQueue, 0, 0U-1, + NULL, NULL, NULL); + + iocpu_platform_action_entry_t * entry; + while ((entry = gIOAllActionsQueue)) + { + gIOAllActionsQueue = entry->alloc_list; + iocpu_remove_platform_action(entry); + IODelete(entry, iocpu_platform_action_entry_t, 1); + } + + if (!queue_empty(&gIOSleepActionQueue)) panic("gIOSleepActionQueue"); + if (!queue_empty(&gIOWakeActionQueue)) panic("gIOWakeActionQueue"); + if (!queue_empty(&gIOHaltRestartActionQueue)) panic("gIOHaltRestartActionQueue"); + gIOHaltRestartActionQueue.next = 0; + + rootDomain->tracePoint( kIOPMTracePointWakeCPUs ); + + // Wake the other CPUs. + for (cnt = 0; cnt < numCPUs; cnt++) + { + target = OSDynamicCast(IOCPU, gIOCPUs->getObject(cnt)); + + // Skip the already-woken boot CPU. + if ((target->getCPUNumber() != kBootCPUNumber) + && (target->getCPUState() == kIOCPUStateStopped)) + { + processor_start(target->getMachProcessor()); + } } - } } void IOCPU::initCPUs(void) @@ -157,17 +487,19 @@ bool IOCPU::start(IOService *provider) gIOCPUs->setObject(this); // Correct the bus, cpu and timebase frequencies in the device tree. - if (gPEClockFrequencyInfo.bus_frequency_hz < 0x100000000ULL) - busFrequency = OSData::withBytesNoCopy((void *)((char *)&gPEClockFrequencyInfo.bus_frequency_hz + 4), 4); - else - busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 8); + if (gPEClockFrequencyInfo.bus_frequency_hz < 0x100000000ULL) { + busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4); + } else { + busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_frequency_hz, 8); + } provider->setProperty("bus-frequency", busFrequency); busFrequency->release(); - if (gPEClockFrequencyInfo.cpu_frequency_hz < 0x100000000ULL) - cpuFrequency = OSData::withBytesNoCopy((void *)((char *)&gPEClockFrequencyInfo.cpu_frequency_hz + 4), 4); - else - cpuFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.cpu_clock_rate_hz, 8); + if (gPEClockFrequencyInfo.cpu_frequency_hz < 0x100000000ULL) { + cpuFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.cpu_clock_rate_hz, 4); + } else { + cpuFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.cpu_frequency_hz, 8); + } provider->setProperty("clock-frequency", cpuFrequency); cpuFrequency->release(); @@ -175,7 +507,7 @@ bool IOCPU::start(IOService *provider) provider->setProperty("timebase-frequency", timebaseFrequency); timebaseFrequency->release(); - setProperty("IOCPUID", (UInt32)this, 32); + super::setProperty("IOCPUID", getRegistryEntryID(), sizeof(uint64_t)*8); setCPUNumber(0); setCPUState(kIOCPUStateUnregistered); @@ -183,36 +515,69 @@ bool IOCPU::start(IOService *provider) return true; } -IOReturn IOCPU::setProperties(OSObject *properties) +OSObject *IOCPU::getProperty(const OSSymbol *aKey) const { - OSDictionary *dict = OSDynamicCast(OSDictionary, properties); - OSString *stateStr; - IOReturn result; + if (aKey == gIOCPUStateKey) return gIOCPUStateNames[_cpuState]; - if (dict == 0) return kIOReturnUnsupported; + return super::getProperty(aKey); +} + +bool IOCPU::setProperty(const OSSymbol *aKey, OSObject *anObject) +{ + OSString *stateStr; - stateStr = OSDynamicCast(OSString, dict->getObject(gIOCPUStateKey)); - if (stateStr != 0) { - result = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator); - if (result != kIOReturnSuccess) return result; + if (aKey == gIOCPUStateKey) { + stateStr = OSDynamicCast(OSString, anObject); + if (stateStr == 0) return false; - if (_cpuNumber == 0) return kIOReturnUnsupported; + if (_cpuNumber == 0) return false; if (stateStr->isEqualTo("running")) { if (_cpuState == kIOCPUStateStopped) { processor_start(machProcessor); } else if (_cpuState != kIOCPUStateRunning) { - return kIOReturnUnsupported; + return false; } } else if (stateStr->isEqualTo("stopped")) { if (_cpuState == kIOCPUStateRunning) { haltCPU(); } else if (_cpuState != kIOCPUStateStopped) { - return kIOReturnUnsupported; + return false; } - } else return kIOReturnUnsupported; + } else return false; + + return true; + } + + return super::setProperty(aKey, anObject); +} + +bool IOCPU::serializeProperties(OSSerialize *serialize) const +{ + bool result; + OSDictionary *dict = dictionaryWithProperties(); + dict->setObject(gIOCPUStateKey, gIOCPUStateNames[_cpuState]); + result = dict->serialize(serialize); + dict->release(); + return result; +} + +IOReturn IOCPU::setProperties(OSObject *properties) +{ + OSDictionary *dict = OSDynamicCast(OSDictionary, properties); + OSString *stateStr; + IOReturn result; + + if (dict == 0) return kIOReturnUnsupported; + + stateStr = OSDynamicCast(OSString, dict->getObject(gIOCPUStateKey)); + if (stateStr != 0) { + result = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator); + if (result != kIOReturnSuccess) return result; - return kIOReturnSuccess; + if (setProperty(gIOCPUStateKey, stateStr)) return kIOReturnSuccess; + + return kIOReturnUnsupported; } return kIOReturnUnsupported; @@ -234,7 +599,7 @@ UInt32 IOCPU::getCPUNumber(void) void IOCPU::setCPUNumber(UInt32 cpuNumber) { _cpuNumber = cpuNumber; - setProperty("IOCPUNumber", _cpuNumber, 32); + super::setProperty("IOCPUNumber", _cpuNumber, 32); } UInt32 IOCPU::getCPUState(void) @@ -244,9 +609,8 @@ UInt32 IOCPU::getCPUState(void) void IOCPU::setCPUState(UInt32 cpuState) { - if ((cpuState >= 0) && (cpuState < kIOCPUStateCount)) { + if (cpuState < kIOCPUStateCount) { _cpuState = cpuState; - setProperty(gIOCPUStateKey, gIOCPUStateNames[cpuState]); } } @@ -334,6 +698,10 @@ void IOCPUInterruptController::setCPUInterruptProperties(IOService *service) OSData *tmpData; long tmpLong; + if ((service->getProperty(gIOInterruptControllersKey) != 0) && + (service->getProperty(gIOInterruptSpecifiersKey) != 0)) + return; + // Create the interrupt specifer array. specifier = OSArray::withCapacity(numCPUs); for (cnt = 0; cnt < numCPUs; cnt++) { @@ -358,12 +726,15 @@ void IOCPUInterruptController::setCPUInterruptProperties(IOService *service) void IOCPUInterruptController::enableCPUInterrupt(IOCPU *cpu) { - ml_install_interrupt_handler(cpu, cpu->getCPUNumber(), this, - (IOInterruptHandler)&IOCPUInterruptController::handleInterrupt, 0); - - enabledCPUs++; - - if (enabledCPUs == numCPUs) thread_wakeup(this); + IOInterruptHandler handler = OSMemberFunctionCast( + IOInterruptHandler, this, &IOCPUInterruptController::handleInterrupt); + + ml_install_interrupt_handler(cpu, cpu->getCPUNumber(), this, handler, 0); + + // Ensure that the increment is seen by all processors + OSIncrementAtomic(&enabledCPUs); + + if (enabledCPUs == numCPUs) thread_wakeup(this); } IOReturn IOCPUInterruptController::registerInterrupt(IOService *nub,