X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/4a3eedf9ecc9bbe3f3a5c6ce5e53ad199d639d32..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/iokit/Kernel/IOPlatformExpert.cpp diff --git a/iokit/Kernel/IOPlatformExpert.cpp b/iokit/Kernel/IOPlatformExpert.cpp index 1b53461ec..407dd5b02 100644 --- a/iokit/Kernel/IOPlatformExpert.cpp +++ b/iokit/Kernel/IOPlatformExpert.cpp @@ -1,8 +1,8 @@ /* - * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998-2017 Apple 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 @@ -11,10 +11,10 @@ * 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 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,14 +22,12 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* - * HISTORY - */ - + #include +#include #include #include #include @@ -42,332 +40,464 @@ #include #include #include +#include +#include + +#include "IOKitKernelInternal.h" #include +#include #include +#include #include +#include + +#if defined(__arm64__) +#include +#endif extern "C" { #include #include #include +#include } -/* Delay period for UPS halt */ -#define kUPSDelayHaltCPU_msec (1000*60*5) +#define kShutdownTimeout 30 //in secs -void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg); +#if defined(XNU_TARGET_OS_OSX) + +boolean_t coprocessor_cross_panic_enabled = TRUE; +#define APPLE_VENDOR_VARIABLE_GUID "4d1ede05-38c7-4a6a-9cc6-4bcca8b38c14" +#endif /* defined(XNU_TARGET_OS_OSX) */ + +void printDictionaryKeys(OSDictionary * inDictionary, char * inMsg); static void getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen); +/* + * There are drivers which take mutexes in the quiesce callout or pass + * the quiesce/active action to super. Even though it sometimes panics, + * because it doesn't *always* panic, they get away with it. + * We need a chicken bit to diagnose and fix them all before this + * can be enabled by default. + * + * tracks turning this on by default. + */ +uint32_t gEnforceQuiesceSafety = 0; + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super IOService OSDefineMetaClassAndStructors(IOPlatformExpert, IOService) -OSMetaClassDefineReservedUsed(IOPlatformExpert, 0); - -OSMetaClassDefineReservedUsed(IOPlatformExpert, 1); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 2); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 3); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 4); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 5); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 6); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 7); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 8); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 9); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 0); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 1); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 2); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 3); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 4); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 5); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 6); + +OSMetaClassDefineReservedUnused(IOPlatformExpert, 7); +OSMetaClassDefineReservedUnused(IOPlatformExpert, 8); +OSMetaClassDefineReservedUnused(IOPlatformExpert, 9); OSMetaClassDefineReservedUnused(IOPlatformExpert, 10); OSMetaClassDefineReservedUnused(IOPlatformExpert, 11); static IOPlatformExpert * gIOPlatform; static OSDictionary * gIOInterruptControllers; static IOLock * gIOInterruptControllersLock; +static IODTNVRAM *gIOOptionsEntry; OSSymbol * gPlatformInterruptControllerName; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -bool IOPlatformExpert::attach( IOService * provider ) -{ - - if( !super::attach( provider )) - return( false); - - return( true); -} - -bool IOPlatformExpert::start( IOService * provider ) -{ - IORangeAllocator * physicalRanges; - OSData * busFrequency; - uint32_t debugFlags; - - if (!super::start(provider)) - return false; - - // Override the mapper present flag is requested by boot arguments. - if (PE_parse_boot_arg("dart", &debugFlags) && (debugFlags == 0)) - removeProperty(kIOPlatformMapperPresentKey); - - // Register the presence or lack thereof a system - // PCI address mapper with the IOMapper class - IOMapper::setMapperRequired(0 != getProperty(kIOPlatformMapperPresentKey)); - - gIOInterruptControllers = OSDictionary::withCapacity(1); - gIOInterruptControllersLock = IOLockAlloc(); - - // Correct the bus frequency in the device tree. - busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4); - provider->setProperty("clock-frequency", busFrequency); - busFrequency->release(); - - gPlatformInterruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("IOPlatformInterruptController"); - - physicalRanges = IORangeAllocator::withRange(0xffffffff, 1, 16, - IORangeAllocator::kLocking); - assert(physicalRanges); - setProperty("Platform Memory Ranges", physicalRanges); - - setPlatform( this ); - gIOPlatform = this; - - PMInstantiatePowerDomains(); - - // Parse the serial-number data and publish a user-readable string - OSData* mydata = (OSData*) (provider->getProperty("serial-number")); - if (mydata != NULL) { - OSString *serNoString = createSystemSerialNumberString(mydata); - if (serNoString != NULL) { - provider->setProperty(kIOPlatformSerialNumberKey, serNoString); - serNoString->release(); - } - } - - return( configure(provider) ); +bool +IOPlatformExpert::attach( IOService * provider ) +{ + if (!super::attach( provider )) { + return false; + } + + return true; } -bool IOPlatformExpert::configure( IOService * provider ) +bool +IOPlatformExpert::start( IOService * provider ) { - OSSet * topLevel; - OSDictionary * dict; - IOService * nub; + IORangeAllocator * physicalRanges; + OSData * busFrequency; + uint32_t debugFlags; + + + if (!super::start(provider)) { + return false; + } + + // Override the mapper present flag is requested by boot arguments, if SIP disabled. +#if CONFIG_CSR + if (csr_check(CSR_ALLOW_UNRESTRICTED_FS) == 0) +#endif /* CONFIG_CSR */ + { + if (PE_parse_boot_argn("dart", &debugFlags, sizeof(debugFlags)) && (debugFlags == 0)) { + removeProperty(kIOPlatformMapperPresentKey); + } +#if DEBUG || DEVELOPMENT + if (PE_parse_boot_argn("-x", &debugFlags, sizeof(debugFlags))) { + removeProperty(kIOPlatformMapperPresentKey); + } +#endif /* DEBUG || DEVELOPMENT */ + } + + // Register the presence or lack thereof a system + // PCI address mapper with the IOMapper class + IOMapper::setMapperRequired(NULL != getProperty(kIOPlatformMapperPresentKey)); + + gIOInterruptControllers = OSDictionary::withCapacity(1); + gIOInterruptControllersLock = IOLockAlloc(); + + // Correct the bus frequency in the device tree. + busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4); + provider->setProperty("clock-frequency", busFrequency); + busFrequency->release(); + + gPlatformInterruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("IOPlatformInterruptController"); + + physicalRanges = IORangeAllocator::withRange(0xffffffff, 1, 16, + IORangeAllocator::kLocking); + assert(physicalRanges); + setProperty("Platform Memory Ranges", physicalRanges); + + setPlatform( this ); + gIOPlatform = this; - topLevel = OSDynamicCast( OSSet, getProperty("top-level")); + PMInstantiatePowerDomains(); - if( topLevel) { - while( (dict = OSDynamicCast( OSDictionary, - topLevel->getAnyObject()))) { - dict->retain(); - topLevel->removeObject( dict ); - nub = createNub( dict ); - if( 0 == nub) - continue; - dict->release(); - nub->attach( this ); - nub->registerService(); - } - } +#if !defined(__x86_64__) + publishPlatformUUIDAndSerial(); +#endif /* !defined(__x86_64__) */ - return( true ); +#if defined (__x86_64__) + if (PEGetCoprocessorVersion() >= kCoprocessorVersion2) { + coprocessor_paniclog_flush = TRUE; + extended_debug_log_init(); + } +#endif + + PE_parse_boot_argn("enforce_quiesce_safety", &gEnforceQuiesceSafety, + sizeof(gEnforceQuiesceSafety)); + + return configure(provider); } -IOService * IOPlatformExpert::createNub( OSDictionary * from ) +bool +IOPlatformExpert::configure( IOService * provider ) +{ + OSSet * topLevel; + OSDictionary * dict; + IOService * nub; + + topLevel = OSDynamicCast( OSSet, getProperty("top-level")); + + if (topLevel) { + while ((dict = OSDynamicCast( OSDictionary, + topLevel->getAnyObject()))) { + dict->retain(); + topLevel->removeObject( dict ); + nub = createNub( dict ); + if (NULL == nub) { + continue; + } + dict->release(); + nub->attach( this ); + nub->registerService(); + } + } + + return true; +} + +IOService * +IOPlatformExpert::createNub( OSDictionary * from ) { - IOService * nub; + IOService * nub; - nub = new IOPlatformDevice; - if(nub) { - if( !nub->init( from )) { - nub->release(); - nub = 0; + nub = new IOPlatformDevice; + if (nub) { + if (!nub->init( from )) { + nub->release(); + nub = NULL; + } } - } - return( nub); + return nub; } -bool IOPlatformExpert::compareNubName( const IOService * nub, - OSString * name, OSString ** matched ) const +bool +IOPlatformExpert::compareNubName( const IOService * nub, + OSString * name, OSString ** matched ) const { - return( nub->IORegistryEntry::compareName( name, matched )); + return nub->IORegistryEntry::compareName( name, matched ); } -IOReturn IOPlatformExpert::getNubResources( IOService * nub ) +bool +IOPlatformExpert::compareNubName( const IOService * nub, + OSString * name, OSSharedPtr& matched ) const { - return( kIOReturnSuccess ); + OSString* matchedRaw = NULL; + bool result = compareNubName(nub, name, &matchedRaw); + matched.reset(matchedRaw, OSNoRetain); + return result; } -long IOPlatformExpert::getBootROMType(void) +IOReturn +IOPlatformExpert::getNubResources( IOService * nub ) { - return _peBootROMType; + return kIOReturnSuccess; } -long IOPlatformExpert::getChipSetType(void) +long +IOPlatformExpert::getBootROMType(void) { - return _peChipSetType; + return _peBootROMType; } -long IOPlatformExpert::getMachineType(void) +long +IOPlatformExpert::getChipSetType(void) { - return _peMachineType; + return _peChipSetType; } -void IOPlatformExpert::setBootROMType(long peBootROMType) +long +IOPlatformExpert::getMachineType(void) { - _peBootROMType = peBootROMType; + return _peMachineType; } -void IOPlatformExpert::setChipSetType(long peChipSetType) +void +IOPlatformExpert::setBootROMType(long peBootROMType) { - _peChipSetType = peChipSetType; + _peBootROMType = peBootROMType; } -void IOPlatformExpert::setMachineType(long peMachineType) +void +IOPlatformExpert::setChipSetType(long peChipSetType) { - _peMachineType = peMachineType; + _peChipSetType = peChipSetType; } -bool IOPlatformExpert::getMachineName( char * /*name*/, int /*maxLength*/) +void +IOPlatformExpert::setMachineType(long peMachineType) { - return( false ); + _peMachineType = peMachineType; } -bool IOPlatformExpert::getModelName( char * /*name*/, int /*maxLength*/) +bool +IOPlatformExpert::getMachineName( char * /*name*/, int /*maxLength*/) { - return( false ); + return false; } -OSString* IOPlatformExpert::createSystemSerialNumberString(OSData* myProperty) +bool +IOPlatformExpert::getModelName( char * /*name*/, int /*maxLength*/) { - return NULL; + return false; } -IORangeAllocator * IOPlatformExpert::getPhysicalRangeAllocator(void) +bool +IOPlatformExpert::getTargetName( char * /*name*/, int /*maxLength*/) { - return(OSDynamicCast(IORangeAllocator, - getProperty("Platform Memory Ranges"))); + return false; } -int (*PE_halt_restart)(unsigned int type) = 0; +bool +IOPlatformExpert::getProductName( char * /*name*/, int /*maxLength*/) +{ + return false; +} -int IOPlatformExpert::haltRestart(unsigned int type) +OSString* +IOPlatformExpert::createSystemSerialNumberString(OSData* myProperty) { - if (type == kPEPanicSync) return 0; + return NULL; +} - if (type == kPEHangCPU) while (1); +IORangeAllocator * +IOPlatformExpert::getPhysicalRangeAllocator(void) +{ + return OSDynamicCast(IORangeAllocator, + getProperty("Platform Memory Ranges")); +} - if (type == kPEUPSDelayHaltCPU) { - // Stall shutdown for 5 minutes, and if no outside force has - // removed our power at that point, proceed with a reboot. - IOSleep( kUPSDelayHaltCPU_msec ); +int (*PE_halt_restart)(unsigned int type) = NULL; - // Ideally we never reach this point. +int +IOPlatformExpert::haltRestart(unsigned int type) +{ + if (type == kPEPanicSync) { + return 0; + } - type = kPERestartCPU; - } + if (type == kPEHangCPU) { + while (true) { + } + } + + if (type == kPEUPSDelayHaltCPU) { + // RestartOnPowerLoss feature was turned on, proceed with shutdown. + type = kPEHaltCPU; + } - // On ARM kPEPanicRestartCPU is supported in the drivers - if (type == kPEPanicRestartCPU) - type = kPERestartCPU; - - if (PE_halt_restart) return (*PE_halt_restart)(type); - else return -1; +#if defined (__x86_64__) + // On ARM kPEPanicRestartCPU is supported in the drivers + if (type == kPEPanicRestartCPU) { + type = kPERestartCPU; + } +#endif + + if (PE_halt_restart) { + return (*PE_halt_restart)(type); + } else { + return -1; + } } -void IOPlatformExpert::sleepKernel(void) +void +IOPlatformExpert::sleepKernel(void) { #if 0 - long cnt; - boolean_t intState; - - intState = ml_set_interrupts_enabled(false); - - for (cnt = 0; cnt < 10000; cnt++) { - IODelay(1000); - } - - ml_set_interrupts_enabled(intState); + long cnt; + boolean_t intState; + + intState = ml_set_interrupts_enabled(false); + + for (cnt = 0; cnt < 10000; cnt++) { + IODelay(1000); + } + + ml_set_interrupts_enabled(intState); #else // PE_initialize_console(0, kPEDisableScreen); - - IOCPUSleepKernel(); - + + IOCPUSleepKernel(); + // PE_initialize_console(0, kPEEnableScreen); #endif } -long IOPlatformExpert::getGMTTimeOfDay(void) +long +IOPlatformExpert::getGMTTimeOfDay(void) { - return(0); + return 0; } -void IOPlatformExpert::setGMTTimeOfDay(long secs) +void +IOPlatformExpert::setGMTTimeOfDay(long secs) { } -IOReturn IOPlatformExpert::getConsoleInfo( PE_Video * consoleInfo ) +IOReturn +IOPlatformExpert::getConsoleInfo( PE_Video * consoleInfo ) { - return( PE_current_console( consoleInfo)); + return PE_current_console( consoleInfo); } -IOReturn IOPlatformExpert::setConsoleInfo( PE_Video * consoleInfo, - unsigned int op) +IOReturn +IOPlatformExpert::setConsoleInfo( PE_Video * consoleInfo, + unsigned int op) { - return( PE_initialize_console( consoleInfo, op )); + return PE_initialize_console( consoleInfo, op ); } -IOReturn IOPlatformExpert::registerInterruptController(OSSymbol *name, IOInterruptController *interruptController) +IOReturn +IOPlatformExpert::registerInterruptController(OSSymbol *name, IOInterruptController *interruptController) { - IOLockLock(gIOInterruptControllersLock); - - gIOInterruptControllers->setObject(name, interruptController); - - IOLockWakeup(gIOInterruptControllersLock, - gIOInterruptControllers, /* one-thread */ false); + IOLockLock(gIOInterruptControllersLock); + + gIOInterruptControllers->setObject(name, interruptController); + + IOLockWakeup(gIOInterruptControllersLock, + gIOInterruptControllers, /* one-thread */ false); - IOLockUnlock(gIOInterruptControllersLock); - - return kIOReturnSuccess; + IOLockUnlock(gIOInterruptControllersLock); + + return kIOReturnSuccess; } -IOInterruptController *IOPlatformExpert::lookUpInterruptController(OSSymbol *name) +IOReturn +IOPlatformExpert::deregisterInterruptController(OSSymbol *name) { - OSObject *object; - - IOLockLock(gIOInterruptControllersLock); - while (1) { - - object = gIOInterruptControllers->getObject(name); - - if (object != 0) - break; - - IOLockSleep(gIOInterruptControllersLock, - gIOInterruptControllers, THREAD_UNINT); - } - - IOLockUnlock(gIOInterruptControllersLock); - return OSDynamicCast(IOInterruptController, object); + IOLockLock(gIOInterruptControllersLock); + + gIOInterruptControllers->removeObject(name); + + IOLockUnlock(gIOInterruptControllersLock); + + return kIOReturnSuccess; } +IOInterruptController * +IOPlatformExpert::lookUpInterruptController(OSSymbol *name) +{ + OSObject *object; + + IOLockLock(gIOInterruptControllersLock); + while (1) { + object = gIOInterruptControllers->getObject(name); -void IOPlatformExpert::setCPUInterruptProperties(IOService *service) + if (object != NULL) { + break; + } + + IOLockSleep(gIOInterruptControllersLock, + gIOInterruptControllers, THREAD_UNINT); + } + + IOLockUnlock(gIOInterruptControllersLock); + return OSDynamicCast(IOInterruptController, object); +} + + +void +IOPlatformExpert::setCPUInterruptProperties(IOService *service) +{ + IOInterruptController *controller; + + OSDictionary *matching = serviceMatching("IOInterruptController"); + matching = propertyMatching(gPlatformInterruptControllerName, kOSBooleanTrue, matching); + + controller = OSDynamicCast(IOInterruptController, waitForService(matching)); + if (controller) { + controller->setCPUInterruptProperties(service); + } +} + +bool +IOPlatformExpert::atInterruptLevel(void) +{ + return ml_at_interrupt_context(); +} + +bool +IOPlatformExpert::platformAdjustService(IOService */*service*/) { - IOCPUInterruptController *controller; - - controller = OSDynamicCast(IOCPUInterruptController, waitForService(serviceMatching("IOCPUInterruptController"))); - if (controller) controller->setCPUInterruptProperties(service); + return true; } -bool IOPlatformExpert::atInterruptLevel(void) +void +IOPlatformExpert::getUTCTimeOfDay(clock_sec_t * secs, clock_nsec_t * nsecs) { - return ml_at_interrupt_context(); + *secs = getGMTTimeOfDay(); + *nsecs = 0; } -bool IOPlatformExpert::platformAdjustService(IOService */*service*/) +void +IOPlatformExpert::setUTCTimeOfDay(clock_sec_t secs, __unused clock_nsec_t nsecs) { - return true; + setGMTTimeOfDay(secs); } @@ -376,45 +506,19 @@ bool IOPlatformExpert::platformAdjustService(IOService */*service*/) // //********************************************************************************* -void IOPlatformExpert:: +void +IOPlatformExpert:: PMLog(const char *who, unsigned long event, - unsigned long param1, unsigned long param2) + unsigned long param1, unsigned long param2) { - UInt32 debugFlags = gIOKitDebug; - - if (debugFlags & kIOLogPower) { - - uint32_t nows, nowus; + clock_sec_t nows; + clock_usec_t nowus; clock_get_system_microtime(&nows, &nowus); nowus += (nows % 1000) * 1000000; - kprintf("pm%u %x %.30s %d %x %x\n", - nowus, (unsigned) current_thread(), who, // Identity - (int) event, param1, param2); // Args - - if (debugFlags & kIOLogTracePower) { - static const UInt32 sStartStopBitField[] = - { 0x00000000, 0x00000040 }; // Only Program Hardware so far - - // Arcane formula from Hacker's Delight by Warren - // abs(x) = ((int) x >> 31) ^ (x + ((int) x >> 31)) - UInt32 sgnevent = ((long) event >> 31); - UInt32 absevent = sgnevent ^ (event + sgnevent); - UInt32 code = IODBG_POWER(absevent); - - UInt32 bit = 1 << (absevent & 0x1f); - if (absevent < sizeof(sStartStopBitField) * 8 - && (sStartStopBitField[absevent >> 5] & bit) ) { - // Or in the START or END bits, Start = 1 & END = 2 - // If sgnevent == 0 then START - 0 => START - // else if sgnevent == -1 then START - -1 => END - code |= DBG_FUNC_START - sgnevent; - } - - // Record the timestamp, wish I had a this pointer - IOTimeStampConstant(code, (UInt32) who, event, param1, param2); - } - } + kprintf("pm%u %p %.30s %d %lx %lx\n", + nowus, OBFUSCATE(current_thread()), who, // Identity + (int) event, (long)OBFUSCATE(param1), (long)OBFUSCATE(param2)); // Args } @@ -423,17 +527,17 @@ PMLog(const char *who, unsigned long event, // // In this vanilla implementation, a Root Power Domain is instantiated. // All other objects which register will be children of this Root. -// Where this is inappropriate, PMInstantiatePowerDomains is overridden +// Where this is inappropriate, PMInstantiatePowerDomains is overridden // in a platform-specific subclass. //********************************************************************************* -void IOPlatformExpert::PMInstantiatePowerDomains ( void ) +void +IOPlatformExpert::PMInstantiatePowerDomains( void ) { - root = new IOPMrootDomain; - root->init(); - root->attach(this); - root->start(this); - root->youAreRoot(); + root = new IOPMrootDomain; + root->init(); + root->attach(this); + root->start(this); } @@ -444,9 +548,10 @@ void IOPlatformExpert::PMInstantiatePowerDomains ( void ) // Where this is inappropriate, PMRegisterDevice is overridden in a platform-specific subclass. //********************************************************************************* -void IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevice) +void +IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevice) { - root->addPowerChild ( theDevice ); + root->addPowerChild( theDevice ); } //********************************************************************************* @@ -454,9 +559,10 @@ void IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevic // //********************************************************************************* -bool IOPlatformExpert::hasPMFeature (unsigned long featureMask) +bool +IOPlatformExpert::hasPMFeature(unsigned long featureMask) { - return ((_pePMFeatures & featureMask) != 0); + return (_pePMFeatures & featureMask) != 0; } //********************************************************************************* @@ -464,9 +570,10 @@ bool IOPlatformExpert::hasPMFeature (unsigned long featureMask) // //********************************************************************************* -bool IOPlatformExpert::hasPrivPMFeature (unsigned long privFeatureMask) +bool +IOPlatformExpert::hasPrivPMFeature(unsigned long privFeatureMask) { - return ((_pePrivPMFeatures & privFeatureMask) != 0); + return (_pePrivPMFeatures & privFeatureMask) != 0; } //********************************************************************************* @@ -474,9 +581,10 @@ bool IOPlatformExpert::hasPrivPMFeature (unsigned long privFeatureMask) // //********************************************************************************* -int IOPlatformExpert::numBatteriesSupported (void) +int +IOPlatformExpert::numBatteriesSupported(void) { - return (_peNumBatteriesSupported); + return _peNumBatteriesSupported; } //********************************************************************************* @@ -490,455 +598,924 @@ int IOPlatformExpert::numBatteriesSupported (void) // registered for the given service. //********************************************************************************* -bool IOPlatformExpert::CheckSubTree (OSArray * inSubTree, IOService * theNub, IOService * theDevice, OSDictionary * theParent) +bool +IOPlatformExpert::CheckSubTree(OSArray * inSubTree, IOService * theNub, IOService * theDevice, OSDictionary * theParent) +{ + unsigned int i; + unsigned int numPowerTreeNodes; + OSDictionary * entry; + OSDictionary * matchingDictionary; + OSDictionary * providerDictionary; + OSDictionary * deviceDictionary; + OSDictionary * nubDictionary; + OSArray * children; + bool nodeFound = false; + bool continueSearch = false; + bool deviceMatch = false; + bool providerMatch = false; + bool multiParentMatch = false; + + if ((NULL == theDevice) || (NULL == inSubTree)) { + return false; + } + + numPowerTreeNodes = inSubTree->getCount(); + + // iterate through the power tree to find a home for this device + + for (i = 0; i < numPowerTreeNodes; i++) { + entry = (OSDictionary *) inSubTree->getObject(i); + + matchingDictionary = (OSDictionary *) entry->getObject("device"); + providerDictionary = (OSDictionary *) entry->getObject("provider"); + + deviceMatch = true; // if no matching dictionary, this is not a criteria and so must match + if (matchingDictionary) { + deviceMatch = false; + if (NULL != (deviceDictionary = theDevice->dictionaryWithProperties())) { + deviceMatch = deviceDictionary->isEqualTo( matchingDictionary, matchingDictionary ); + deviceDictionary->release(); + } + } + + providerMatch = true; // we indicate a match if there is no nub or provider + if (theNub && providerDictionary) { + providerMatch = false; + if (NULL != (nubDictionary = theNub->dictionaryWithProperties())) { + providerMatch = nubDictionary->isEqualTo( providerDictionary, providerDictionary ); + nubDictionary->release(); + } + } + + multiParentMatch = true; // again we indicate a match if there is no multi-parent node + if (deviceMatch && providerMatch) { + if (NULL != multipleParentKeyValue) { + OSNumber * aNumber = (OSNumber *) entry->getObject("multiple-parent"); + multiParentMatch = (NULL != aNumber) ? multipleParentKeyValue->isEqualTo(aNumber) : false; + } + } + + nodeFound = (deviceMatch && providerMatch && multiParentMatch); + + // if the power tree specifies a provider dictionary but theNub is + // NULL then we cannot match with this entry. + + if (theNub == NULL && providerDictionary != NULL) { + nodeFound = false; + } + + // if this node is THE ONE...then register the device + + if (nodeFound) { + if (RegisterServiceInTree(theDevice, entry, theParent, theNub)) { + if (kIOLogPower & gIOKitDebug) { + IOLog("PMRegisterDevice/CheckSubTree - service registered!\n"); + } + + numInstancesRegistered++; + + // determine if we need to search for additional nodes for this item + multipleParentKeyValue = (OSNumber *) entry->getObject("multiple-parent"); + } else { + nodeFound = false; + } + } + + continueSearch = ((false == nodeFound) || (NULL != multipleParentKeyValue)); + + if (continueSearch && (NULL != (children = (OSArray *) entry->getObject("children")))) { + nodeFound = CheckSubTree( children, theNub, theDevice, entry ); + continueSearch = ((false == nodeFound) || (NULL != multipleParentKeyValue)); + } + + if (false == continueSearch) { + break; + } + } + + return nodeFound; +} + +//********************************************************************************* +// RegisterServiceInTree +// +// Register a device at the specified node of our power tree. +//********************************************************************************* + +bool +IOPlatformExpert::RegisterServiceInTree(IOService * theService, OSDictionary * theTreeNode, OSDictionary * theTreeParentNode, IOService * theProvider) +{ + IOService * aService; + bool registered = false; + OSArray * children; + unsigned int numChildren; + OSDictionary * child; + + // make sure someone is not already registered here + + if (NULL == theTreeNode->getObject("service")) { + if (theTreeNode->setObject("service", OSDynamicCast( OSObject, theService))) { + // 1. CHILDREN ------------------ + + // we registered the node in the tree...now if the node has children + // registered we must tell this service to add them. + + if (NULL != (children = (OSArray *) theTreeNode->getObject("children"))) { + numChildren = children->getCount(); + for (unsigned int i = 0; i < numChildren; i++) { + if (NULL != (child = (OSDictionary *) children->getObject(i))) { + if (NULL != (aService = (IOService *) child->getObject("service"))) { + theService->addPowerChild(aService); + } + } + } + } + + // 2. PARENT -------------------- + + // also we must notify the parent of this node (if a registered service + // exists there) of a new child. + + if (theTreeParentNode) { + if (NULL != (aService = (IOService *) theTreeParentNode->getObject("service"))) { + if (aService != theProvider) { + aService->addPowerChild(theService); + } + } + } + + registered = true; + } + } + + return registered; +} + +//********************************************************************************* +// printDictionaryKeys +// +// Print the keys for the given dictionary and selected contents. +//********************************************************************************* +void +printDictionaryKeys(OSDictionary * inDictionary, char * inMsg) { - unsigned int i; - unsigned int numPowerTreeNodes; - OSDictionary * entry; - OSDictionary * matchingDictionary; - OSDictionary * providerDictionary; - OSDictionary * deviceDictionary; - OSDictionary * nubDictionary; - OSArray * children; - bool nodeFound = false; - bool continueSearch = false; - bool deviceMatch = false; - bool providerMatch = false; - bool multiParentMatch = false; + OSCollectionIterator * mcoll = OSCollectionIterator::withCollection(inDictionary); + OSSymbol * mkey; + OSString * ioClass; + unsigned int i = 0; - if ( (NULL == theDevice) || (NULL == inSubTree) ) - return false; + mcoll->reset(); - numPowerTreeNodes = inSubTree->getCount (); + mkey = OSDynamicCast(OSSymbol, mcoll->getNextObject()); - // iterate through the power tree to find a home for this device + while (mkey) { + // kprintf ("dictionary key #%d: %s\n", i, mkey->getCStringNoCopy () ); - for ( i = 0; i < numPowerTreeNodes; i++ ) { + // if this is the IOClass key, print it's contents - entry = (OSDictionary *) inSubTree->getObject (i); + if (mkey->isEqualTo("IOClass")) { + ioClass = (OSString *) inDictionary->getObject("IOClass"); + if (ioClass) { + IOLog("%s IOClass is %s\n", inMsg, ioClass->getCStringNoCopy()); + } + } - matchingDictionary = (OSDictionary *) entry->getObject ("device"); - providerDictionary = (OSDictionary *) entry->getObject ("provider"); + // if this is an IOProviderClass key print it - deviceMatch = true; // if no matching dictionary, this is not a criteria and so must match - if ( matchingDictionary ) { - deviceMatch = false; - if ( NULL != (deviceDictionary = theDevice->dictionaryWithProperties ())) { - deviceMatch = deviceDictionary->isEqualTo ( matchingDictionary, matchingDictionary ); - deviceDictionary->release (); - } - } + if (mkey->isEqualTo("IOProviderClass")) { + ioClass = (OSString *) inDictionary->getObject("IOProviderClass"); + if (ioClass) { + IOLog("%s IOProviderClass is %s\n", inMsg, ioClass->getCStringNoCopy()); + } + } - providerMatch = true; // we indicate a match if there is no nub or provider - if ( theNub && providerDictionary ) { - providerMatch = false; - if ( NULL != (nubDictionary = theNub->dictionaryWithProperties ()) ) { - providerMatch = nubDictionary->isEqualTo ( providerDictionary, providerDictionary ); - nubDictionary->release (); - } - } + // also print IONameMatch keys + if (mkey->isEqualTo("IONameMatch")) { + ioClass = (OSString *) inDictionary->getObject("IONameMatch"); + if (ioClass) { + IOLog("%s IONameMatch is %s\n", inMsg, ioClass->getCStringNoCopy()); + } + } - multiParentMatch = true; // again we indicate a match if there is no multi-parent node - if (deviceMatch && providerMatch) { - if (NULL != multipleParentKeyValue) { - OSNumber * aNumber = (OSNumber *) entry->getObject ("multiple-parent"); - multiParentMatch = (NULL != aNumber) ? multipleParentKeyValue->isEqualTo (aNumber) : false; - } - } + // also print IONameMatched keys - nodeFound = (deviceMatch && providerMatch && multiParentMatch); + if (mkey->isEqualTo("IONameMatched")) { + ioClass = (OSString *) inDictionary->getObject("IONameMatched"); + if (ioClass) { + IOLog("%s IONameMatched is %s\n", inMsg, ioClass->getCStringNoCopy()); + } + } - // if the power tree specifies a provider dictionary but theNub is - // NULL then we cannot match with this entry. +#if 0 + // print clock-id + + if (mkey->isEqualTo("AAPL,clock-id")) { + char * cstr; + cstr = getCStringForObject(inDictionary->getObject("AAPL,clock-id")); + if (cstr) { + kprintf(" ===> AAPL,clock-id is %s\n", cstr ); + } + } +#endif - if ( theNub == NULL && providerDictionary != NULL ) - nodeFound = false; - - // if this node is THE ONE...then register the device + // print name - if ( nodeFound ) { - if (RegisterServiceInTree (theDevice, entry, theParent, theNub) ) { + if (mkey->isEqualTo("name")) { + char nameStr[64]; + nameStr[0] = 0; + getCStringForObject(inDictionary->getObject("name"), nameStr, + sizeof(nameStr)); + if (strlen(nameStr) > 0) { + IOLog("%s name is %s\n", inMsg, nameStr); + } + } - if ( kIOLogPower & gIOKitDebug) - IOLog ("PMRegisterDevice/CheckSubTree - service registered!\n"); + mkey = (OSSymbol *) mcoll->getNextObject(); - numInstancesRegistered++; + i++; + } - // determine if we need to search for additional nodes for this item - multipleParentKeyValue = (OSNumber *) entry->getObject ("multiple-parent"); - } - else - nodeFound = false; - } + mcoll->release(); +} - continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) ); +static void +getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen) +{ + char * buffer; + unsigned int len, i; - if ( continueSearch && (NULL != (children = (OSArray *) entry->getObject ("children"))) ) { - nodeFound = CheckSubTree ( children, theNub, theDevice, entry ); - continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) ); - } + if ((NULL == inObj) || (NULL == outStr)) { + return; + } - if ( false == continueSearch ) - break; - } + char * objString = (char *) (inObj->getMetaClass())->getClassName(); + + if ((0 == strncmp(objString, "OSString", sizeof("OSString"))) || + (0 == strncmp(objString, "OSSymbol", sizeof("OSSymbol")))) { + strlcpy(outStr, ((OSString *)inObj)->getCStringNoCopy(), outStrLen); + } else if (0 == strncmp(objString, "OSData", sizeof("OSData"))) { + len = ((OSData *)inObj)->getLength(); + buffer = (char *)((OSData *)inObj)->getBytesNoCopy(); + if (buffer && (len > 0)) { + for (i = 0; i < len; i++) { + outStr[i] = buffer[i]; + } + outStr[len] = 0; + } + } +} - return ( nodeFound ); +/* IOShutdownNotificationsTimedOut + * - Called from a timer installed by PEHaltRestart + */ +#if !defined(__x86_64) +__abortlike +#endif +static void +IOShutdownNotificationsTimedOut( + thread_call_param_t p0, + thread_call_param_t p1) +{ +#if !defined(__x86_64__) + /* 30 seconds has elapsed - panic */ + panic("Halt/Restart Timed Out"); + +#else /* !defined(__x86_64__) */ + int type = (int)(long)p0; + uint32_t timeout = (uint32_t)(uintptr_t)p1; + + IOPMrootDomain *pmRootDomain = IOService::getPMRootDomain(); + if (pmRootDomain) { + if ((PEGetCoprocessorVersion() >= kCoprocessorVersion2) || pmRootDomain->checkShutdownTimeout()) { + pmRootDomain->panicWithShutdownLog(timeout * 1000); + } + } + + /* 30 seconds has elapsed - resume shutdown */ + if (gIOPlatform) { + gIOPlatform->haltRestart(type); + } +#endif /* defined(__x86_64__) */ } -//********************************************************************************* -// RegisterServiceInTree -// -// Register a device at the specified node of our power tree. -//********************************************************************************* -bool IOPlatformExpert::RegisterServiceInTree (IOService * theService, OSDictionary * theTreeNode, OSDictionary * theTreeParentNode, IOService * theProvider) -{ - IOService * aService; - bool registered = false; - OSArray * children; - unsigned int numChildren; - OSDictionary * child; +extern "C" { +/* + * Callouts from BSD for machine name & model + */ - // make sure someone is not already registered here +/* + * PEGetMachineName() and PEGetModelName() are inconsistent across + * architectures, and considered deprecated. Use PEGetTargetName() and + * PEGetProductName() instead. + */ +boolean_t +PEGetMachineName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getMachineName( name, maxLength ); + } else { + return false; + } +} - if ( NULL == theTreeNode->getObject ("service") ) { +/* + * PEGetMachineName() and PEGetModelName() are inconsistent across + * architectures, and considered deprecated. Use PEGetTargetName() and + * PEGetProductName() instead. + */ +boolean_t +PEGetModelName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getModelName( name, maxLength ); + } else { + return false; + } +} - if ( theTreeNode->setObject ("service", OSDynamicCast ( OSObject, theService)) ) { +boolean_t +PEGetTargetName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getTargetName( name, maxLength ); + } else { + return false; + } +} - // 1. CHILDREN ------------------ +boolean_t +PEGetProductName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getProductName( name, maxLength ); + } else { + return false; + } +} - // we registered the node in the tree...now if the node has children - // registered we must tell this service to add them. +int +PEGetPlatformEpoch(void) +{ + if (gIOPlatform) { + return (int) gIOPlatform->getBootROMType(); + } else { + return -1; + } +} - if ( NULL != (children = (OSArray *) theTreeNode->getObject ("children")) ) { - numChildren = children->getCount (); - for ( unsigned int i = 0; i < numChildren; i++ ) { - if ( NULL != (child = (OSDictionary *) children->getObject (i)) ) { - if ( NULL != (aService = (IOService *) child->getObject ("service")) ) - theService->addPowerChild (aService); - } - } - } +/* Handle necessary platform specific actions prior to panic */ +void +PEInitiatePanic(void) +{ +#if defined(__arm64__) + /* + * Trigger a TLB flush so any hard hangs exercise the SoC diagnostic + * collection flow rather than hanging late in panic (see rdar://58062030) + */ + flush_mmu_tlb_entry(0); +#endif +} - // 2. PARENT -------------------- +int +PEHaltRestartInternal(unsigned int type, uint32_t details) +{ + IOPMrootDomain *pmRootDomain; + AbsoluteTime deadline; + thread_call_t shutdown_hang; + IORegistryEntry *node; + OSData *data; + uint32_t timeout = kShutdownTimeout; + static boolean_t panic_begin_called = FALSE; + + if (type == kPEHaltCPU || type == kPERestartCPU || type == kPEUPSDelayHaltCPU) { + /* If we're in the panic path, the locks and memory allocations required below + * could fail. So just try to reboot instead of risking a nested panic. + */ + if (panic_begin_called) { + goto skip_to_haltRestart; + } + + pmRootDomain = IOService::getPMRootDomain(); + /* Notify IOKit PM clients of shutdown/restart + * Clients subscribe to this message with a call to + * IOService::registerInterest() + */ + + /* Spawn a thread that will panic in 30 seconds. + * If all goes well the machine will be off by the time + * the timer expires. If the device wants a different + * timeout, use that value instead of 30 seconds. + */ +#if defined(__arm__) || defined(__arm64__) +#define RESTART_NODE_PATH "/defaults" +#else +#define RESTART_NODE_PATH "/chosen" +#endif + node = IORegistryEntry::fromPath( RESTART_NODE_PATH, gIODTPlane ); + if (node) { + data = OSDynamicCast( OSData, node->getProperty( "halt-restart-timeout" )); + if (data && data->getLength() == 4) { + timeout = *((uint32_t *) data->getBytesNoCopy()); + } + } + +#if (DEVELOPMENT || DEBUG) + /* Override the default timeout via a boot-arg */ + uint32_t boot_arg_val; + if (PE_parse_boot_argn("halt_restart_timeout", &boot_arg_val, sizeof(boot_arg_val))) { + timeout = boot_arg_val; + } +#endif - // also we must notify the parent of this node (if a registered service - // exists there) of a new child. + if (timeout) { + shutdown_hang = thread_call_allocate( &IOShutdownNotificationsTimedOut, + (thread_call_param_t)(uintptr_t) type); + clock_interval_to_deadline( timeout, kSecondScale, &deadline ); + thread_call_enter1_delayed( shutdown_hang, (thread_call_param_t)(uintptr_t)timeout, deadline ); + } + + pmRootDomain->handlePlatformHaltRestart(type); + /* This notification should have few clients who all do + * their work synchronously. + * + * In this "shutdown notification" context we don't give + * drivers the option of working asynchronously and responding + * later. PM internals make it very hard to wait for asynchronous + * replies. + */ + } else if (type == kPEPanicRestartCPU || type == kPEPanicSync || type == kPEPanicRestartCPUNoCallouts) { + if (type == kPEPanicRestartCPU) { + // Notify any listeners that we're done collecting + // panic data before we call through to do the restart +#if defined(__x86_64__) + if (coprocessor_cross_panic_enabled) +#endif + IOCPURunPlatformPanicActions(kPEPanicEnd, details); + } else if (type == kPEPanicRestartCPUNoCallouts) { + // We skipped the callouts so now set the type to + // the variant that the platform uses for panic restarts. + type = kPEPanicRestartCPU; + } + + + // Do an initial sync to flush as much panic data as possible, + // in case we have a problem in one of the platorm panic handlers. + // After running the platform handlers, do a final sync w/ + // platform hardware quiesced for the panic. + PE_sync_panic_buffers(); + IOCPURunPlatformPanicActions(type, details); + PE_sync_panic_buffers(); + } else if (type == kPEPanicEnd) { +#if defined(__x86_64__) + if (coprocessor_cross_panic_enabled) +#endif + IOCPURunPlatformPanicActions(type, details); + } else if (type == kPEPanicBegin) { +#if defined(__x86_64__) + if (coprocessor_cross_panic_enabled) +#endif + { + // Only call the kPEPanicBegin callout once + if (!panic_begin_called) { + panic_begin_called = TRUE; + IOCPURunPlatformPanicActions(type, details); + } + } + } - if ( theTreeParentNode ) { - if ( NULL != (aService = (IOService *) theTreeParentNode->getObject ("service")) ) - if (aService != theProvider) - aService->addPowerChild (theService); - } +skip_to_haltRestart: + if (gIOPlatform) { + return gIOPlatform->haltRestart(type); + } else { + return -1; + } +} - registered = true; - } - } +int +PEHaltRestart(unsigned int type) +{ + return PEHaltRestartInternal(type, 0); +} - return registered; +UInt32 +PESavePanicInfo(UInt8 *buffer, UInt32 length) +{ + if (gIOPlatform != NULL) { + return (UInt32) gIOPlatform->savePanicInfo(buffer, length); + } else { + return 0; + } } -//********************************************************************************* -// printDictionaryKeys -// -// Print the keys for the given dictionary and selected contents. -//********************************************************************************* -void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg) +void +PESavePanicInfoAction(void *buffer, UInt32 offset, UInt32 length) { - OSCollectionIterator * mcoll = OSCollectionIterator::withCollection (inDictionary); - OSSymbol * mkey; - OSString * ioClass; - unsigned int i = 0; - - mcoll->reset (); + IOCPURunPlatformPanicSyncAction(buffer, offset, length); + return; +} - mkey = OSDynamicCast (OSSymbol, mcoll->getNextObject ()); - while (mkey) { +/* + * Depending on the platform, the /options node may not be created + * until after IOKit matching has started, by an externally-supplied + * platform expert subclass. Therefore, we must check for its presence + * here and update gIOOptionsEntry for the platform code as necessary. + */ +inline static int +init_gIOOptionsEntry(void) +{ + IORegistryEntry *entry; + void *nvram_entry; + volatile void **options; + int ret = -1; - // kprintf ("dictionary key #%d: %s\n", i, mkey->getCStringNoCopy () ); + if (gIOOptionsEntry) { + return 0; + } - // if this is the IOClass key, print it's contents + entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); + if (!entry) { + return -1; + } - if ( mkey->isEqualTo ("IOClass") ) { - ioClass = (OSString *) inDictionary->getObject ("IOClass"); - if ( ioClass ) IOLog ("%s IOClass is %s\n", inMsg, ioClass->getCStringNoCopy () ); - } + nvram_entry = (void *) OSDynamicCast(IODTNVRAM, entry); + if (!nvram_entry) { + goto release; + } - // if this is an IOProviderClass key print it + options = (volatile void **) &gIOOptionsEntry; + if (!OSCompareAndSwapPtr(NULL, nvram_entry, options)) { + ret = 0; + goto release; + } - if ( mkey->isEqualTo ("IOProviderClass") ) { - ioClass = (OSString *) inDictionary->getObject ("IOProviderClass"); - if ( ioClass ) IOLog ("%s IOProviderClass is %s\n", inMsg, ioClass->getCStringNoCopy () ); + return 0; - } +release: + entry->release(); + return ret; +} - // also print IONameMatch keys - if ( mkey->isEqualTo ("IONameMatch") ) { - ioClass = (OSString *) inDictionary->getObject ("IONameMatch"); - if ( ioClass ) IOLog ("%s IONameMatch is %s\n", inMsg, ioClass->getCStringNoCopy () ); - } +/* pass in a NULL value if you just want to figure out the len */ +boolean_t +PEReadNVRAMProperty(const char *symbol, void *value, + unsigned int *len) +{ + OSObject *obj; + OSData *data; + unsigned int vlen; - // also print IONameMatched keys + if (!symbol || !len) { + goto err; + } - if ( mkey->isEqualTo ("IONameMatched") ) { - ioClass = (OSString *) inDictionary->getObject ("IONameMatched"); - if ( ioClass ) IOLog ("%s IONameMatched is %s\n", inMsg, ioClass->getCStringNoCopy () ); - } + if (init_gIOOptionsEntry() < 0) { + goto err; + } -#if 0 - // print clock-id - - if ( mkey->isEqualTo ("AAPL,clock-id") ) { - char * cstr; - cstr = getCStringForObject (inDictionary->getObject ("AAPL,clock-id")); - if (cstr) - kprintf (" ===> AAPL,clock-id is %s\n", cstr ); - } -#endif + vlen = *len; + *len = 0; - // print name + obj = gIOOptionsEntry->getProperty(symbol); + if (!obj) { + goto err; + } - if ( mkey->isEqualTo ("name") ) { - char nameStr[64]; - nameStr[0] = 0; - getCStringForObject(inDictionary->getObject("name"), nameStr, - sizeof(nameStr)); - if (strlen(nameStr) > 0) - IOLog ("%s name is %s\n", inMsg, nameStr); - } + /* convert to data */ + data = OSDynamicCast(OSData, obj); + if (!data) { + goto err; + } - mkey = (OSSymbol *) mcoll->getNextObject (); + *len = data->getLength(); + vlen = min(vlen, *len); + if (value && vlen) { + memcpy((void *) value, data->getBytesNoCopy(), vlen); + } - i++; - } + return TRUE; - mcoll->release (); +err: + return FALSE; } -static void -getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen) +boolean_t +PEWriteNVRAMBooleanProperty(const char *symbol, boolean_t value) { - char * buffer; - unsigned int len, i; + const OSSymbol *sym = NULL; + OSBoolean *data = NULL; + bool ret = false; - if ( (NULL == inObj) || (NULL == outStr)) - return; + if (symbol == NULL) { + goto exit; + } + + if (init_gIOOptionsEntry() < 0) { + goto exit; + } + + if ((sym = OSSymbol::withCStringNoCopy(symbol)) == NULL) { + goto exit; + } + + data = value ? kOSBooleanTrue : kOSBooleanFalse; + ret = gIOOptionsEntry->setProperty(sym, data); - char * objString = (char *) (inObj->getMetaClass())->getClassName(); + sym->release(); - if ((0 == strncmp(objString, "OSString", sizeof("OSString"))) || - (0 == strncmp(objString, "OSSymbol", sizeof("OSSymbol")))) - strlcpy(outStr, ((OSString *)inObj)->getCStringNoCopy(), outStrLen); + /* success, force the NVRAM to flush writes */ + if (ret == true) { + gIOOptionsEntry->sync(); + } - else if (0 == strncmp(objString, "OSData", sizeof("OSData"))) { - len = ((OSData *)inObj)->getLength(); - buffer = (char *)((OSData *)inObj)->getBytesNoCopy(); - if (buffer && (len > 0)) { - for (i=0; i < len; i++) { - outStr[i] = buffer[i]; - } - outStr[len] = 0; - } - } +exit: + return ret; } -/* IOShutdownNotificationsTimedOut - * - Called from a timer installed by PEHaltRestart - */ -static void IOShutdownNotificationsTimedOut( - thread_call_param_t p0, - thread_call_param_t p1) +static boolean_t +PEWriteNVRAMPropertyInternal(const char *symbol, boolean_t copySymbol, const void *value, + const unsigned int len) { - int type = (int)p0; + const OSSymbol *sym; + OSData *data; + bool ret = false; - /* 30 seconds has elapsed - resume shutdown */ - if(gIOPlatform) gIOPlatform->haltRestart(type); -} + if (!symbol || !value || !len) { + goto err; + } + if (init_gIOOptionsEntry() < 0) { + goto err; + } -extern "C" { + if (copySymbol == TRUE) { + sym = OSSymbol::withCString(symbol); + } else { + sym = OSSymbol::withCStringNoCopy(symbol); + } -/* - * Callouts from BSD for machine name & model - */ + if (!sym) { + goto err; + } -boolean_t PEGetMachineName( char * name, int maxLength ) -{ - if( gIOPlatform) - return( gIOPlatform->getMachineName( name, maxLength )); - else - return( false ); + data = OSData::withBytes((void *) value, len); + if (!data) { + goto sym_done; + } + + ret = gIOOptionsEntry->setProperty(sym, data); + data->release(); + +sym_done: + sym->release(); + + if (ret == true) { + gIOOptionsEntry->sync(); + return TRUE; + } + +err: + return FALSE; } -boolean_t PEGetModelName( char * name, int maxLength ) +boolean_t +PEWriteNVRAMProperty(const char *symbol, const void *value, + const unsigned int len) { - if( gIOPlatform) - return( gIOPlatform->getModelName( name, maxLength )); - else - return( false ); + return PEWriteNVRAMPropertyInternal(symbol, FALSE, value, len); } -int PEGetPlatformEpoch(void) +boolean_t +PEWriteNVRAMPropertyWithCopy(const char *symbol, const void *value, + const unsigned int len) { - if( gIOPlatform) - return( gIOPlatform->getBootROMType()); - else - return( -1 ); + return PEWriteNVRAMPropertyInternal(symbol, TRUE, value, len); } -int PEHaltRestart(unsigned int type) +boolean_t +PERemoveNVRAMProperty(const char *symbol) { - IOPMrootDomain *pmRootDomain = IOService::getPMRootDomain(); - AbsoluteTime deadline; - thread_call_t shutdown_hang; - unsigned int tell_type; - - if(type == kPEHaltCPU || type == kPERestartCPU || type == kPEUPSDelayHaltCPU) - { - /* Notify IOKit PM clients of shutdown/restart - Clients subscribe to this message with a call to - IOService::registerInterest() - */ - - /* Spawn a thread that will panic in 30 seconds. - If all goes well the machine will be off by the time - the timer expires. - */ - shutdown_hang = thread_call_allocate( &IOShutdownNotificationsTimedOut, - (thread_call_param_t) type); - clock_interval_to_deadline( 30, kSecondScale, &deadline ); - thread_call_enter1_delayed( shutdown_hang, 0, deadline ); - + const OSSymbol *sym; - if( kPEUPSDelayHaltCPU == type ) { - tell_type = kPEHaltCPU; - } else { - tell_type = type; - } + if (!symbol) { + goto err; + } + + if (init_gIOOptionsEntry() < 0) { + goto err; + } + + sym = OSSymbol::withCStringNoCopy(symbol); + if (!sym) { + goto err; + } + + gIOOptionsEntry->removeProperty(sym); - pmRootDomain->handlePlatformHaltRestart(tell_type); - /* This notification should have few clients who all do - their work synchronously. - - In this "shutdown notification" context we don't give - drivers the option of working asynchronously and responding - later. PM internals make it very hard to wait for asynchronous - replies. - */ - } + sym->release(); - if (gIOPlatform) return gIOPlatform->haltRestart(type); - else return -1; + gIOOptionsEntry->sync(); + return TRUE; + +err: + return FALSE; +} + +long +PEGetGMTTimeOfDay(void) +{ + clock_sec_t secs; + clock_usec_t usecs; + + PEGetUTCTimeOfDay(&secs, &usecs); + return secs; } -UInt32 PESavePanicInfo(UInt8 *buffer, UInt32 length) +void +PESetGMTTimeOfDay(long secs) { - if (gIOPlatform != 0) return gIOPlatform->savePanicInfo(buffer, length); - else return 0; + PESetUTCTimeOfDay(secs, 0); } -long PEGetGMTTimeOfDay(void) +void +PEGetUTCTimeOfDay(clock_sec_t * secs, clock_usec_t * usecs) { - long result = 0; + clock_nsec_t nsecs = 0; - if( gIOPlatform) - result = gIOPlatform->getGMTTimeOfDay(); + *secs = 0; + if (gIOPlatform) { + gIOPlatform->getUTCTimeOfDay(secs, &nsecs); + } - return (result); + assert(nsecs < NSEC_PER_SEC); + *usecs = nsecs / NSEC_PER_USEC; } -void PESetGMTTimeOfDay(long secs) +void +PESetUTCTimeOfDay(clock_sec_t secs, clock_usec_t usecs) { - if( gIOPlatform) - gIOPlatform->setGMTTimeOfDay(secs); + assert(usecs < USEC_PER_SEC); + if (gIOPlatform) { + gIOPlatform->setUTCTimeOfDay(secs, usecs * NSEC_PER_USEC); + } } +coprocessor_type_t +PEGetCoprocessorVersion( void ) +{ + coprocessor_type_t coprocessor_version = kCoprocessorVersionNone; +#if defined(__x86_64__) + IORegistryEntry *platform_entry = NULL; + OSData *coprocessor_version_obj = NULL; + + platform_entry = IORegistryEntry::fromPath(kIODeviceTreePlane ":/efi/platform"); + if (platform_entry != NULL) { + coprocessor_version_obj = OSDynamicCast(OSData, platform_entry->getProperty("apple-coprocessor-version")); + if ((coprocessor_version_obj != NULL) && (coprocessor_version_obj->getLength() <= sizeof(uint64_t))) { + memcpy(&coprocessor_version, coprocessor_version_obj->getBytesNoCopy(), coprocessor_version_obj->getLength()); + } + platform_entry->release(); + } +#endif + return coprocessor_version; +} } /* extern "C" */ -void IOPlatformExpert::registerNVRAMController(IONVRAMController * caller) -{ - OSData * data; - IORegistryEntry * entry; - OSString * string = 0; - char uuid[ 36 + 1 ]; - - entry = IORegistryEntry::fromPath( "/efi/platform", gIODTPlane ); - if ( entry ) - { - data = OSDynamicCast( OSData, entry->getProperty( "system-id" ) ); - if ( data && data->getLength( ) == 16 ) - { - SHA1_CTX context; - uint8_t digest[ SHA_DIGEST_LENGTH ]; - const uuid_t space = { 0x2A, 0x06, 0x19, 0x90, 0xD3, 0x8D, 0x44, 0x40, 0xA1, 0x39, 0xC4, 0x97, 0x70, 0x37, 0x65, 0xAC }; - - SHA1Init( &context ); - SHA1Update( &context, space, sizeof( space ) ); - SHA1Update( &context, data->getBytesNoCopy( ), data->getLength( ) ); - SHA1Final( digest, &context ); - - digest[ 6 ] = ( digest[ 6 ] & 0x0F ) | 0x50; - digest[ 8 ] = ( digest[ 8 ] & 0x3F ) | 0x80; - - uuid_unparse( digest, uuid ); - string = OSString::withCString( uuid ); - } - - entry->release( ); - } - - if ( string == 0 ) - { - entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); - if ( entry ) - { - data = OSDynamicCast( OSData, entry->getProperty( "platform-uuid" ) ); - if ( data && data->getLength( ) == sizeof( uuid_t ) ) - { - uuid_unparse( ( uint8_t * ) data->getBytesNoCopy( ), uuid ); - string = OSString::withCString( uuid ); - } - - entry->release( ); - } - } - - if ( string ) - { - getProvider( )->setProperty( kIOPlatformUUIDKey, string ); - publishResource( kIOPlatformUUIDKey, string ); - - string->release( ); - } - - publishResource("IONVRAM"); -} - -IOReturn IOPlatformExpert::callPlatformFunction(const OSSymbol *functionName, - bool waitForFunction, - void *param1, void *param2, - void *param3, void *param4) -{ - IOService *service, *_resources; - - if (waitForFunction) { - _resources = waitForService(resourceMatching(functionName)); - } else { - _resources = resources(); - } - if (_resources == 0) return kIOReturnUnsupported; - - service = OSDynamicCast(IOService, _resources->getProperty(functionName)); - if (service == 0) return kIOReturnUnsupported; - - return service->callPlatformFunction(functionName, waitForFunction, - param1, param2, param3, param4); -} - -IOByteCount IOPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) -{ - return 0; +bool gIOPlatformUUIDAndSerialDone = false; + +void +IOPlatformExpert::publishPlatformUUIDAndSerial( void ) +{ + if (!gIOPlatformUUIDAndSerialDone) { + // Parse the serial-number data and publish a user-readable string + if (NULL == getProvider()->getProperty(kIOPlatformSerialNumberKey)) { + OSData* mydata = (OSData*) (getProvider()->getProperty("serial-number")); + if (mydata != NULL) { + OSString *serNoString = createSystemSerialNumberString(mydata); + if (serNoString != NULL) { + getProvider()->setProperty(kIOPlatformSerialNumberKey, serNoString); + serNoString->release(); + } + } + } + IOPlatformExpertDevice *provider = OSDynamicCast(IOPlatformExpertDevice, getProvider()); + assert(provider != NULL); + provider->generatePlatformUUID(); + } + + if (gIOPlatformUUIDAndSerialDone) { + publishResource(kIOPlatformUUIDKey, getProvider()->getProperty(kIOPlatformUUIDKey)); + } +} + +void +IOPlatformExpert::publishNVRAM( void ) +{ + if (init_gIOOptionsEntry() < 0) { + IOPlatformExpertDevice *provider = OSDynamicCast(IOPlatformExpertDevice, getProvider()); + assert(provider != NULL); + provider->createNVRAM(); + } + if (gIOOptionsEntry != NULL) { + gIOOptionsEntry->registerService(); + } +} + +void +IOPlatformExpert::registerNVRAMController(IONVRAMController * caller) +{ +#if defined(__x86_64__) + OSData * data; + IORegistryEntry * entry; + + /* + * If we have panic debugging enabled and a prod-fused coprocessor, + * disable cross panics so that the co-processor doesn't cause the system + * to reset when we enter the debugger or hit a panic on the x86 side. + */ + if (panicDebugging) { + entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); + if (entry) { + data = OSDynamicCast( OSData, entry->getProperty( APPLE_VENDOR_VARIABLE_GUID":BridgeOSPanicWatchdogEnabled" )); + if (data && (data->getLength() == sizeof(UInt8))) { + UInt8 *panicWatchdogEnabled = (UInt8 *) data->getBytesNoCopy(); + UInt32 debug_flags = 0; + if (*panicWatchdogEnabled || (PE_i_can_has_debugger(&debug_flags) && + (debug_flags & DB_DISABLE_CROSS_PANIC))) { + coprocessor_cross_panic_enabled = FALSE; + } + } + entry->release(); + } + } + +#if (DEVELOPMENT || DEBUG) + entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); + if (entry) { + data = OSDynamicCast( OSData, entry->getProperty(nvram_osenvironment)); + if (data) { + sysctl_set_osenvironment(data->getLength(), data->getBytesNoCopy()); + entry->removeProperty(nvram_osenvironment); + IODTNVRAM * nvramOptionsEntry = OSDynamicCast(IODTNVRAM, entry); + if (nvramOptionsEntry) { + nvramOptionsEntry->sync(); + } + } + entry->release(); + } + sysctl_unblock_osenvironment(); +#endif + /* on intel the UUID must be published after nvram is available */ + publishPlatformUUIDAndSerial(); + +#endif /* defined(__x86_64__) */ + + publishResource("IONVRAM"); +} + +IOReturn +IOPlatformExpert::callPlatformFunction(const OSSymbol *functionName, + bool waitForFunction, + void *param1, void *param2, + void *param3, void *param4) +{ + IOService *service, *_resources; + + if (functionName == gIOPlatformQuiesceActionKey || + functionName == gIOPlatformActiveActionKey) { + /* + * Services which register for IOPlatformQuiesceAction / IOPlatformActiveAction + * must consume that event themselves, without passing it up to super/IOPlatformExpert. + */ + if (gEnforceQuiesceSafety) { + panic("Class %s passed the quiesce/active action to IOPlatformExpert", + getMetaClass()->getClassName()); + } + } + + if (waitForFunction) { + _resources = waitForService(resourceMatching(functionName)); + } else { + _resources = getResourceService(); + } + if (_resources == NULL) { + return kIOReturnUnsupported; + } + + service = OSDynamicCast(IOService, _resources->getProperty(functionName)); + if (service == NULL) { + return kIOReturnUnsupported; + } + + return service->callPlatformFunction(functionName, waitForFunction, + param1, param2, param3, param4); +} + +IOByteCount +IOPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) +{ + return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -948,291 +1525,463 @@ IOByteCount IOPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) OSDefineMetaClassAndAbstractStructors( IODTPlatformExpert, IOPlatformExpert ) -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 0); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 1); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 2); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 3); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 4); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 5); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 6); -OSMetaClassDefineReservedUnused(IODTPlatformExpert, 7); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 0); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 1); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 2); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 3); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 4); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 5); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 6); +OSMetaClassDefineReservedUnused(IODTPlatformExpert, 7); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -IOService * IODTPlatformExpert::probe( IOService * provider, - SInt32 * score ) +IOService * +IODTPlatformExpert::probe( IOService * provider, + SInt32 * score ) { - if( !super::probe( provider, score)) - return( 0 ); + if (!super::probe( provider, score)) { + return NULL; + } - // check machine types - if( !provider->compareNames( getProperty( gIONameMatchKey ) )) - return( 0 ); + // check machine types + if (!provider->compareNames( getProperty( gIONameMatchKey ))) { + return NULL; + } - return( this); + return this; } -bool IODTPlatformExpert::configure( IOService * provider ) +bool +IODTPlatformExpert::configure( IOService * provider ) { - if( !super::configure( provider)) - return( false); + if (!super::configure( provider)) { + return false; + } - processTopLevel( provider ); + processTopLevel( provider ); - return( true ); + return true; } -IOService * IODTPlatformExpert::createNub( IORegistryEntry * from ) +IOService * +IODTPlatformExpert::createNub( IORegistryEntry * from ) { - IOService * nub; + IOService * nub; - nub = new IOPlatformDevice; - if( nub) { - if( !nub->init( from, gIODTPlane )) { - nub->free(); - nub = 0; + nub = new IOPlatformDevice; + if (nub) { + if (!nub->init( from, gIODTPlane )) { + nub->free(); + nub = NULL; + } } - } - return( nub); + return nub; } -bool IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter ) -{ - IORegistryEntry * next; - IOService * nub; - bool ok = true; - - if( iter) { - while( (next = (IORegistryEntry *) iter->getNextObject())) { - - if( 0 == (nub = createNub( next ))) - continue; - - nub->attach( parent ); - nub->registerService(); - } - iter->release(); - } +bool +IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter ) +{ + IORegistryEntry * next; + IOService * nub; + bool ok = true; + + if (iter) { + while ((next = (IORegistryEntry *) iter->getNextObject())) { + if (NULL == (nub = createNub( next ))) { + continue; + } + + nub->attach( parent ); +#if !defined(__x86_64__) + OSData *tmpData = (OSData *)next->getProperty("device_type"); + if (tmpData == NULL) { + nub->registerService(); + continue; + } + + char *device_type = (char *)tmpData->getBytesNoCopy(); + if (strcmp(device_type, "cpu") != 0) { + nub->registerService(); + continue; + } + + tmpData = (OSData *)next->getProperty("reg"); + assert(tmpData != NULL); + assert(tmpData->getLength() >= sizeof(UInt32)); + + uint32_t phys_id = *(UInt32 *)tmpData->getBytesNoCopy(); + int logical_cpu_id = ml_get_cpu_number(phys_id); + int logical_cluster_id = ml_get_cluster_number(phys_id); + + /* + * If the following condition triggers, it means that a CPU that was present in the DT + * was ignored by XNU at topology parsing time. This can happen currently when using the + * cpus=N boot-arg; for example, cpus=1 will cause XNU to parse and enable a single CPU. + * + * Note that this condition will not trigger for harvested cores because these do not show up + * in the DT/IORegistry in the first place. + */ + if (logical_cpu_id < 0) { + nub->registerService(); + continue; + } + + __assert_only bool logical_id_added_to_ioreg = nub->setProperty("logical-cpu-id", logical_cpu_id, 32U); + assert(logical_id_added_to_ioreg == true); + logical_id_added_to_ioreg = nub->setProperty("logical-cluster-id", logical_cluster_id, 32U); + assert(logical_id_added_to_ioreg == true); +#endif + nub->registerService(); + } + iter->release(); + } - return( ok ); + return ok; } -void IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry ) +void +IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry ) { - OSIterator * kids; - IORegistryEntry * next; - IORegistryEntry * cpus; - IORegistryEntry * options; + OSIterator * kids; + IORegistryEntry * next; + IORegistryEntry * cpus; - // infanticide - kids = IODTFindMatchingEntries( rootEntry, 0, deleteList() ); - if( kids) { - while( (next = (IORegistryEntry *)kids->getNextObject())) { - next->detachAll( gIODTPlane); + // infanticide + kids = IODTFindMatchingEntries( rootEntry, 0, deleteList()); + if (kids) { + while ((next = (IORegistryEntry *)kids->getNextObject())) { + next->detachAll( gIODTPlane); + } + kids->release(); } - kids->release(); - } - // Publish an IODTNVRAM class on /options. - options = rootEntry->childFromPath("options", gIODTPlane); - if (options) { - dtNVRAM = new IODTNVRAM; - if (dtNVRAM) { - if (!dtNVRAM->init(options, gIODTPlane)) { - dtNVRAM->release(); - dtNVRAM = 0; - } else { - dtNVRAM->attach(this); - dtNVRAM->registerService(); - } - } - } + publishNVRAM(); + assert(gIOOptionsEntry != NULL); // subclasses that do their own NVRAM initialization shouldn't be calling this + dtNVRAM = gIOOptionsEntry; - // Publish the cpus. - cpus = rootEntry->childFromPath( "cpus", gIODTPlane); - if ( cpus) - createNubs( this, IODTFindMatchingEntries( cpus, kIODTExclusive, 0)); + // Publish the cpus. + cpus = rootEntry->childFromPath( "cpus", gIODTPlane); + if (cpus) { + createNubs( this, IODTFindMatchingEntries( cpus, kIODTExclusive, NULL)); + cpus->release(); + } - // publish top level, minus excludeList - createNubs( this, IODTFindMatchingEntries( rootEntry, kIODTExclusive, excludeList())); + // publish top level, minus excludeList + createNubs( this, IODTFindMatchingEntries( rootEntry, kIODTExclusive, excludeList())); } -IOReturn IODTPlatformExpert::getNubResources( IOService * nub ) +IOReturn +IODTPlatformExpert::getNubResources( IOService * nub ) { - if( nub->getDeviceMemory()) - return( kIOReturnSuccess ); + if (nub->getDeviceMemory()) { + return kIOReturnSuccess; + } - IODTResolveAddressing( nub, "reg", 0); + IODTResolveAddressing( nub, "reg", NULL); - return( kIOReturnSuccess); + return kIOReturnSuccess; } -bool IODTPlatformExpert::compareNubName( const IOService * nub, - OSString * name, OSString ** matched ) const +bool +IODTPlatformExpert::compareNubName( const IOService * nub, + OSString * name, OSString ** matched ) const { - return( IODTCompareNubName( nub, name, matched ) - || super::compareNubName( nub, name, matched) ); + return IODTCompareNubName( nub, name, matched ) + || super::compareNubName( nub, name, matched); } -bool IODTPlatformExpert::getModelName( char * name, int maxLength ) -{ - OSData * prop; - const char * str; - int len; - char c; - bool ok = false; - maxLength--; +/* + * Do not use this method directly, it returns inconsistent results + * across architectures and is considered deprecated. + * + * Use getTargetName and getProductName respectively. For example: + * + * targetName: J137AP + * productName: iMacPro1,1 + * + * targetName: D331pAP + * productName: iPhone11,6 + */ - prop = (OSData *) getProvider()->getProperty( gIODTCompatibleKey ); - if( prop ) { - str = (const char *) prop->getBytesNoCopy(); +bool +IODTPlatformExpert::getModelName( char * name, int maxLength ) +{ + OSData * prop; + const char * str; + int len; + char c; + bool ok = false; + + maxLength--; + + prop = (OSData *) getProvider()->getProperty( gIODTCompatibleKey ); + if (prop) { + str = (const char *) prop->getBytesNoCopy(); + + if (0 == strncmp( str, "AAPL,", strlen( "AAPL," ))) { + str += strlen( "AAPL," ); + } + + len = 0; + while ((c = *str++)) { + if ((c == '/') || (c == ' ')) { + c = '-'; + } + + name[len++] = c; + if (len >= maxLength) { + break; + } + } + + name[len] = 0; + ok = true; + } + return ok; +} - if( 0 == strncmp( str, "AAPL,", strlen( "AAPL," ) )) - str += strlen( "AAPL," ); +/* + * Do not use this method directly, it returns inconsistent results + * across architectures and is considered deprecated. + * + * Use getTargetName and getProductName respectively. For example: + * + * targetName: J137AP + * productName: iMacPro1,1 + * + * targetName: D331pAP + * productName: iPhone11,6 + */ - len = 0; - while( (c = *str++)) { - if( (c == '/') || (c == ' ')) - c = '-'; +bool +IODTPlatformExpert::getMachineName( char * name, int maxLength ) +{ + OSData * prop; + bool ok = false; - name[ len++ ] = c; - if( len >= maxLength) - break; + maxLength--; + prop = (OSData *) getProvider()->getProperty( gIODTModelKey ); + ok = (NULL != prop); + + if (ok) { + strlcpy( name, (const char *) prop->getBytesNoCopy(), maxLength ); } - name[ len ] = 0; - ok = true; - } - return( ok ); + return ok; } -bool IODTPlatformExpert::getMachineName( char * name, int maxLength ) +/* Examples: J137AP, D331pAP... */ + +bool +IODTPlatformExpert::getTargetName( char * name, int maxLength ) { - OSData * prop; - bool ok = false; +#if __x86_64__ + OSData * prop; - maxLength--; - prop = (OSData *) getProvider()->getProperty( gIODTModelKey ); - ok = (0 != prop); + const OSSymbol * key = gIODTBridgeModelKey; - if( ok ) - strlcpy( name, (const char *) prop->getBytesNoCopy(), maxLength ); + maxLength--; + prop = (OSData *) getProvider()->getProperty( key ); + + if (prop == NULL) { + // This happens if there is no bridge. + char const * const unknown = ""; + + strlcpy( name, unknown, maxLength ); + } else { + strlcpy( name, (const char *)prop->getBytesNoCopy(), maxLength ); + } - return( ok ); + return true; +#else + return getModelName( name, maxLength ); +#endif +} + +/* Examples: iMacPro1,1, iPhone11,6... */ + +bool +IODTPlatformExpert::getProductName( char * name, int maxLength ) +{ +#if __x86_64__ + return getModelName( name, maxLength ); +#else + return getMachineName( name, maxLength ); +#endif } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -void IODTPlatformExpert::registerNVRAMController( IONVRAMController * nvram ) +void +IODTPlatformExpert::registerNVRAMController( IONVRAMController * nvram ) { - if (dtNVRAM) dtNVRAM->registerNVRAMController(nvram); - - super::registerNVRAMController(nvram); + if (dtNVRAM) { + dtNVRAM->registerNVRAMController(nvram); + } + + super::registerNVRAMController(nvram); } -int IODTPlatformExpert::haltRestart(unsigned int type) +int +IODTPlatformExpert::haltRestart(unsigned int type) { - if (dtNVRAM) dtNVRAM->sync(); - - return super::haltRestart(type); + if (dtNVRAM) { + dtNVRAM->sync(); + } + + return super::haltRestart(type); } -IOReturn IODTPlatformExpert::readXPRAM(IOByteCount offset, UInt8 * buffer, - IOByteCount length) +IOReturn +IODTPlatformExpert::readXPRAM(IOByteCount offset, UInt8 * buffer, + IOByteCount length) { - if (dtNVRAM) return dtNVRAM->readXPRAM(offset, buffer, length); - else return kIOReturnNotReady; + if (dtNVRAM) { + return dtNVRAM->readXPRAM(offset, buffer, length); + } else { + return kIOReturnNotReady; + } } -IOReturn IODTPlatformExpert::writeXPRAM(IOByteCount offset, UInt8 * buffer, - IOByteCount length) +IOReturn +IODTPlatformExpert::writeXPRAM(IOByteCount offset, UInt8 * buffer, + IOByteCount length) { - if (dtNVRAM) return dtNVRAM->writeXPRAM(offset, buffer, length); - else return kIOReturnNotReady; + if (dtNVRAM) { + return dtNVRAM->writeXPRAM(offset, buffer, length); + } else { + return kIOReturnNotReady; + } } -IOReturn IODTPlatformExpert::readNVRAMProperty( +IOReturn +IODTPlatformExpert::readNVRAMProperty( IORegistryEntry * entry, const OSSymbol ** name, OSData ** value ) { - if (dtNVRAM) return dtNVRAM->readNVRAMProperty(entry, name, value); - else return kIOReturnNotReady; + if (dtNVRAM) { + return dtNVRAM->readNVRAMProperty(entry, name, value); + } else { + return kIOReturnNotReady; + } } -IOReturn IODTPlatformExpert::writeNVRAMProperty( +IOReturn +IODTPlatformExpert::readNVRAMProperty( IORegistryEntry * entry, - const OSSymbol * name, OSData * value ) + OSSharedPtr& name, OSSharedPtr& value ) { - if (dtNVRAM) return dtNVRAM->writeNVRAMProperty(entry, name, value); - else return kIOReturnNotReady; + const OSSymbol* nameRaw = NULL; + OSData* valueRaw = NULL; + + IOReturn result = readNVRAMProperty(entry, &nameRaw, &valueRaw); + + name.reset(nameRaw, OSNoRetain); + value.reset(valueRaw, OSNoRetain); + + return result; } -OSDictionary *IODTPlatformExpert::getNVRAMPartitions(void) +IOReturn +IODTPlatformExpert::writeNVRAMProperty( + IORegistryEntry * entry, + const OSSymbol * name, OSData * value ) { - if (dtNVRAM) return dtNVRAM->getNVRAMPartitions(); - else return 0; + if (dtNVRAM) { + return dtNVRAM->writeNVRAMProperty(entry, name, value); + } else { + return kIOReturnNotReady; + } } -IOReturn IODTPlatformExpert::readNVRAMPartition(const OSSymbol * partitionID, - IOByteCount offset, UInt8 * buffer, - IOByteCount length) +OSDictionary * +IODTPlatformExpert::getNVRAMPartitions(void) { - if (dtNVRAM) return dtNVRAM->readNVRAMPartition(partitionID, offset, - buffer, length); - else return kIOReturnNotReady; + if (dtNVRAM) { + return dtNVRAM->getNVRAMPartitions(); + } else { + return NULL; + } } -IOReturn IODTPlatformExpert::writeNVRAMPartition(const OSSymbol * partitionID, - IOByteCount offset, UInt8 * buffer, - IOByteCount length) +IOReturn +IODTPlatformExpert::readNVRAMPartition(const OSSymbol * partitionID, + IOByteCount offset, UInt8 * buffer, + IOByteCount length) { - if (dtNVRAM) return dtNVRAM->writeNVRAMPartition(partitionID, offset, - buffer, length); - else return kIOReturnNotReady; + if (dtNVRAM) { + return dtNVRAM->readNVRAMPartition(partitionID, offset, + buffer, length); + } else { + return kIOReturnNotReady; + } } -IOByteCount IODTPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) +IOReturn +IODTPlatformExpert::writeNVRAMPartition(const OSSymbol * partitionID, + IOByteCount offset, UInt8 * buffer, + IOByteCount length) { - IOByteCount lengthSaved = 0; - - if (dtNVRAM) lengthSaved = dtNVRAM->savePanicInfo(buffer, length); - - if (lengthSaved == 0) lengthSaved = super::savePanicInfo(buffer, length); - - return lengthSaved; + if (dtNVRAM) { + return dtNVRAM->writeNVRAMPartition(partitionID, offset, + buffer, length); + } else { + return kIOReturnNotReady; + } } -OSString* IODTPlatformExpert::createSystemSerialNumberString(OSData* myProperty) { - UInt8* serialNumber; - unsigned int serialNumberSize; - unsigned short pos = 0; - char* temp; - char SerialNo[30]; - - if (myProperty != NULL) { - serialNumberSize = myProperty->getLength(); - serialNumber = (UInt8*)(myProperty->getBytesNoCopy()); - temp = (char*)serialNumber; - if (serialNumberSize > 0) { - // check to see if this is a CTO serial number... - while (pos < serialNumberSize && temp[pos] != '-') pos++; - - if (pos < serialNumberSize) { // there was a hyphen, so it's a CTO serial number - memcpy(SerialNo, serialNumber + 12, 8); - memcpy(&SerialNo[8], serialNumber, 3); - SerialNo[11] = '-'; - memcpy(&SerialNo[12], serialNumber + 3, 8); - SerialNo[20] = 0; - } else { // just a normal serial number - memcpy(SerialNo, serialNumber + 13, 8); - memcpy(&SerialNo[8], serialNumber, 3); - SerialNo[11] = 0; - } - return OSString::withCString(SerialNo); - } - } - return NULL; +IOByteCount +IODTPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) +{ + IOByteCount lengthSaved = 0; + + if (dtNVRAM) { + lengthSaved = dtNVRAM->savePanicInfo(buffer, length); + } + + if (lengthSaved == 0) { + lengthSaved = super::savePanicInfo(buffer, length); + } + + return lengthSaved; +} + +OSString* +IODTPlatformExpert::createSystemSerialNumberString(OSData* myProperty) +{ + UInt8* serialNumber; + unsigned int serialNumberSize; + unsigned short pos = 0; + char* temp; + char SerialNo[30]; + + if (myProperty != NULL) { + serialNumberSize = myProperty->getLength(); + serialNumber = (UInt8*)(myProperty->getBytesNoCopy()); + temp = (char*)serialNumber; + if (serialNumberSize > 0) { + // check to see if this is a CTO serial number... + while (pos < serialNumberSize && temp[pos] != '-') { + pos++; + } + + if (pos < serialNumberSize) { // there was a hyphen, so it's a CTO serial number + memcpy(SerialNo, serialNumber + 12, 8); + memcpy(&SerialNo[8], serialNumber, 3); + SerialNo[11] = '-'; + memcpy(&SerialNo[12], serialNumber + 3, 8); + SerialNo[20] = 0; + } else { // just a normal serial number + memcpy(SerialNo, serialNumber + 13, 8); + memcpy(&SerialNo[8], serialNumber, 3); + SerialNo[11] = 0; + } + return OSString::withCString(SerialNo); + } + } + return NULL; } @@ -1243,108 +1992,238 @@ OSString* IODTPlatformExpert::createSystemSerialNumberString(OSData* myProperty) OSDefineMetaClassAndStructors(IOPlatformExpertDevice, IOService) -OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 0); -OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 1); -OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 2); -OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 3); +OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 0); +OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 1); +OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 2); +OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 3); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -bool IOPlatformExpertDevice::compareName( OSString * name, - OSString ** matched ) const +bool +IOPlatformExpertDevice::compareName( OSString * name, + OSString ** matched ) const { - return( IODTCompareNubName( this, name, matched )); + return IODTCompareNubName( this, name, matched ); } bool -IOPlatformExpertDevice::initWithArgs( - void * dtTop, void * p2, void * p3, void * p4 ) +IOPlatformExpertDevice::init(void *dtRoot) { - IORegistryEntry * dt = 0; - void * argsData[ 4 ]; - bool ok; + IORegistryEntry * dt = NULL; + bool ok; - // dtTop may be zero on non- device tree systems - if( dtTop && (dt = IODeviceTreeAlloc( dtTop ))) - ok = super::init( dt, gIODTPlane ); - else - ok = super::init(); + if ((dtRoot != NULL) && (dt = IODeviceTreeAlloc(dtRoot))) { + ok = super::init( dt, gIODTPlane ); + } else { + ok = super::init(); + } - if( !ok) - return( false); + if (!ok) { + return false; + } - workLoop = IOWorkLoop::workLoop(); - if (!workLoop) - return false; + return true; +} - argsData[ 0 ] = dtTop; - argsData[ 1 ] = p2; - argsData[ 2 ] = p3; - argsData[ 3 ] = p4; +bool +IOPlatformExpertDevice::startIOServiceMatching(void) +{ + workLoop = IOWorkLoop::workLoop(); + if (!workLoop) { + return false; + } - setProperty("IOPlatformArgs", (void *)argsData, sizeof(argsData)); + registerService(); - return( true); + return true; } -IOWorkLoop *IOPlatformExpertDevice::getWorkLoop() const +IOWorkLoop * +IOPlatformExpertDevice::getWorkLoop() const { - return workLoop; + return workLoop; } -IOReturn IOPlatformExpertDevice::setProperties( OSObject * properties ) +IOReturn +IOPlatformExpertDevice::setProperties( OSObject * properties ) { - OSDictionary * dictionary; - OSObject * object; - IOReturn status; + return kIOReturnUnsupported; +} - status = super::setProperties( properties ); - if ( status != kIOReturnUnsupported ) return status; +IOReturn +IOPlatformExpertDevice::newUserClient( task_t owningTask, void * securityID, + UInt32 type, OSDictionary * properties, + IOUserClient ** handler ) +{ + IOReturn err = kIOReturnSuccess; + IOUserClient * newConnect = NULL; + IOUserClient * theConnect = NULL; - status = IOUserClient::clientHasPrivilege( current_task( ), kIOClientPrivilegeAdministrator ); - if ( status != kIOReturnSuccess ) return status; + switch (type) { + case kIOKitDiagnosticsClientType: + newConnect = IOKitDiagnosticsClient::withTask(owningTask); + if (!newConnect) { + err = kIOReturnNotPermitted; + } + break; + case kIOKitUserServerClientType: + newConnect = IOUserServer::withTask(owningTask); + if (!newConnect) { + err = kIOReturnNotPermitted; + } + break; + default: + err = kIOReturnBadArgument; + } - dictionary = OSDynamicCast( OSDictionary, properties ); - if ( dictionary == 0 ) return kIOReturnBadArgument; + if (newConnect) { + if ((false == newConnect->attach(this)) + || (false == newConnect->start(this))) { + newConnect->detach( this ); + newConnect->release(); + err = kIOReturnNotPermitted; + } else { + theConnect = newConnect; + } + } - object = dictionary->getObject( kIOPlatformUUIDKey ); - if ( object ) - { - IORegistryEntry * entry; - OSString * string; - uuid_t uuid; + *handler = theConnect; + return err; +} - string = ( OSString * ) getProperty( kIOPlatformUUIDKey ); - if ( string ) return kIOReturnNotPermitted; +void +IOPlatformExpertDevice::free() +{ + if (workLoop) { + workLoop->release(); + } +} - string = OSDynamicCast( OSString, object ); - if ( string == 0 ) return kIOReturnBadArgument; +void +IOPlatformExpertDevice::configureDefaults( void ) +{ + createNVRAM(); + // Parse the serial-number data and publish a user-readable string + OSData* mydata = (OSData*) (getProperty("serial-number")); + if (mydata != NULL) { + OSString *serNoString = OSString::withCString((const char *)mydata->getBytesNoCopy()); + if (serNoString != NULL) { + setProperty(kIOPlatformSerialNumberKey, serNoString); + serNoString->release(); + } + } + generatePlatformUUID(); +} - status = uuid_parse( string->getCStringNoCopy( ), uuid ); - if ( status != 0 ) return kIOReturnBadArgument; +void +IOPlatformExpertDevice::createNVRAM( void ) +{ + /* + * Publish an IODTNVRAM class on /options, if present. + * DT-based platforms may need NVRAM access prior to the start + * of IOKit matching, to support security-related operations + * that must happen before machine_lockdown(). + */ + IORegistryEntry *options = IORegistryEntry::fromPath("/options", gIODTPlane); + if (options == NULL) { + return; // /options may not be present + } - entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); - if ( entry ) - { - entry->setProperty( "platform-uuid", uuid, sizeof( uuid_t ) ); - entry->release( ); - } + assert(gIOOptionsEntry == NULL); + gIOOptionsEntry = new IODTNVRAM; - setProperty( kIOPlatformUUIDKey, string ); - publishResource( kIOPlatformUUIDKey, string ); + assert(gIOOptionsEntry != NULL); - return kIOReturnSuccess; - } + gIOOptionsEntry->init(options, gIODTPlane); - return kIOReturnUnsupported; + gIOOptionsEntry->attach(this); + options->release(); } -void IOPlatformExpertDevice::free() +void +IOPlatformExpertDevice::generatePlatformUUID( void ) { - if (workLoop) - workLoop->release(); -} + IORegistryEntry * entry; + OSString * string = NULL; + uuid_string_t uuid; + +#if !defined(__x86_64__) + entry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ); + if (entry) { + OSData * data1; + + data1 = OSDynamicCast( OSData, entry->getProperty( "unique-chip-id" )); + if (data1 && data1->getLength() == 8) { + OSData * data2; + + data2 = OSDynamicCast( OSData, entry->getProperty( "chip-id" )); + if (data2 && data2->getLength() == 4) { + SHA1_CTX context; + uint8_t digest[SHA_DIGEST_LENGTH]; + const uuid_t space = { 0xA6, 0xDD, 0x4C, 0xCB, 0xB5, 0xE8, 0x4A, 0xF5, 0xAC, 0xDD, 0xB6, 0xDC, 0x6A, 0x05, 0x42, 0xB8 }; + + SHA1Init( &context ); + SHA1Update( &context, space, sizeof(space)); + SHA1Update( &context, data1->getBytesNoCopy(), data1->getLength()); + SHA1Update( &context, data2->getBytesNoCopy(), data2->getLength()); + SHA1Final( digest, &context ); + + digest[6] = (digest[6] & 0x0F) | 0x50; + digest[8] = (digest[8] & 0x3F) | 0x80; + uuid_unparse( digest, uuid ); + string = OSString::withCString( uuid ); + } + } + + entry->release(); + } +#else /* !defined(__x86_64__) */ + OSData * data; + + entry = IORegistryEntry::fromPath( "/efi/platform", gIODTPlane ); + if (entry) { + data = OSDynamicCast( OSData, entry->getProperty( "system-id" )); + if (data && data->getLength() == 16) { + SHA1_CTX context; + uint8_t digest[SHA_DIGEST_LENGTH]; + const uuid_t space = { 0x2A, 0x06, 0x19, 0x90, 0xD3, 0x8D, 0x44, 0x40, 0xA1, 0x39, 0xC4, 0x97, 0x70, 0x37, 0x65, 0xAC }; + + SHA1Init( &context ); + SHA1Update( &context, space, sizeof(space)); + SHA1Update( &context, data->getBytesNoCopy(), data->getLength()); + SHA1Final( digest, &context ); + + digest[6] = (digest[6] & 0x0F) | 0x50; + digest[8] = (digest[8] & 0x3F) | 0x80; + + uuid_unparse( digest, uuid ); + string = OSString::withCString( uuid ); + } + + entry->release(); + } + if (!string) { + /* vmware still runs this path */ + entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); + if (entry) { + data = OSDynamicCast( OSData, entry->getProperty( "platform-uuid" )); + if (data && data->getLength() == sizeof(uuid_t)) { + uuid_unparse((uint8_t *) data->getBytesNoCopy(), uuid ); + string = OSString::withCString( uuid ); + } + entry->release(); + } + } +#endif /* defined(__x86_64__) */ + + if (string) { + setProperty( kIOPlatformUUIDKey, string ); + gIOPlatformUUIDAndSerialDone = true; + + string->release(); + } +} /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super @@ -1352,28 +2231,31 @@ void IOPlatformExpertDevice::free() OSDefineMetaClassAndStructors(IOPlatformDevice, IOService) -OSMetaClassDefineReservedUnused(IOPlatformDevice, 0); -OSMetaClassDefineReservedUnused(IOPlatformDevice, 1); -OSMetaClassDefineReservedUnused(IOPlatformDevice, 2); -OSMetaClassDefineReservedUnused(IOPlatformDevice, 3); +OSMetaClassDefineReservedUnused(IOPlatformDevice, 0); +OSMetaClassDefineReservedUnused(IOPlatformDevice, 1); +OSMetaClassDefineReservedUnused(IOPlatformDevice, 2); +OSMetaClassDefineReservedUnused(IOPlatformDevice, 3); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -bool IOPlatformDevice::compareName( OSString * name, - OSString ** matched ) const +bool +IOPlatformDevice::compareName( OSString * name, + OSString ** matched ) const { - return( ((IOPlatformExpert *)getProvider())-> - compareNubName( this, name, matched )); + return ((IOPlatformExpert *)getProvider())-> + compareNubName( this, name, matched ); } -IOService * IOPlatformDevice::matchLocation( IOService * /* client */ ) +IOService * +IOPlatformDevice::matchLocation( IOService * /* client */ ) { - return( this ); + return this; } -IOReturn IOPlatformDevice::getResources( void ) +IOReturn +IOPlatformDevice::getResources( void ) { - return( ((IOPlatformExpert *)getProvider())->getNubResources( this )); + return ((IOPlatformExpert *)getProvider())->getNubResources( this ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -1386,23 +2268,27 @@ IOReturn IOPlatformDevice::getResources( void ) *********************************************************************/ class IOPanicPlatform : IOPlatformExpert { - OSDeclareDefaultStructors(IOPanicPlatform); + OSDeclareDefaultStructors(IOPanicPlatform); public: - bool start(IOService * provider); + bool start(IOService * provider) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(IOPanicPlatform, IOPlatformExpert); -bool IOPanicPlatform::start(IOService * provider) { - const char * platform_name = "(unknown platform name)"; +bool +IOPanicPlatform::start(IOService * provider) +{ + const char * platform_name = "(unknown platform name)"; - if (provider) platform_name = provider->getName(); + if (provider) { + platform_name = provider->getName(); + } - panic("Unable to find driver for this platform: \"%s\".\n", - platform_name); + panic("Unable to find driver for this platform: \"%s\".\n", + platform_name); - return false; + return false; }