]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOPlatformExpert.cpp
xnu-7195.81.3.tar.gz
[apple/xnu.git] / iokit / Kernel / IOPlatformExpert.cpp
index d7087fbb250ecbf665090ba38f0a1570bda6cca2..407dd5b02e3e46e4ca8b0ea3d9972f9fd716f276 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include <IOKit/IOCPU.h>
+#include <IOKit/IOPlatformActions.h>
 #include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IOKitDebug.h>
 #include <IOKit/IOMapper.h>
 #include <IOKit/IOTimeStamp.h>
 #include <IOKit/IOUserClient.h>
 #include <IOKit/IOKitDiagnosticsUserClient.h>
+#include <IOKit/IOUserServer.h>
+
+#include "IOKitKernelInternal.h"
 
 #include <IOKit/system.h>
 #include <sys/csr.h>
 
 #include <libkern/c++/OSContainers.h>
+#include <libkern/c++/OSSharedPtr.h>
 #include <libkern/crypto/sha1.h>
 #include <libkern/OSAtomic.h>
 
+#if defined(__arm64__)
+#include <arm64/tlb.h>
+#endif
+
 extern "C" {
 #include <machine/machine_routines.h>
 #include <pexpert/pexpert.h>
 #include <uuid/uuid.h>
+#include <sys/sysctl.h>
 }
 
 #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.
+ *
+ * <rdar://problem/33831837> 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<OSString>& 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<const OSSymbol>& name, OSSharedPtr<OSData>& 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