X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0a7de7458d150b5d4dffc935ba399be265ef0a1a..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/iokit/Kernel/IOPlatformExpert.cpp diff --git a/iokit/Kernel/IOPlatformExpert.cpp b/iokit/Kernel/IOPlatformExpert.cpp index d7087fbb2..407dd5b02 100644 --- a/iokit/Kernel/IOPlatformExpert.cpp +++ b/iokit/Kernel/IOPlatformExpert.cpp @@ -27,6 +27,7 @@ */ #include +#include #include #include #include @@ -40,45 +41,65 @@ #include #include #include +#include + +#include "IOKitKernelInternal.h" #include #include #include +#include #include #include +#if defined(__arm64__) +#include +#endif + extern "C" { #include #include #include +#include } #define kShutdownTimeout 30 //in secs -#if !CONFIG_EMBEDDED +#if defined(XNU_TARGET_OS_OSX) boolean_t coprocessor_cross_panic_enabled = TRUE; -#define APPLE_SECURE_BOOT_VARIABLE_GUID "94b73556-2197-4702-82a8-3e1337dafbfb" -#endif /* !CONFIG_EMBEDDED */ +#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); -OSMetaClassDefineReservedUsed(IOPlatformExpert, 2); -OSMetaClassDefineReservedUsed(IOPlatformExpert, 3); -OSMetaClassDefineReservedUsed(IOPlatformExpert, 4); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 0); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 1); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 2); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 3); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 4); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 5); +OSMetaClassDefineReservedUsedX86(IOPlatformExpert, 6); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 5); -OSMetaClassDefineReservedUnused(IOPlatformExpert, 6); OSMetaClassDefineReservedUnused(IOPlatformExpert, 7); OSMetaClassDefineReservedUnused(IOPlatformExpert, 8); OSMetaClassDefineReservedUnused(IOPlatformExpert, 9); @@ -133,7 +154,7 @@ IOPlatformExpert::start( IOService * provider ) // Register the presence or lack thereof a system // PCI address mapper with the IOMapper class - IOMapper::setMapperRequired(0 != getProperty(kIOPlatformMapperPresentKey)); + IOMapper::setMapperRequired(NULL != getProperty(kIOPlatformMapperPresentKey)); gIOInterruptControllers = OSDictionary::withCapacity(1); gIOInterruptControllersLock = IOLockAlloc(); @@ -155,23 +176,20 @@ IOPlatformExpert::start( IOService * provider ) 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(); - } - } +#if !defined(__x86_64__) + publishPlatformUUIDAndSerial(); +#endif /* !defined(__x86_64__) */ -#if !CONFIG_EMBEDDED +#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); } @@ -190,7 +208,7 @@ IOPlatformExpert::configure( IOService * provider ) dict->retain(); topLevel->removeObject( dict ); nub = createNub( dict ); - if (0 == nub) { + if (NULL == nub) { continue; } dict->release(); @@ -211,7 +229,7 @@ IOPlatformExpert::createNub( OSDictionary * from ) if (nub) { if (!nub->init( from )) { nub->release(); - nub = 0; + nub = NULL; } } return nub; @@ -224,6 +242,16 @@ IOPlatformExpert::compareNubName( const IOService * nub, return nub->IORegistryEntry::compareName( name, matched ); } +bool +IOPlatformExpert::compareNubName( const IOService * nub, + OSString * name, OSSharedPtr& matched ) const +{ + OSString* matchedRaw = NULL; + bool result = compareNubName(nub, name, &matchedRaw); + matched.reset(matchedRaw, OSNoRetain); + return result; +} + IOReturn IOPlatformExpert::getNubResources( IOService * nub ) { @@ -278,6 +306,18 @@ IOPlatformExpert::getModelName( char * /*name*/, int /*maxLength*/) return false; } +bool +IOPlatformExpert::getTargetName( char * /*name*/, int /*maxLength*/) +{ + return false; +} + +bool +IOPlatformExpert::getProductName( char * /*name*/, int /*maxLength*/) +{ + return false; +} + OSString* IOPlatformExpert::createSystemSerialNumberString(OSData* myProperty) { @@ -291,7 +331,7 @@ IOPlatformExpert::getPhysicalRangeAllocator(void) getProperty("Platform Memory Ranges")); } -int (*PE_halt_restart)(unsigned int type) = 0; +int (*PE_halt_restart)(unsigned int type) = NULL; int IOPlatformExpert::haltRestart(unsigned int type) @@ -310,7 +350,7 @@ IOPlatformExpert::haltRestart(unsigned int type) type = kPEHaltCPU; } -#if !CONFIG_EMBEDDED +#if defined (__x86_64__) // On ARM kPEPanicRestartCPU is supported in the drivers if (type == kPEPanicRestartCPU) { type = kPERestartCPU; @@ -408,7 +448,7 @@ IOPlatformExpert::lookUpInterruptController(OSSymbol *name) while (1) { object = gIOInterruptControllers->getObject(name); - if (object != 0) { + if (object != NULL) { break; } @@ -424,9 +464,12 @@ IOPlatformExpert::lookUpInterruptController(OSSymbol *name) void IOPlatformExpert::setCPUInterruptProperties(IOService *service) { - IOCPUInterruptController *controller; + IOInterruptController *controller; + + OSDictionary *matching = serviceMatching("IOInterruptController"); + matching = propertyMatching(gPlatformInterruptControllerName, kOSBooleanTrue, matching); - controller = OSDynamicCast(IOCPUInterruptController, waitForService(serviceMatching("IOCPUInterruptController"))); + controller = OSDynamicCast(IOInterruptController, waitForService(matching)); if (controller) { controller->setCPUInterruptProperties(service); } @@ -825,16 +868,19 @@ getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen) /* 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) { -#ifdef CONFIG_EMBEDDED +#if !defined(__x86_64__) /* 30 seconds has elapsed - panic */ panic("Halt/Restart Timed Out"); -#else /* ! CONFIG_EMBEDDED */ +#else /* !defined(__x86_64__) */ int type = (int)(long)p0; uint32_t timeout = (uint32_t)(uintptr_t)p1; @@ -849,7 +895,7 @@ IOShutdownNotificationsTimedOut( if (gIOPlatform) { gIOPlatform->haltRestart(type); } -#endif /* CONFIG_EMBEDDED */ +#endif /* defined(__x86_64__) */ } @@ -858,6 +904,11 @@ extern "C" { * Callouts from BSD for machine name & model */ +/* + * PEGetMachineName() and PEGetModelName() are inconsistent across + * architectures, and considered deprecated. Use PEGetTargetName() and + * PEGetProductName() instead. + */ boolean_t PEGetMachineName( char * name, int maxLength ) { @@ -868,6 +919,11 @@ PEGetMachineName( char * name, int maxLength ) } } +/* + * PEGetMachineName() and PEGetModelName() are inconsistent across + * architectures, and considered deprecated. Use PEGetTargetName() and + * PEGetProductName() instead. + */ boolean_t PEGetModelName( char * name, int maxLength ) { @@ -878,18 +934,51 @@ PEGetModelName( char * name, int maxLength ) } } +boolean_t +PEGetTargetName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getTargetName( name, maxLength ); + } else { + return false; + } +} + +boolean_t +PEGetProductName( char * name, int maxLength ) +{ + if (gIOPlatform) { + return gIOPlatform->getProductName( name, maxLength ); + } else { + return false; + } +} + int PEGetPlatformEpoch(void) { if (gIOPlatform) { - return gIOPlatform->getBootROMType(); + return (int) gIOPlatform->getBootROMType(); } else { return -1; } } +/* 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 +} + int -PEHaltRestart(unsigned int type) +PEHaltRestartInternal(unsigned int type, uint32_t details) { IOPMrootDomain *pmRootDomain; AbsoluteTime deadline; @@ -900,6 +989,13 @@ PEHaltRestart(unsigned int type) 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 @@ -911,7 +1007,7 @@ PEHaltRestart(unsigned int type) * the timer expires. If the device wants a different * timeout, use that value instead of 30 seconds. */ -#if CONFIG_EMBEDDED +#if defined(__arm__) || defined(__arm64__) #define RESTART_NODE_PATH "/defaults" #else #define RESTART_NODE_PATH "/chosen" @@ -924,10 +1020,20 @@ PEHaltRestart(unsigned int type) } } - 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 ); +#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 + + 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 @@ -938,46 +1044,47 @@ PEHaltRestart(unsigned int type) * later. PM internals make it very hard to wait for asynchronous * replies. */ - } else if (type == kPEPanicRestartCPU || type == kPEPanicSync) { + } 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 !CONFIG_EMBEDDED +#if defined(__x86_64__) if (coprocessor_cross_panic_enabled) #endif - IOCPURunPlatformPanicActions(kPEPanicEnd); - - // Callout to shutdown the disk driver once we've returned from the - // kPEPanicEnd callback (and we know all core dumps on this system - // are complete). - IOCPURunPlatformPanicActions(kPEPanicDiskShutdown); + 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); + IOCPURunPlatformPanicActions(type, details); PE_sync_panic_buffers(); } else if (type == kPEPanicEnd) { -#if !CONFIG_EMBEDDED +#if defined(__x86_64__) if (coprocessor_cross_panic_enabled) #endif - IOCPURunPlatformPanicActions(type); + IOCPURunPlatformPanicActions(type, details); } else if (type == kPEPanicBegin) { -#if !CONFIG_EMBEDDED +#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); + IOCPURunPlatformPanicActions(type, details); } } } +skip_to_haltRestart: if (gIOPlatform) { return gIOPlatform->haltRestart(type); } else { @@ -985,11 +1092,17 @@ PEHaltRestart(unsigned int type) } } +int +PEHaltRestart(unsigned int type) +{ + return PEHaltRestartInternal(type, 0); +} + UInt32 PESavePanicInfo(UInt8 *buffer, UInt32 length) { - if (gIOPlatform != 0) { - return gIOPlatform->savePanicInfo(buffer, length); + if (gIOPlatform != NULL) { + return (UInt32) gIOPlatform->savePanicInfo(buffer, length); } else { return 0; } @@ -1003,6 +1116,12 @@ PESavePanicInfoAction(void *buffer, UInt32 offset, UInt32 length) } +/* + * 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) { @@ -1246,7 +1365,7 @@ coprocessor_type_t PEGetCoprocessorVersion( void ) { coprocessor_type_t coprocessor_version = kCoprocessorVersionNone; -#if !CONFIG_EMBEDDED +#if defined(__x86_64__) IORegistryEntry *platform_entry = NULL; OSData *coprocessor_version_obj = NULL; @@ -1263,46 +1382,53 @@ PEGetCoprocessorVersion( void ) } } /* extern "C" */ +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; - OSString * string = 0; - uuid_string_t uuid; - -#if CONFIG_EMBEDDED - 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 /* !CONFIG_EMBEDDED */ /* * If we have panic debugging enabled and a prod-fused coprocessor, * disable cross panics so that the co-processor doesn't cause the system @@ -1311,11 +1437,11 @@ IOPlatformExpert::registerNVRAMController(IONVRAMController * caller) if (panicDebugging) { entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); if (entry) { - data = OSDynamicCast( OSData, entry->getProperty( APPLE_SECURE_BOOT_VARIABLE_GUID":EffectiveProductionStatus" )); + data = OSDynamicCast( OSData, entry->getProperty( APPLE_VENDOR_VARIABLE_GUID":BridgeOSPanicWatchdogEnabled" )); if (data && (data->getLength() == sizeof(UInt8))) { - UInt8 *isProdFused = (UInt8 *) data->getBytesNoCopy(); + UInt8 *panicWatchdogEnabled = (UInt8 *) data->getBytesNoCopy(); UInt32 debug_flags = 0; - if (*isProdFused || (PE_i_can_has_debugger(&debug_flags) && + if (*panicWatchdogEnabled || (PE_i_can_has_debugger(&debug_flags) && (debug_flags & DB_DISABLE_CROSS_PANIC))) { coprocessor_cross_panic_enabled = FALSE; } @@ -1324,49 +1450,26 @@ IOPlatformExpert::registerNVRAMController(IONVRAMController * caller) } } - entry = IORegistryEntry::fromPath( "/efi/platform", gIODTPlane ); +#if (DEVELOPMENT || DEBUG) + entry = IORegistryEntry::fromPath( "/options", 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(); - } -#endif /* !CONFIG_EMBEDDED */ - - 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 ); + 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(); } + entry->release(); } + sysctl_unblock_osenvironment(); +#endif + /* on intel the UUID must be published after nvram is available */ + publishPlatformUUIDAndSerial(); - if (string) { - getProvider()->setProperty( kIOPlatformUUIDKey, string ); - publishResource( kIOPlatformUUIDKey, string ); - - string->release(); - } +#endif /* defined(__x86_64__) */ publishResource("IONVRAM"); } @@ -1379,17 +1482,29 @@ IOPlatformExpert::callPlatformFunction(const OSSymbol *functionName, { 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 == 0) { + if (_resources == NULL) { return kIOReturnUnsupported; } service = OSDynamicCast(IOService, _resources->getProperty(functionName)); - if (service == 0) { + if (service == NULL) { return kIOReturnUnsupported; } @@ -1426,12 +1541,12 @@ IODTPlatformExpert::probe( IOService * provider, SInt32 * score ) { if (!super::probe( provider, score)) { - return 0; + return NULL; } // check machine types if (!provider->compareNames( getProperty( gIONameMatchKey ))) { - return 0; + return NULL; } return this; @@ -1458,7 +1573,7 @@ IODTPlatformExpert::createNub( IORegistryEntry * from ) if (nub) { if (!nub->init( from, gIODTPlane )) { nub->free(); - nub = 0; + nub = NULL; } } return nub; @@ -1473,11 +1588,50 @@ IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter ) if (iter) { while ((next = (IORegistryEntry *) iter->getNextObject())) { - if (0 == (nub = createNub( next ))) { + 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(); @@ -1492,7 +1646,6 @@ IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry ) OSIterator * kids; IORegistryEntry * next; IORegistryEntry * cpus; - IORegistryEntry * options; // infanticide kids = IODTFindMatchingEntries( rootEntry, 0, deleteList()); @@ -1503,26 +1656,14 @@ IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry ) 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(); - options->release(); - } - } - } + 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)); + createNubs( this, IODTFindMatchingEntries( cpus, kIODTExclusive, NULL)); cpus->release(); } @@ -1537,7 +1678,7 @@ IODTPlatformExpert::getNubResources( IOService * nub ) return kIOReturnSuccess; } - IODTResolveAddressing( nub, "reg", 0); + IODTResolveAddressing( nub, "reg", NULL); return kIOReturnSuccess; } @@ -1550,6 +1691,20 @@ IODTPlatformExpert::compareNubName( const IOService * nub, || super::compareNubName( nub, name, matched); } + +/* + * 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 + */ + bool IODTPlatformExpert::getModelName( char * name, int maxLength ) { @@ -1587,6 +1742,19 @@ IODTPlatformExpert::getModelName( char * name, int maxLength ) return ok; } +/* + * 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 + */ + bool IODTPlatformExpert::getMachineName( char * name, int maxLength ) { @@ -1595,7 +1763,7 @@ IODTPlatformExpert::getMachineName( char * name, int maxLength ) maxLength--; prop = (OSData *) getProvider()->getProperty( gIODTModelKey ); - ok = (0 != prop); + ok = (NULL != prop); if (ok) { strlcpy( name, (const char *) prop->getBytesNoCopy(), maxLength ); @@ -1604,6 +1772,46 @@ IODTPlatformExpert::getMachineName( char * name, int maxLength ) return ok; } +/* Examples: J137AP, D331pAP... */ + +bool +IODTPlatformExpert::getTargetName( char * name, int maxLength ) +{ +#if __x86_64__ + OSData * prop; + + const OSSymbol * key = gIODTBridgeModelKey; + + 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 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 @@ -1660,6 +1868,22 @@ IODTPlatformExpert::readNVRAMProperty( } } +IOReturn +IODTPlatformExpert::readNVRAMProperty( + IORegistryEntry * entry, + OSSharedPtr& name, OSSharedPtr& value ) +{ + const OSSymbol* nameRaw = NULL; + OSData* valueRaw = NULL; + + IOReturn result = readNVRAMProperty(entry, &nameRaw, &valueRaw); + + name.reset(nameRaw, OSNoRetain); + value.reset(valueRaw, OSNoRetain); + + return result; +} + IOReturn IODTPlatformExpert::writeNVRAMProperty( IORegistryEntry * entry, @@ -1678,7 +1902,7 @@ IODTPlatformExpert::getNVRAMPartitions(void) if (dtNVRAM) { return dtNVRAM->getNVRAMPartitions(); } else { - return 0; + return NULL; } } @@ -1783,14 +2007,12 @@ IOPlatformExpertDevice::compareName( OSString * name, } bool -IOPlatformExpertDevice::initWithArgs( - void * dtTop, void * p2, void * p3, void * p4 ) +IOPlatformExpertDevice::init(void *dtRoot) { - IORegistryEntry * dt = 0; + IORegistryEntry * dt = NULL; bool ok; - // dtTop may be zero on non- device tree systems - if (dtTop && (dt = IODeviceTreeAlloc( dtTop ))) { + if ((dtRoot != NULL) && (dt = IODeviceTreeAlloc(dtRoot))) { ok = super::init( dt, gIODTPlane ); } else { ok = super::init(); @@ -1800,11 +2022,19 @@ IOPlatformExpertDevice::initWithArgs( return false; } + return true; +} + +bool +IOPlatformExpertDevice::startIOServiceMatching(void) +{ workLoop = IOWorkLoop::workLoop(); if (!workLoop) { return false; } + registerService(); + return true; } @@ -1826,8 +2056,8 @@ IOPlatformExpertDevice::newUserClient( task_t owningTask, void * securityID, IOUserClient ** handler ) { IOReturn err = kIOReturnSuccess; - IOUserClient * newConnect = 0; - IOUserClient * theConnect = 0; + IOUserClient * newConnect = NULL; + IOUserClient * theConnect = NULL; switch (type) { case kIOKitDiagnosticsClientType: @@ -1836,6 +2066,12 @@ IOPlatformExpertDevice::newUserClient( task_t owningTask, void * securityID, err = kIOReturnNotPermitted; } break; + case kIOKitUserServerClientType: + newConnect = IOUserServer::withTask(owningTask); + if (!newConnect) { + err = kIOReturnNotPermitted; + } + break; default: err = kIOReturnBadArgument; } @@ -1863,6 +2099,131 @@ IOPlatformExpertDevice::free() } } +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(); +} + +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 + } + + assert(gIOOptionsEntry == NULL); + gIOOptionsEntry = new IODTNVRAM; + + assert(gIOOptionsEntry != NULL); + + gIOOptionsEntry->init(options, gIODTPlane); + + gIOOptionsEntry->attach(this); + options->release(); +} + +void +IOPlatformExpertDevice::generatePlatformUUID( void ) +{ + 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