/* * Copyright (c) 1998-2022 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 * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * 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@ */ #include #include #include #include #include #include #include #include #include #include #include #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 } #define kShutdownTimeout 30 //in secs #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 gEnforcePlatformActionSafety = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super IOService OSDefineMetaClassAndStructors(IOPlatformExpert, IOService) 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 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); OSSafeReleaseNULL(physicalRanges); setPlatform( this ); gIOPlatform = this; PMInstantiatePowerDomains(); #if !defined(__x86_64__) publishPlatformUUIDAndSerial(); #endif /* !defined(__x86_64__) */ #if defined (__x86_64__) if (PEGetCoprocessorVersion() >= kCoprocessorVersion2) { coprocessor_paniclog_flush = TRUE; extended_debug_log_init(); } #endif PE_parse_boot_argn("enforce_platform_action_safety", &gEnforcePlatformActionSafety, sizeof(gEnforcePlatformActionSafety)); return configure(provider); } bool IOPlatformExpert::configure( IOService * provider ) { OSSet * topLevel; OSDictionary * dict; IOService * nub = NULL; topLevel = OSDynamicCast( OSSet, getProperty("top-level")); if (topLevel) { while ((dict = OSDynamicCast( OSDictionary, topLevel->getAnyObject()))) { dict->retain(); topLevel->removeObject( dict ); OSSafeReleaseNULL(nub); nub = createNub( dict ); dict->release(); if (NULL == nub) { continue; } nub->attach( this ); nub->registerService(); } } OSSafeReleaseNULL(nub); return true; } IOService * IOPlatformExpert::createNub( OSDictionary * from ) { IOService * nub; nub = new IOPlatformDevice; if (nub) { if (!nub->init( from )) { nub->release(); nub = NULL; } } return nub; } bool IOPlatformExpert::compareNubName( const IOService * nub, OSString * name, OSString ** matched ) const { 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 ) { return kIOReturnSuccess; } long IOPlatformExpert::getBootROMType(void) { return _peBootROMType; } long IOPlatformExpert::getChipSetType(void) { return _peChipSetType; } long IOPlatformExpert::getMachineType(void) { return _peMachineType; } void IOPlatformExpert::setBootROMType(long peBootROMType) { _peBootROMType = peBootROMType; } void IOPlatformExpert::setChipSetType(long peChipSetType) { _peChipSetType = peChipSetType; } void IOPlatformExpert::setMachineType(long peMachineType) { _peMachineType = peMachineType; } bool IOPlatformExpert::getMachineName( char * /*name*/, int /*maxLength*/) { return false; } bool 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) { return NULL; } IORangeAllocator * IOPlatformExpert::getPhysicalRangeAllocator(void) { return OSDynamicCast(IORangeAllocator, getProperty("Platform Memory Ranges")); } int (*PE_halt_restart)(unsigned int type) = NULL; int IOPlatformExpert::haltRestart(unsigned int type) { if (type == kPEPanicSync) { return 0; } if (type == kPEHangCPU) { while (true) { asm volatile (""); } } if (type == kPEUPSDelayHaltCPU) { // RestartOnPowerLoss feature was turned on, proceed with shutdown. type = kPEHaltCPU; } #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) { #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); #else // PE_initialize_console(0, kPEDisableScreen); IOCPUSleepKernel(); // PE_initialize_console(0, kPEEnableScreen); #endif } long IOPlatformExpert::getGMTTimeOfDay(void) { return 0; } void IOPlatformExpert::setGMTTimeOfDay(long secs) { } IOReturn IOPlatformExpert::getConsoleInfo( PE_Video * consoleInfo ) { return PE_current_console( consoleInfo); } IOReturn IOPlatformExpert::setConsoleInfo( PE_Video * consoleInfo, unsigned int op) { return PE_initialize_console( consoleInfo, op ); } IOReturn IOPlatformExpert::registerInterruptController(OSSymbol *name, IOInterruptController *interruptController) { IOLockLock(gIOInterruptControllersLock); gIOInterruptControllers->setObject(name, interruptController); IOLockWakeup(gIOInterruptControllersLock, gIOInterruptControllers, /* one-thread */ false); IOLockUnlock(gIOInterruptControllersLock); return kIOReturnSuccess; } IOReturn IOPlatformExpert::deregisterInterruptController(OSSymbol *name) { IOLockLock(gIOInterruptControllersLock); gIOInterruptControllers->removeObject(name); IOLockUnlock(gIOInterruptControllersLock); return kIOReturnSuccess; } IOInterruptController * IOPlatformExpert::lookUpInterruptController(OSSymbol *name) { OSObject *object; IOLockLock(gIOInterruptControllersLock); while (1) { object = gIOInterruptControllers->getObject(name); 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*/) { return true; } void IOPlatformExpert::getUTCTimeOfDay(clock_sec_t * secs, clock_nsec_t * nsecs) { *secs = getGMTTimeOfDay(); *nsecs = 0; } void IOPlatformExpert::setUTCTimeOfDay(clock_sec_t secs, __unused clock_nsec_t nsecs) { setGMTTimeOfDay(secs); } //********************************************************************************* // PMLog // //********************************************************************************* void IOPlatformExpert:: PMLog(const char *who, unsigned long event, unsigned long param1, unsigned long param2) { clock_sec_t nows; clock_usec_t nowus; clock_get_system_microtime(&nows, &nowus); nowus += (nows % 1000) * 1000000; kprintf("pm%u %p %.30s %d %lx %lx\n", nowus, OBFUSCATE(current_thread()), who, // Identity (int) event, (long)OBFUSCATE(param1), (long)OBFUSCATE(param2)); // Args } //********************************************************************************* // PMInstantiatePowerDomains // // 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 // in a platform-specific subclass. //********************************************************************************* void IOPlatformExpert::PMInstantiatePowerDomains( void ) { root = new IOPMrootDomain; root->init(); root->attach(this); root->start(this); } //********************************************************************************* // PMRegisterDevice // // In this vanilla implementation, all callers are made children of the root power domain. // Where this is inappropriate, PMRegisterDevice is overridden in a platform-specific subclass. //********************************************************************************* void IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevice) { root->addPowerChild( theDevice ); } //********************************************************************************* // hasPMFeature // //********************************************************************************* bool IOPlatformExpert::hasPMFeature(unsigned long featureMask) { return (_pePMFeatures & featureMask) != 0; } //********************************************************************************* // hasPrivPMFeature // //********************************************************************************* bool IOPlatformExpert::hasPrivPMFeature(unsigned long privFeatureMask) { return (_pePrivPMFeatures & privFeatureMask) != 0; } //********************************************************************************* // numBatteriesSupported // //********************************************************************************* int IOPlatformExpert::numBatteriesSupported(void) { return _peNumBatteriesSupported; } //********************************************************************************* // CheckSubTree // // This method is called by the instantiated sublass of the platform expert to // determine how a device should be inserted into the Power Domain. The subclass // provides an XML power tree description against which a device is matched based // on class and provider. If a match is found this routine returns true in addition // to flagging the description tree at the appropriate node that a device has been // registered for the given service. //********************************************************************************* 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) { OSCollectionIterator * mcoll = OSCollectionIterator::withCollection(inDictionary); OSSymbol * mkey; OSString * ioClass; unsigned int i = 0; mcoll->reset(); mkey = OSDynamicCast(OSSymbol, mcoll->getNextObject()); while (mkey) { // kprintf ("dictionary key #%d: %s\n", i, mkey->getCStringNoCopy () ); // if this is the IOClass key, print it's contents if (mkey->isEqualTo("IOClass")) { ioClass = (OSString *) inDictionary->getObject("IOClass"); if (ioClass) { IOLog("%s IOClass is %s\n", inMsg, ioClass->getCStringNoCopy()); } } // if this is an IOProviderClass key print it if (mkey->isEqualTo("IOProviderClass")) { ioClass = (OSString *) inDictionary->getObject("IOProviderClass"); if (ioClass) { IOLog("%s IOProviderClass is %s\n", inMsg, ioClass->getCStringNoCopy()); } } // also print IONameMatch keys if (mkey->isEqualTo("IONameMatch")) { ioClass = (OSString *) inDictionary->getObject("IONameMatch"); if (ioClass) { IOLog("%s IONameMatch is %s\n", inMsg, ioClass->getCStringNoCopy()); } } // also print IONameMatched keys if (mkey->isEqualTo("IONameMatched")) { ioClass = (OSString *) inDictionary->getObject("IONameMatched"); if (ioClass) { IOLog("%s IONameMatched is %s\n", inMsg, ioClass->getCStringNoCopy()); } } #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 // print name 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); } } mkey = (OSSymbol *) mcoll->getNextObject(); i++; } mcoll->release(); } static void getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen) { char * buffer; unsigned int len, i; if ((NULL == inObj) || (NULL == outStr)) { return; } 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; } } } /* 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__) */ } 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 ) { if (gIOPlatform) { return gIOPlatform->getMachineName( name, maxLength ); } else { return false; } } /* * 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; } } 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 (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_entries_async(0, PAGE_SIZE, PAGE_SIZE, true, true); arm64_sync_tlb(true); #endif // defined(__arm64__) } 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(__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()); } OSSafeReleaseNULL(node); } #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 * 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); } } } else if (type == kPEPanicDiagnosticsDone || type == kPEPanicDiagnosticsInProgress) { IOCPURunPlatformPanicActions(type, details); } skip_to_haltRestart: if (gIOPlatform) { // note that this will not necessarily halt or restart the system... // Implementors of this function will check the type and take action accordingly return gIOPlatform->haltRestart(type); } else { return -1; } } int PEHaltRestart(unsigned int type) { return PEHaltRestartInternal(type, 0); } UInt32 PESavePanicInfo(UInt8 *buffer, UInt32 length) { if (gIOPlatform != NULL) { return (UInt32) gIOPlatform->savePanicInfo(buffer, length); } else { return 0; } } void PESavePanicInfoAction(void *buffer, UInt32 offset, UInt32 length) { IOCPURunPlatformPanicSyncAction(buffer, offset, length); return; } /* * 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; if (gIOOptionsEntry) { return 0; } entry = IORegistryEntry::fromPath( "/options", gIODTPlane ); if (!entry) { return -1; } nvram_entry = (void *) OSDynamicCast(IODTNVRAM, entry); if (!nvram_entry) { goto release; } options = (volatile void **) &gIOOptionsEntry; if (!OSCompareAndSwapPtr(NULL, nvram_entry, options)) { ret = 0; goto release; } return 0; release: entry->release(); return ret; } /* 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; if (!symbol || !len) { goto err; } if (init_gIOOptionsEntry() < 0) { goto err; } vlen = *len; *len = 0; obj = gIOOptionsEntry->getProperty(symbol); if (!obj) { goto err; } /* convert to data */ data = OSDynamicCast(OSData, obj); if (!data) { goto err; } *len = data->getLength(); vlen = min(vlen, *len); if (value && vlen) { memcpy((void *) value, data->getBytesNoCopy(), vlen); } return TRUE; err: return FALSE; } boolean_t PEReadNVRAMBooleanProperty(const char *symbol, boolean_t *value) { OSObject *obj; OSBoolean *data; if (!symbol || !value) { goto err; } if (init_gIOOptionsEntry() < 0) { goto err; } obj = gIOOptionsEntry->getProperty(symbol); if (!obj) { return TRUE; } /* convert to bool */ data = OSDynamicCast(OSBoolean, obj); if (!data) { goto err; } *value = data->isTrue() ? TRUE : FALSE; return TRUE; err: return FALSE; } boolean_t PEWriteNVRAMBooleanProperty(const char *symbol, boolean_t value) { const OSSymbol *sym = NULL; OSBoolean *data = NULL; bool ret = false; 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); sym->release(); /* success, force the NVRAM to flush writes */ if (ret == true) { gIOOptionsEntry->sync(); } exit: return ret; } static boolean_t PEWriteNVRAMPropertyInternal(const char *symbol, boolean_t copySymbol, const void *value, const unsigned int len) { const OSSymbol *sym; OSData *data; bool ret = false; if (!symbol || !value || !len) { goto err; } if (init_gIOOptionsEntry() < 0) { goto err; } if (copySymbol == TRUE) { sym = OSSymbol::withCString(symbol); } else { sym = OSSymbol::withCStringNoCopy(symbol); } if (!sym) { goto err; } 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 PEWriteNVRAMProperty(const char *symbol, const void *value, const unsigned int len) { return PEWriteNVRAMPropertyInternal(symbol, FALSE, value, len); } boolean_t PEWriteNVRAMPropertyWithCopy(const char *symbol, const void *value, const unsigned int len) { return PEWriteNVRAMPropertyInternal(symbol, TRUE, value, len); } boolean_t PERemoveNVRAMProperty(const char *symbol) { const OSSymbol *sym; if (!symbol) { goto err; } if (init_gIOOptionsEntry() < 0) { goto err; } sym = OSSymbol::withCStringNoCopy(symbol); if (!sym) { goto err; } gIOOptionsEntry->removeProperty(sym); sym->release(); gIOOptionsEntry->sync(); return TRUE; err: return FALSE; } boolean_t PESyncNVRAM(void) { if (gIOOptionsEntry != nullptr) { gIOOptionsEntry->sync(); } return TRUE; } long PEGetGMTTimeOfDay(void) { clock_sec_t secs; clock_usec_t usecs; PEGetUTCTimeOfDay(&secs, &usecs); return secs; } void PESetGMTTimeOfDay(long secs) { PESetUTCTimeOfDay(secs, 0); } void PEGetUTCTimeOfDay(clock_sec_t * secs, clock_usec_t * usecs) { clock_nsec_t nsecs = 0; *secs = 0; if (gIOPlatform) { gIOPlatform->getUTCTimeOfDay(secs, &nsecs); } assert(nsecs < NSEC_PER_SEC); *usecs = nsecs / NSEC_PER_USEC; } void PESetUTCTimeOfDay(clock_sec_t secs, clock_usec_t usecs) { 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" */ 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 WITHOUT behavior to reboot after any crash (DB_REBOOT_ALWAYS) * and we are on a co-processor system that has the panic SoC watchdog enabled, 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 && !(debug_boot_arg & DB_REBOOT_ALWAYS)) { 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; OSObject *prop = NULL; IOReturn ret; if (functionName == gIOPlatformQuiesceActionKey || functionName == gIOPlatformActiveActionKey || functionName == gIOPlatformPanicActionKey) { /* * Services which register for IOPlatformQuiesceAction / IOPlatformActiveAction / IOPlatformPanicAction * must consume that event themselves, without passing it up to super/IOPlatformExpert. */ if (gEnforcePlatformActionSafety) { panic("Class %s passed the %s action to IOPlatformExpert", getMetaClass()->getClassName(), functionName->getCStringNoCopy()); } } if (waitForFunction) { _resources = waitForService(resourceMatching(functionName)); } else { _resources = getResourceService(); } if (_resources == NULL) { return kIOReturnUnsupported; } prop = _resources->copyProperty(functionName); service = OSDynamicCast(IOService, prop); if (service == NULL) { ret = kIOReturnUnsupported; goto finish; } ret = service->callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4); finish: OSSafeReleaseNULL(prop); return ret; } IOByteCount IOPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length) { return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super #define super IOPlatformExpert 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); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ IOService * IODTPlatformExpert::probe( IOService * provider, SInt32 * score ) { if (!super::probe( provider, score)) { return NULL; } // check machine types if (!provider->compareNames( getProperty( gIONameMatchKey ))) { return NULL; } return this; } bool IODTPlatformExpert::configure( IOService * provider ) { if (!super::configure( provider)) { return false; } processTopLevel( provider ); return true; } IOService * IODTPlatformExpert::createNub( IORegistryEntry * from ) { IOService * nub; nub = new IOPlatformDevice; if (nub) { if (!nub->init( from, gIODTPlane )) { nub->free(); nub = NULL; } } return nub; } bool IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter ) { IORegistryEntry * next; IOService * nub = NULL; bool ok = true; if (iter) { while ((next = (IORegistryEntry *) iter->getNextObject())) { OSSafeReleaseNULL(nub); 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(); } OSSafeReleaseNULL(nub); iter->release(); } return ok; } void IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry ) { OSIterator * kids; IORegistryEntry * next; IORegistryEntry * cpus; // infanticide kids = IODTFindMatchingEntries( rootEntry, 0, deleteList()); if (kids) { while ((next = (IORegistryEntry *)kids->getNextObject())) { next->detachAll( gIODTPlane); } kids->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, NULL)); cpus->release(); } // publish top level, minus excludeList createNubs( this, IODTFindMatchingEntries( rootEntry, kIODTExclusive, excludeList())); } IOReturn IODTPlatformExpert::getNubResources( IOService * nub ) { if (nub->getDeviceMemory()) { return kIOReturnSuccess; } IODTResolveAddressing( nub, "reg", NULL); return kIOReturnSuccess; } bool IODTPlatformExpert::compareNubName( const IOService * nub, OSString * name, OSString ** matched ) const { return IODTCompareNubName( nub, name, matched ) || 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 ) { 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; } /* * 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 ) { OSData * prop; bool ok = false; maxLength--; prop = (OSData *) getProvider()->getProperty( gIODTModelKey ); ok = (NULL != prop); if (ok) { strlcpy( name, (const char *) prop->getBytesNoCopy(), 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 IODTPlatformExpert::registerNVRAMController( IONVRAMController * nvram ) { if (dtNVRAM) { dtNVRAM->registerNVRAMController(nvram); } super::registerNVRAMController(nvram); } int IODTPlatformExpert::haltRestart(unsigned int type) { return super::haltRestart(type); } IOReturn IODTPlatformExpert::readXPRAM(IOByteCount offset, UInt8 * buffer, IOByteCount length) { if (dtNVRAM) { return dtNVRAM->readXPRAM(offset, buffer, length); } else { return kIOReturnNotReady; } } IOReturn IODTPlatformExpert::writeXPRAM(IOByteCount offset, UInt8 * buffer, IOByteCount length) { if (dtNVRAM) { return dtNVRAM->writeXPRAM(offset, buffer, length); } else { return kIOReturnNotReady; } } IOReturn IODTPlatformExpert::readNVRAMProperty( IORegistryEntry * entry, const OSSymbol ** name, OSData ** value ) { if (dtNVRAM) { return dtNVRAM->readNVRAMProperty(entry, name, value); } else { return kIOReturnNotReady; } } 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, const OSSymbol * name, OSData * value ) { if (dtNVRAM) { return dtNVRAM->writeNVRAMProperty(entry, name, value); } else { return kIOReturnNotReady; } } OSDictionary * IODTPlatformExpert::getNVRAMPartitions(void) { if (dtNVRAM) { return dtNVRAM->getNVRAMPartitions(); } else { return NULL; } } IOReturn IODTPlatformExpert::readNVRAMPartition(const OSSymbol * partitionID, IOByteCount offset, UInt8 * buffer, IOByteCount length) { if (dtNVRAM) { return dtNVRAM->readNVRAMPartition(partitionID, offset, buffer, length); } else { return kIOReturnNotReady; } } IOReturn IODTPlatformExpert::writeNVRAMPartition(const OSSymbol * partitionID, IOByteCount offset, UInt8 * buffer, IOByteCount length) { if (dtNVRAM) { return dtNVRAM->writeNVRAMPartition(partitionID, offset, buffer, length); } else { return kIOReturnNotReady; } } 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; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef super #define super IOService OSDefineMetaClassAndStructors(IOPlatformExpertDevice, IOService) OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 0); OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 1); OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 2); OSMetaClassDefineReservedUnused(IOPlatformExpertDevice, 3); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool IOPlatformExpertDevice::compareName( OSString * name, OSString ** matched ) const { return IODTCompareNubName( this, name, matched ); } bool IOPlatformExpertDevice::init(void *dtRoot) { IORegistryEntry * dt = NULL; bool ok; if ((dtRoot != NULL) && (dt = IODeviceTreeAlloc(dtRoot))) { ok = super::init( dt, gIODTPlane ); } else { ok = super::init(); } if (!ok) { return false; } return true; } bool IOPlatformExpertDevice::startIOServiceMatching(void) { workLoop = IOWorkLoop::workLoop(); if (!workLoop) { return false; } registerService(); return true; } IOWorkLoop * IOPlatformExpertDevice::getWorkLoop() const { return workLoop; } IOReturn IOPlatformExpertDevice::setProperties( OSObject * properties ) { return kIOReturnUnsupported; } IOReturn IOPlatformExpertDevice::newUserClient( task_t owningTask, void * securityID, UInt32 type, OSDictionary * properties, IOUserClient ** handler ) { IOReturn err = kIOReturnSuccess; IOUserClient * newConnect = NULL; IOUserClient * theConnect = NULL; 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; } if (newConnect) { if ((false == newConnect->attach(this)) || (false == newConnect->start(this))) { newConnect->detach( this ); newConnect->release(); err = kIOReturnNotPermitted; } else { theConnect = newConnect; } } *handler = theConnect; return err; } void IOPlatformExpertDevice::free() { if (workLoop) { workLoop->release(); } } 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); gIOOptionsEntry->start(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 #define super IOService OSDefineMetaClassAndStructors(IOPlatformDevice, IOService) OSMetaClassDefineReservedUnused(IOPlatformDevice, 0); OSMetaClassDefineReservedUnused(IOPlatformDevice, 1); OSMetaClassDefineReservedUnused(IOPlatformDevice, 2); OSMetaClassDefineReservedUnused(IOPlatformDevice, 3); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool IOPlatformDevice::compareName( OSString * name, OSString ** matched ) const { return ((IOPlatformExpert *)getProvider())-> compareNubName( this, name, matched ); } IOService * IOPlatformDevice::matchLocation( IOService * /* client */ ) { return this; } IOReturn IOPlatformDevice::getResources( void ) { return ((IOPlatformExpert *)getProvider())->getNubResources( this ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /********************************************************************* * IOPanicPlatform class * * If no legitimate IOPlatformDevice matches, this one does and panics * the kernel with a suitable message. *********************************************************************/ class IOPanicPlatform : IOPlatformExpert { OSDeclareDefaultStructors(IOPanicPlatform); public: bool start(IOService * provider) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndStructors(IOPanicPlatform, IOPlatformExpert); bool IOPanicPlatform::start(IOService * provider) { const char * platform_name = "(unknown platform name)"; if (provider) { platform_name = provider->getName(); } panic("Unable to find driver for this platform: \"%s\".", platform_name); return false; }