X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..cc8bc92ae4a8e9f1a1ab61bf83d34ad8150b3405:/iokit/Kernel/IOPMrootDomain.cpp?ds=sidebyside diff --git a/iokit/Kernel/IOPMrootDomain.cpp b/iokit/Kernel/IOPMrootDomain.cpp index ddce48807..3b778df40 100644 --- a/iokit/Kernel/IOPMrootDomain.cpp +++ b/iokit/Kernel/IOPMrootDomain.cpp @@ -47,6 +47,7 @@ #include "IOPMPowerStateQueue.h" #include #include +#include "IOKitKernelInternal.h" #if HIBERNATION #include #endif @@ -56,6 +57,8 @@ #include #include #include +#include +#include #include #include "IOServicePrivate.h" // _IOServiceInterestNotifier @@ -63,6 +66,7 @@ __BEGIN_DECLS #include +#include __END_DECLS #if defined(__i386__) || defined(__x86_64__) @@ -81,14 +85,23 @@ __END_DECLS #define LOG(x...) \ do { kprintf(LOG_PREFIX x); } while (false) +#if DEVELOPMENT #define DLOG(x...) do { \ if (kIOLogPMRootDomain & gIOKitDebug) \ kprintf(LOG_PREFIX x); \ + else \ + os_log(OS_LOG_DEFAULT, LOG_PREFIX x); \ } while (false) +#else +#define DLOG(x...) do { \ + if (kIOLogPMRootDomain & gIOKitDebug) \ + kprintf(LOG_PREFIX x); \ +} while (false) +#endif #define DMSG(x...) do { \ if (kIOLogPMRootDomain & gIOKitDebug) { \ - kprintf(LOG_PREFIX x); IOLog(x); \ + kprintf(LOG_PREFIX x); \ } \ } while (false) @@ -169,10 +182,9 @@ IOReturn OSKextSystemSleepOrWake( UInt32 ); } extern "C" ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va); extern "C" addr64_t kvtophys(vm_offset_t va); -extern "C" int stack_snapshot_from_kernel(pid_t pid, void *buf, uint32_t size, uint32_t flags, unsigned *bytesTraced); static void idleSleepTimerExpired( thread_call_param_t, thread_call_param_t ); -static void notifySystemShutdown( IOService * root, unsigned long event ); +static void notifySystemShutdown( IOService * root, uint32_t messageType ); static void handleAggressivesFunction( thread_call_param_t, thread_call_param_t ); static void pmEventTimeStamp(uint64_t *recordTS); @@ -184,21 +196,15 @@ static const OSSymbol *sleepMessagePEFunction = NULL; #define kIOPMSystemCapabilitiesKey "System Capabilities" #define kIORequestWranglerIdleKey "IORequestIdle" -#define kDefaultWranglerIdlePeriod 25 // in milliseconds +#define kDefaultWranglerIdlePeriod 1000 // in milliseconds #define kIOSleepWakeDebugKey "Persistent-memory-note" +#define kIOEFIBootRomFailureKey "wake-failure" #define kRD_AllPowerSources (kIOPMSupportedOnAC \ | kIOPMSupportedOnBatt \ | kIOPMSupportedOnUPS) -enum -{ - // not idle around autowake time, secs - kAutoWakePreWindow = 45, - kAutoWakePostWindow = 15 -}; - #define kLocalEvalClamshellCommand (1 << 15) #define kIdleSleepRetryInterval (3 * 60) @@ -312,10 +318,19 @@ static UInt32 gWillShutdown = 0; static UInt32 gPagingOff = 0; static UInt32 gSleepWakeUUIDIsSet = false; static uint32_t gAggressivesState = 0; +static uint32_t gHaltTimeMaxLog; +static uint32_t gHaltTimeMaxPanic; +IOLock * gHaltLogLock; +static char * gHaltLog; +enum { kHaltLogSize = 2048 }; +static size_t gHaltLogPos; +static uint64_t gHaltStartTime; + uuid_string_t bootsessionuuid_string; static uint32_t gDarkWakeFlags = kDarkWakeFlagHIDTickleNone; +static uint32_t gNoIdleFlag = 0; static PMStatsStruct gPMStats; #if HIBERNATION @@ -329,6 +344,21 @@ struct timeval gIOLastWakeTime; static char gWakeReasonString[128]; static bool gWakeReasonSysctlRegistered = false; +static AbsoluteTime gIOLastWakeAbsTime; +static AbsoluteTime gIOLastSleepAbsTime; + +#if defined(__i386__) || defined(__x86_64__) +static bool gSpinDumpBufferFull = false; +#endif + +static unsigned int gPMHaltBusyCount; +static unsigned int gPMHaltIdleCount; +static int gPMHaltDepth; +static uint32_t gPMHaltMessageType; +static IOLock * gPMHaltLock = 0; +static OSArray * gPMHaltArray = 0; +static const OSSymbol * gPMHaltClientAcknowledgeKey = 0; +static bool gPMQuiesced; // Constants used as arguments to IOPMrootDomain::informCPUStateChange #define kCPUUnknownIndex 9999999 @@ -338,10 +368,10 @@ enum { kInformableCount = 2 }; -const OSSymbol *gIOPMStatsApplicationResponseTimedOut; -const OSSymbol *gIOPMStatsApplicationResponseCancel; -const OSSymbol *gIOPMStatsApplicationResponseSlow; -const OSSymbol *gIOPMStatsApplicationResponsePrompt; +const OSSymbol *gIOPMStatsResponseTimedOut; +const OSSymbol *gIOPMStatsResponseCancel; +const OSSymbol *gIOPMStatsResponseSlow; +const OSSymbol *gIOPMStatsResponsePrompt; const OSSymbol *gIOPMStatsDriverPSChangeSlow; #define kBadPMFeatureID 0 @@ -357,7 +387,7 @@ class PMSettingHandle : public OSObject private: PMSettingObject *pmso; - void free(void); + void free(void) APPLE_KEXT_OVERRIDE; }; /* @@ -381,7 +411,7 @@ private: uint32_t settingCount; bool disabled; - void free(void); + void free(void) APPLE_KEXT_OVERRIDE; public: static PMSettingObject *pmSettingObject( @@ -426,26 +456,28 @@ public: static PMTraceWorker *tracer( IOPMrootDomain * ); void tracePCIPowerChange(change_t, IOService *, uint32_t, uint32_t); void tracePoint(uint8_t phase); - void tracePoint(uint8_t phase, uint8_t data8); void traceDetail(uint32_t detail); - void traceLoginWindowPhase(uint8_t phase); + void traceComponentWakeProgress(uint32_t component, uint32_t data); int recordTopLevelPCIDevice(IOService *); void RTC_TRACE(void); - virtual bool serialize(OSSerialize *s) const; + virtual bool serialize(OSSerialize *s) const APPLE_KEXT_OVERRIDE; IOPMTracePointHandler tracePointHandler; void * tracePointTarget; uint64_t getPMStatusCode(); + uint8_t getTracePhase(); + uint32_t getTraceData(); private: IOPMrootDomain *owner; - IOLock *pciMappingLock; + IOLock *pmTraceWorkerLock; OSArray *pciDeviceBitMappings; uint8_t addedToRegistry; uint8_t tracePhase; - uint8_t loginWindowPhase; - uint8_t traceData8; uint32_t traceData32; + uint8_t loginWindowData; + uint8_t coreDisplayData; + uint8_t coreGraphicsData; }; /* @@ -525,7 +557,7 @@ public: static void main( void * arg, wait_result_t waitResult ); static void work( PMHaltWorker * me ); static void checkTimeout( PMHaltWorker * me, AbsoluteTime * now ); - virtual void free( void ); + virtual void free( void ) APPLE_KEXT_OVERRIDE; }; OSDefineMetaClassAndFinalStructors( PMHaltWorker, OSObject ) @@ -547,50 +579,115 @@ static void IOPMRootDomainWillShutdown(void) } } -extern "C" +extern "C" IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) { - IONotifier * registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) - { - return gRootDomain->registerInterest( gIOGeneralInterest, handler, self, ref ); - } + return gRootDomain->registerInterest( gIOGeneralInterest, handler, self, ref ); +} - IONotifier * registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) - { - return gRootDomain->registerInterest( gIOPriorityPowerStateInterest, handler, self, ref ); - } +extern "C" IONotifier * registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) +{ + return gRootDomain->registerInterest( gIOPriorityPowerStateInterest, handler, self, ref ); +} - IOReturn acknowledgeSleepWakeNotification(void * PMrefcon) - { - return gRootDomain->allowPowerChange ( (unsigned long)PMrefcon ); - } +extern "C" IOReturn acknowledgeSleepWakeNotification(void * PMrefcon) +{ + return gRootDomain->allowPowerChange ( (unsigned long)PMrefcon ); +} - IOReturn vetoSleepWakeNotification(void * PMrefcon) - { - return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon ); - } +extern "C" IOReturn vetoSleepWakeNotification(void * PMrefcon) +{ + return gRootDomain->cancelPowerChange ( (unsigned long)PMrefcon ); +} - IOReturn rootDomainRestart ( void ) - { - return gRootDomain->restartSystem(); +extern "C" IOReturn rootDomainRestart ( void ) +{ + return gRootDomain->restartSystem(); +} + +extern "C" IOReturn rootDomainShutdown ( void ) +{ + return gRootDomain->shutdownSystem(); +} + +static void halt_log_putc(char c) +{ + if (gHaltLogPos >= (kHaltLogSize - 1)) return; + gHaltLog[gHaltLogPos++] = c; +} + +extern "C" void +_doprnt_log(const char *fmt, + va_list *argp, + void (*putc)(char), + int radix); + +static int +halt_log(const char *fmt, ...) +{ + va_list listp; + + va_start(listp, fmt); + _doprnt_log(fmt, &listp, &halt_log_putc, 16); + va_end(listp); + + return (0); +} + +extern "C" void +halt_log_enter(const char * what, const void * pc, uint64_t time) +{ + uint64_t nano, millis; + + if (!gHaltLog) return; + absolutetime_to_nanoseconds(time, &nano); + millis = nano / 1000000ULL; + if (millis < 100) return; + + IOLockLock(gHaltLogLock); + if (pc) { + halt_log("%s: %qd ms @ 0x%lx, ", what, millis, VM_KERNEL_UNSLIDE(pc)); + OSKext::printKextsInBacktrace((vm_offset_t *) &pc, 1, &halt_log, + OSKext::kPrintKextsLock | OSKext::kPrintKextsUnslide | OSKext::kPrintKextsTerse); + } else { + halt_log("%s: %qd ms\n", what, millis, VM_KERNEL_UNSLIDE(pc)); } + IOLockUnlock(gHaltLogLock); +} + +extern uint32_t gFSState; + +extern "C" void IOSystemShutdownNotification(void) +{ + uint64_t startTime; - IOReturn rootDomainShutdown ( void ) + IOLockLock(gHaltLogLock); + if (!gHaltLog) { - return gRootDomain->shutdownSystem(); + gHaltLog = IONew(char, kHaltLogSize); + gHaltStartTime = mach_absolute_time(); + if (gHaltLog) halt_log_putc('\n'); } + IOLockUnlock(gHaltLogLock); - void IOSystemShutdownNotification(void) + startTime = mach_absolute_time(); + IOPMRootDomainWillShutdown(); + halt_log_enter("IOPMRootDomainWillShutdown", 0, mach_absolute_time() - startTime); +#if HIBERNATION + startTime = mach_absolute_time(); + IOHibernateSystemPostWake(true); + halt_log_enter("IOHibernateSystemPostWake", 0, mach_absolute_time() - startTime); +#endif + if (OSCompareAndSwap(0, 1, &gPagingOff)) { - IOPMRootDomainWillShutdown(); - if (OSCompareAndSwap(0, 1, &gPagingOff)) - { - gRootDomain->handlePlatformHaltRestart(kPEPagingOff); - } +#if !CONFIG_EMBEDDED + gRootDomain->handlePlatformHaltRestart(kPEPagingOff); +#endif } - - int sync_internal(void); } + +extern "C" int sync_internal(void); + /* A device is always in the highest power state which satisfies its driver, its policy-maker, and any power children it has, but within the constraint @@ -647,6 +744,94 @@ IOPMrootDomain * IOPMrootDomain::construct( void ) } //****************************************************************************** +// updateConsoleUsersCallout +// +//****************************************************************************** + +static void updateConsoleUsersCallout(thread_call_param_t p0, thread_call_param_t p1) +{ + IOPMrootDomain * rootDomain = (IOPMrootDomain *) p0; + rootDomain->updateConsoleUsers(); +} + +void IOPMrootDomain::updateConsoleUsers(void) +{ + IOService::updateConsoleUsers(NULL, kIOMessageSystemHasPoweredOn); + if (tasksSuspended) + { + tasksSuspended = FALSE; + tasks_system_suspend(tasksSuspended); + } +} + +//****************************************************************************** + +static void swdDebugSetupCallout( thread_call_param_t p0, thread_call_param_t p1 ) +{ + IOPMrootDomain * rootDomain = (IOPMrootDomain *) p0; + uint32_t notifyRef = (uint32_t)(uintptr_t) p1; + + rootDomain->swdDebugSetup(); + + if (p1) { + rootDomain->allowPowerChange(notifyRef); + } + DLOG("swdDebugSetupCallout finish\n"); +} + +void IOPMrootDomain::swdDebugSetup( ) +{ +#if HIBERNATION + static int32_t mem_only = -1; + if ((mem_only == -1) && + (PE_parse_boot_argn("swd_mem_only", &mem_only, sizeof(mem_only)) == false)) { + mem_only = 0; + } + + if ((mem_only == 1) || (gRootDomain->sleepWakeDebugIsWdogEnabled() == false)) { + return; + } + DLOG("swdDebugSetup state:%d\n", swd_DebugImageSetup); + if (swd_DebugImageSetup == FALSE) { + swd_DebugImageSetup = TRUE; + if (CAP_GAIN(kIOPMSystemCapabilityGraphics) || + (CAP_LOSS(kIOPMSystemCapabilityGraphics))) { + IOHibernateSystemPostWake(true); + } + IOOpenDebugDataFile(kSleepWakeStackBinFilename, SWD_BUF_SIZE); + } +#endif + + +} + +static void swdDebugTeardownCallout( thread_call_param_t p0, thread_call_param_t p1 ) +{ + IOPMrootDomain * rootDomain = (IOPMrootDomain *) p0; + uint32_t notifyRef = (uint32_t)(uintptr_t) p1; + + rootDomain->swdDebugTeardown(); + if (p1) { + rootDomain->allowPowerChange(notifyRef); + } + DLOG("swdDebugTeardownCallout finish\n"); +} + +void IOPMrootDomain::swdDebugTeardown( ) +{ + +#if HIBERNATION + DLOG("swdDebugTeardown state:%d\n", swd_DebugImageSetup); + if (swd_DebugImageSetup == TRUE) { + swd_DebugImageSetup = FALSE; + IOCloseDebugDataFile(); + } +#endif + + +} +//****************************************************************************** + static void disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 ) { @@ -659,11 +844,16 @@ static void disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 ) if (ON_STATE == powerState) { sync_internal(); + swdDebugSetupCallout(p0, NULL); } #if HIBERNATION else { - IOHibernateSystemPostWake(); + swdDebugTeardownCallout(p0, NULL); + IOHibernateSystemPostWake(false); + + if (gRootDomain) + gRootDomain->sleepWakeDebugSaveSpinDumpFile(); } #endif @@ -672,31 +862,18 @@ static void disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 ) } //****************************************************************************** - -static void hib_debugSetup_callout( thread_call_param_t p0, thread_call_param_t p1 ) -{ - IOService * rootDomain = (IOService *) p0; - uint32_t notifyRef = (uint32_t)(uintptr_t) p1; - -#if HIBERNATION - IOHibernateOpenForDebugData(); -#endif - - rootDomain->allowPowerChange(notifyRef); - DLOG("hib_debugSetup_callout finish\n"); -} -//****************************************************************************** - -static UInt32 computeDeltaTimeMS( const AbsoluteTime * startTime ) +static UInt32 computeDeltaTimeMS( const AbsoluteTime * startTime, AbsoluteTime * elapsedTime ) { AbsoluteTime endTime; UInt64 nano = 0; clock_get_uptime(&endTime); - if (CMP_ABSOLUTETIME(&endTime, startTime) > 0) + if (CMP_ABSOLUTETIME(&endTime, startTime) <= 0) *elapsedTime = 0; + else { SUB_ABSOLUTETIME(&endTime, startTime); absolutetime_to_nanoseconds(endTime, &nano); + *elapsedTime = endTime; } return (UInt32)(nano / 1000000ULL); @@ -713,12 +890,12 @@ sysctl_sleepwaketime SYSCTL_HANDLER_ARGS if (p == kernproc) { return sysctl_io_opaque(req, swt, sizeof(*swt), NULL); } else if(proc_is64bit(p)) { - struct user64_timeval t; + struct user64_timeval t = {}; t.tv_sec = swt->tv_sec; t.tv_usec = swt->tv_usec; return sysctl_io_opaque(req, &t, sizeof(t), NULL); } else { - struct user32_timeval t; + struct user32_timeval t = {}; t.tv_sec = swt->tv_sec; t.tv_usec = swt->tv_usec; return sysctl_io_opaque(req, &t, sizeof(t), NULL); @@ -733,6 +910,8 @@ static SYSCTL_PROC(_kern, OID_AUTO, waketime, CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, &gIOLastWakeTime, 0, sysctl_sleepwaketime, "S,timeval", ""); +SYSCTL_QUAD(_kern, OID_AUTO, wake_abs_time, CTLFLAG_RD|CTLFLAG_LOCKED, &gIOLastWakeAbsTime, ""); +SYSCTL_QUAD(_kern, OID_AUTO, sleep_abs_time, CTLFLAG_RD|CTLFLAG_LOCKED, &gIOLastSleepAbsTime, ""); static int sysctl_willshutdown @@ -753,6 +932,10 @@ static SYSCTL_PROC(_kern, OID_AUTO, willshutdown, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, 0, 0, sysctl_willshutdown, "I", ""); +extern struct sysctl_oid sysctl__kern_iokittest; +extern struct sysctl_oid sysctl__debug_iokit; + +#if !CONFIG_EMBEDDED static int sysctl_progressmeterenable @@ -790,6 +973,39 @@ static SYSCTL_PROC(_kern, OID_AUTO, progressmeter, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, 0, 0, sysctl_progressmeter, "I", ""); +#endif /* !CONFIG_EMBEDDED */ + + + +static int +sysctl_consoleoptions +(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) +{ + int error, changed; + uint32_t new_value; + + error = sysctl_io_number(req, vc_user_options.options, sizeof(uint32_t), &new_value, &changed); + + if (changed) vc_user_options.options = new_value; + + return (error); +} + +static SYSCTL_PROC(_kern, OID_AUTO, consoleoptions, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, + 0, 0, sysctl_consoleoptions, "I", ""); + + +static int +sysctl_progressoptions SYSCTL_HANDLER_ARGS +{ + return sysctl_io_opaque(req, &vc_user_options, sizeof(vc_user_options), NULL); +} + +static SYSCTL_PROC(_kern, OID_AUTO, progressoptions, + CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, + NULL, 0, sysctl_progressoptions, "S,vc_progress_user_options", ""); + static int sysctl_wakereason SYSCTL_HANDLER_ARGS @@ -807,7 +1023,33 @@ SYSCTL_PROC(_kern, OID_AUTO, wakereason, CTLTYPE_STRING| CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, NULL, 0, sysctl_wakereason, "A", "wakereason"); +static int +sysctl_targettype SYSCTL_HANDLER_ARGS +{ + IOService * root; + OSObject * obj; + OSData * data; + char tt[32]; + + tt[0] = '\0'; + root = IOService::getServiceRoot(); + if (root && (obj = root->copyProperty(gIODTTargetTypeKey))) + { + if ((data = OSDynamicCast(OSData, obj))) + { + strlcpy(tt, (const char *) data->getBytesNoCopy(), sizeof(tt)); + } + obj->release(); + } + return sysctl_io_string(req, tt, 0, 0, NULL); +} + +SYSCTL_PROC(_hw, OID_AUTO, targettype, + CTLTYPE_STRING| CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, + NULL, 0, sysctl_targettype, "A", "targettype"); + static SYSCTL_INT(_debug, OID_AUTO, darkwake, CTLFLAG_RW, &gDarkWakeFlags, 0, ""); +static SYSCTL_INT(_debug, OID_AUTO, noidle, CTLFLAG_RW, &gNoIdleFlag, 0, ""); static const OSSymbol * gIOPMSettingAutoWakeCalendarKey; static const OSSymbol * gIOPMSettingAutoWakeSecondsKey; @@ -846,10 +1088,10 @@ bool IOPMrootDomain::start( IOService * nub ) gIOPMUserTriggeredFullWakeKey = OSSymbol::withCStringNoCopy(kIOPMUserTriggeredFullWakeKey); gIOPMUserIsActiveKey = OSSymbol::withCStringNoCopy(kIOPMUserIsActiveKey); - gIOPMStatsApplicationResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut); - gIOPMStatsApplicationResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel); - gIOPMStatsApplicationResponseSlow = OSSymbol::withCString(kIOPMStatsResponseSlow); - gIOPMStatsApplicationResponsePrompt = OSSymbol::withCString(kIOPMStatsResponsePrompt); + gIOPMStatsResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut); + gIOPMStatsResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel); + gIOPMStatsResponseSlow = OSSymbol::withCString(kIOPMStatsResponseSlow); + gIOPMStatsResponsePrompt = OSSymbol::withCString(kIOPMStatsResponsePrompt); gIOPMStatsDriverPSChangeSlow = OSSymbol::withCString(kIOPMStatsDriverPSChangeSlow); sleepSupportedPEFunction = OSSymbol::withCString("IOPMSetSleepSupported"); @@ -877,6 +1119,9 @@ bool IOPMrootDomain::start( IOService * nub ) }; PE_parse_boot_argn("darkwake", &gDarkWakeFlags, sizeof(gDarkWakeFlags)); + PE_parse_boot_argn("noidle", &gNoIdleFlag, sizeof(gNoIdleFlag)); + PE_parse_boot_argn("haltmspanic", &gHaltTimeMaxPanic, sizeof(gHaltTimeMaxPanic)); + PE_parse_boot_argn("haltmslog", &gHaltTimeMaxLog, sizeof(gHaltTimeMaxLog)); queue_init(&aggressivesQueue); aggressivesThreadCall = thread_call_allocate(handleAggressivesFunction, this); @@ -886,6 +1131,7 @@ bool IOPMrootDomain::start( IOService * nub ) featuresDictLock = IOLockAlloc(); settingsCtrlLock = IOLockAlloc(); wakeEventLock = IOLockAlloc(); + gHaltLogLock = IOLockAlloc(); setPMRootDomain(this); extraSleepTimer = thread_call_allocate( @@ -895,8 +1141,14 @@ bool IOPMrootDomain::start( IOService * nub ) diskSyncCalloutEntry = thread_call_allocate( &disk_sync_callout, (thread_call_param_t) this); - hibDebugSetupEntry = thread_call_allocate( - &hib_debugSetup_callout, + swdDebugSetupEntry = thread_call_allocate( + &swdDebugSetupCallout, + (thread_call_param_t) this); + swdDebugTearDownEntry = thread_call_allocate( + &swdDebugTeardownCallout, + (thread_call_param_t) this); + updateConsoleUsersEntry = thread_call_allocate( + &updateConsoleUsersCallout, (thread_call_param_t) this); #if DARK_TO_FULL_EVALUATE_CLAMSHELL @@ -916,6 +1168,7 @@ bool IOPMrootDomain::start( IOService * nub ) userDisabledAllSleep = false; systemBooting = true; + idleSleepEnabled = false; sleepSlider = 0; idleSleepTimerPending = false; wrangler = NULL; @@ -952,6 +1205,7 @@ bool IOPMrootDomain::start( IOService * nub ) _statsResponseTypeKey = OSSymbol::withCString(kIOPMStatsApplicationResponseTypeKey); _statsMessageTypeKey = OSSymbol::withCString(kIOPMStatsMessageTypeKey); _statsPowerCapsKey = OSSymbol::withCString(kIOPMStatsPowerCapabilityKey); + assertOnWakeSecs = -1; // Invalid value to prevent updates pmStatsLock = IOLockAlloc(); idxPMCPUClamshell = kCPUUnknownIndex; @@ -980,16 +1234,14 @@ bool IOPMrootDomain::start( IOService * nub ) preventSystemSleepList = OSSet::withCapacity(2); PMinit(); // creates gIOPMWorkLoop + gIOPMWorkLoop = getIOPMWorkloop(); // Create IOPMPowerStateQueue used to queue external power // events, and to handle those events on the PM work loop. pmPowerStateQueue = IOPMPowerStateQueue::PMPowerStateQueue( this, OSMemberFunctionCast(IOEventSource::Action, this, &IOPMrootDomain::dispatchPowerEvent)); - getPMworkloop()->addEventSource(pmPowerStateQueue); -#ifdef CHECK_THREAD_CONTEXT - gIOPMWorkLoop = getPMworkloop(); -#endif + gIOPMWorkLoop->addEventSource(pmPowerStateQueue); // create our power parent patriarch = new IORootParent; @@ -1047,7 +1299,10 @@ bool IOPMrootDomain::start( IOService * nub ) // IOBacklightDisplay can take a long time to load at boot, or it may // not load at all if you're booting with clamshell closed. We publish // 'DisplayDims' here redundantly to get it published early and at all. - psIterator = getMatchingServices( serviceMatching("IOPMPowerSource") ); + OSDictionary * matching; + matching = serviceMatching("IOPMPowerSource"); + psIterator = getMatchingServices( matching ); + if (matching) matching->release(); if( psIterator && psIterator->getNextObject() ) { // There's at least one battery on the system, so we publish @@ -1061,9 +1316,17 @@ bool IOPMrootDomain::start( IOService * nub ) sysctl_register_oid(&sysctl__kern_sleeptime); sysctl_register_oid(&sysctl__kern_waketime); sysctl_register_oid(&sysctl__kern_willshutdown); + sysctl_register_oid(&sysctl__kern_iokittest); + sysctl_register_oid(&sysctl__debug_iokit); + sysctl_register_oid(&sysctl__hw_targettype); + +#if !CONFIG_EMBEDDED sysctl_register_oid(&sysctl__kern_progressmeterenable); sysctl_register_oid(&sysctl__kern_progressmeter); sysctl_register_oid(&sysctl__kern_wakereason); +#endif /* !CONFIG_EMBEDDED */ + sysctl_register_oid(&sysctl__kern_consoleoptions); + sysctl_register_oid(&sysctl__kern_progressoptions); #if HIBERNATION IOHibernateSystemInit(this); @@ -1099,7 +1362,9 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) const OSSymbol *idle_seconds_string = OSSymbol::withCString("System Idle Seconds"); const OSSymbol *sleepdisabled_string = OSSymbol::withCString("SleepDisabled"); const OSSymbol *ondeck_sleepwake_uuid_string = OSSymbol::withCString(kIOPMSleepWakeUUIDKey); - const OSSymbol *loginwindow_tracepoint_string = OSSymbol::withCString(kIOPMLoginWindowSecurityDebugKey); + const OSSymbol *loginwindow_progress_string = OSSymbol::withCString(kIOPMLoginWindowProgressKey); + const OSSymbol *coredisplay_progress_string = OSSymbol::withCString(kIOPMCoreDisplayProgressKey); + const OSSymbol *coregraphics_progress_string = OSSymbol::withCString(kIOPMCoreGraphicsProgressKey); #if HIBERNATION const OSSymbol *hibernatemode_string = OSSymbol::withCString(kIOHibernateModeKey); const OSSymbol *hibernatefile_string = OSSymbol::withCString(kIOHibernateFileKey); @@ -1180,10 +1445,29 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) obj->retain(); pmPowerStateQueue->submitPowerEvent(kPowerEventQueueSleepWakeUUID, (void *)obj); } - else if (key->isEqualTo(loginwindow_tracepoint_string)) + else if (key->isEqualTo(loginwindow_progress_string)) { - if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) - pmTracer->traceLoginWindowPhase(n->unsigned8BitValue()); + if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { + uint32_t data = n->unsigned32BitValue(); + pmTracer->traceComponentWakeProgress(kIOPMLoginWindowProgress, data); + kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMLoginWindowProgress, data); + } + } + else if (key->isEqualTo(coredisplay_progress_string)) + { + if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { + uint32_t data = n->unsigned32BitValue(); + pmTracer->traceComponentWakeProgress(kIOPMCoreDisplayProgress, data); + kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMCoreDisplayProgress, data); + } + } + else if (key->isEqualTo(coregraphics_progress_string)) + { + if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { + uint32_t data = n->unsigned32BitValue(); + pmTracer->traceComponentWakeProgress(kIOPMCoreGraphicsProgress, data); + kdebugTrace(kPMLogComponentWakeProgress, 0, kIOPMCoreGraphicsProgress, data); + } } else if (key->isEqualTo(kIOPMDeepSleepEnabledKey) || key->isEqualTo(kIOPMDestroyFVKeyOnStandbyKey) || @@ -1194,6 +1478,7 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) setProperty(key, b); } else if (key->isEqualTo(kIOPMDeepSleepDelayKey) || + key->isEqualTo(kIOPMDeepSleepTimerKey) || key->isEqualTo(kIOPMAutoPowerOffDelayKey) || key->isEqualTo(kIOPMAutoPowerOffTimerKey)) { @@ -1212,25 +1497,6 @@ IOReturn IOPMrootDomain::setProperties( OSObject * props_obj ) // Relay our allowed PM settings onto our registered PM clients else if ((allowedPMSettings->getNextIndexOfObject(key, 0) != (unsigned int) -1)) { - if ((gIOPMSettingAutoWakeSecondsKey == key) && ((n = OSDynamicCast(OSNumber, obj)))) - { - UInt32 rsecs = n->unsigned32BitValue(); - if (!rsecs) - autoWakeStart = autoWakeEnd = 0; - else - { - AbsoluteTime deadline; - clock_interval_to_deadline(rsecs + kAutoWakePostWindow, kSecondScale, &deadline); - autoWakeEnd = AbsoluteTime_to_scalar(&deadline); - if (rsecs > kAutoWakePreWindow) - rsecs -= kAutoWakePreWindow; - else - rsecs = 0; - clock_interval_to_deadline(rsecs, kSecondScale, &deadline); - autoWakeStart = AbsoluteTime_to_scalar(&deadline); - } - } - return_value = setPMSetting(key, obj); if (kIOReturnSuccess != return_value) break; @@ -1281,7 +1547,9 @@ exit: if(idle_seconds_string) idle_seconds_string->release(); if(sleepdisabled_string) sleepdisabled_string->release(); if(ondeck_sleepwake_uuid_string) ondeck_sleepwake_uuid_string->release(); - if(loginwindow_tracepoint_string) loginwindow_tracepoint_string->release(); + if(loginwindow_progress_string) loginwindow_progress_string->release(); + if(coredisplay_progress_string) coredisplay_progress_string->release(); + if(coregraphics_progress_string) coregraphics_progress_string->release(); #if HIBERNATION if(hibernatemode_string) hibernatemode_string->release(); if(hibernatefile_string) hibernatefile_string->release(); @@ -1753,7 +2021,7 @@ void IOPMrootDomain::broadcastAggressives( if (!connect || !connect->getReadyFlag()) continue; - if ((service = (IOService *) connect->copyChildEntry(gIOPowerPlane))) + if ((service = OSDynamicCast(IOService, connect->copyChildEntry(gIOPowerPlane)))) { if (service->assertPMDriverCall(&callEntry)) { @@ -1793,6 +2061,10 @@ void IOPMrootDomain::startIdleSleepTimer( uint32_t inSeconds ) AbsoluteTime deadline; ASSERT_GATED(); + if (gNoIdleFlag) { + DLOG("idle timer not set (noidle=%d)\n", gNoIdleFlag); + return; + } if (inSeconds) { clock_interval_to_deadline(inSeconds, kSecondScale, &deadline); @@ -1819,6 +2091,18 @@ void IOPMrootDomain::cancelIdleSleepTimer( void ) DLOG("idle timer cancelled\n"); thread_call_cancel(extraSleepTimer); idleSleepTimerPending = false; + + if (!assertOnWakeSecs && gIOLastWakeAbsTime) { + AbsoluteTime now; + clock_usec_t microsecs; + clock_get_uptime(&now); + SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); + absolutetime_to_microtime(now, &assertOnWakeSecs, µsecs); + if (assertOnWakeReport) { + HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); + DLOG("Updated assertOnWake %lu\n", (unsigned long)assertOnWakeSecs); + } + } } } @@ -1842,9 +2126,9 @@ static void idleSleepTimerExpired( void IOPMrootDomain::handleSleepTimerExpiration( void ) { - if (!getPMworkloop()->inGate()) + if (!gIOPMWorkLoop->inGate()) { - getPMworkloop()->runAction( + gIOPMWorkLoop->runAction( OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::handleSleepTimerExpiration), this); @@ -1859,13 +2143,6 @@ void IOPMrootDomain::handleSleepTimerExpiration( void ) idleSleepTimerPending = false; clock_get_uptime(&time); - if ((AbsoluteTime_to_scalar(&time) > autoWakeStart) && - (AbsoluteTime_to_scalar(&time) < autoWakeEnd)) - { - thread_call_enter_delayed(extraSleepTimer, *((AbsoluteTime *) &autoWakeEnd)); - return; - } - setQuickSpinDownTimeout(); adjustPowerState(true); } @@ -1886,7 +2163,7 @@ uint32_t IOPMrootDomain::getTimeToIdleSleep( void ) uint32_t minutesSinceUserInactive = 0; uint32_t sleepDelay = 0; - if (sleepSlider == 0) + if (!idleSleepEnabled) return 0xffffffff; if (userActivityTime) @@ -2007,6 +2284,10 @@ IOReturn IOPMrootDomain::privateSleepSystem( uint32_t sleepReason ) void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) { +#if !__i386__ && !__x86_64__ + uint64_t timeSinceReset = 0; +#endif + uint64_t now; ASSERT_GATED(); DLOG("PowerChangeDone: %u->%u\n", (uint32_t) previousPowerState, (uint32_t) getPowerState()); @@ -2024,13 +2305,28 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) clock_sec_t secs; clock_usec_t microsecs; - clock_get_calendar_microtime(&secs, µsecs); + clock_get_calendar_absolute_and_microtime(&secs, µsecs, &now); logtime(secs); gIOLastSleepTime.tv_sec = secs; gIOLastSleepTime.tv_usec = microsecs; gIOLastWakeTime.tv_sec = 0; gIOLastWakeTime.tv_usec = 0; + gIOLastSleepAbsTime = now; + if (wake2DarkwakeDelay && sleepDelaysReport) { + clock_usec_t microsecs; + clock_sec_t wake2DarkwakeSecs, darkwake2SleepSecs; + // Update 'wake2DarkwakeDelay' histogram if this is a fullwake->sleep transition + + SUB_ABSOLUTETIME(&now, &ts_sleepStart); + absolutetime_to_microtime(now, &darkwake2SleepSecs, µsecs); + absolutetime_to_microtime(wake2DarkwakeDelay, &wake2DarkwakeSecs, µsecs); + HISTREPORT_TALLYVALUE(sleepDelaysReport, + (int64_t)(wake2DarkwakeSecs+darkwake2SleepSecs)); + + DLOG("Updated sleepDelaysReport %lu %lu\n", (unsigned long)wake2DarkwakeSecs, (unsigned long)darkwake2SleepSecs); + wake2DarkwakeDelay = 0; + } #if HIBERNATION LOG("System %sSleep\n", gIOHibernateState ? "Safe" : ""); @@ -2040,14 +2336,39 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) #else LOG("System Sleep\n"); #endif - + if (thermalWarningState) { + const OSSymbol *event = OSSymbol::withCString(kIOPMThermalLevelWarningKey); + if (event) { + systemPowerEventOccurred(event, kIOPMThermalLevelUnknown); + event->release(); + } + } + assertOnWakeSecs = 0; ((IOService *)this)->stop_watchdog_timer(); //14456299 + lowBatteryCondition = false; + +#if DEVELOPMENT || DEBUG + extern int g_should_log_clock_adjustments; + if (g_should_log_clock_adjustments) { + clock_sec_t secs = 0; + clock_usec_t microsecs = 0; + uint64_t now_b = mach_absolute_time(); + + PEGetUTCTimeOfDay(&secs, µsecs); + + uint64_t now_a = mach_absolute_time(); + os_log(OS_LOG_DEFAULT, "%s PMU before going to sleep %lu s %d u %llu abs_b_PEG %llu abs_a_PEG \n", + __func__, (unsigned long)secs, microsecs, now_b, now_a); + } +#endif + getPlatform()->sleepKernel(); // The CPU(s) are off at this point, // Code will resume execution here upon wake. - clock_get_uptime(&systemWakeTime); + clock_get_uptime(&gIOLastWakeAbsTime); + IOLog("gIOLastWakeAbsTime: %lld\n", gIOLastWakeAbsTime); _highestCapability = 0; ((IOService *)this)->start_watchdog_timer(); //14456299 @@ -2058,27 +2379,30 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) // sleep transition complete gSleepOrShutdownPending = 0; - // trip the reset of the calendar clock - clock_wakeup_calendar(); + // trip the reset of the calendar clock + { + clock_sec_t wakeSecs; + clock_usec_t wakeMicrosecs; + + clock_wakeup_calendar(); + + clock_get_calendar_microtime(&wakeSecs, &wakeMicrosecs); + gIOLastWakeTime.tv_sec = wakeSecs; + gIOLastWakeTime.tv_usec = wakeMicrosecs; + } #if HIBERNATION LOG("System %sWake\n", gIOHibernateState ? "SafeSleep " : ""); #endif - // log system wake - PMDebug(kPMLogSystemWake, 0, 0); - lowBatteryCondition = false; lastSleepReason = 0; _lastDebugWakeSeconds = _debugWakeSeconds; _debugWakeSeconds = 0; _scheduledAlarms = 0; -#ifndef __LP64__ - systemWake(); -#endif - #if defined(__i386__) || defined(__x86_64__) + kdebugTrace(kPMLogSystemWake, 0, 0, 0); wranglerTickled = false; graphicsSuppressed = false; darkWakePostTickle = false; @@ -2111,9 +2435,9 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) } else if ((gDarkWakeFlags & kDarkWakeFlagHIDTickleMask) != 0) { +#if HIBERNATION OSNumber * hibOptions = OSDynamicCast( OSNumber, getProperty(kIOHibernateOptionsKey)); - if (hibernateAborted || ((hibOptions && !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake)))) { @@ -2124,6 +2448,7 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) hibOptions ? hibOptions->unsigned32BitValue() : 0); } else +#endif if (wakeType && ( wakeType->isEqualTo(kIOPMRootDomainWakeTypeUser) || wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm))) @@ -2158,9 +2483,11 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) { darkWakeMaintenance = true; darkWakeSleepService = true; +#if HIBERNATION if (kIOHibernateStateWakingFromHibernate == gIOHibernateState) { sleepToStandby = true; } +#endif } else if (wakeType && @@ -2206,6 +2533,10 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) fullWakeReason = kFullWakeReasonLocalUser; reportUserInput(); } + else if (displayPowerOnRequested && checkSystemCanSustainFullWake()) + { + handleDisplayPowerOn(); + } else if (!darkWakeMaintenance) { // Early/late tickle for non-maintenance wake. @@ -2218,6 +2549,9 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) } } #else /* !__i386__ && !__x86_64__ */ + timeSinceReset = ml_get_time_since_reset(); + + kdebugTrace(kPMLogSystemWake, 0, timeSinceReset >> 32, timeSinceReset); // stay awake for at least 30 seconds wranglerTickled = true; fullWakeReason = kFullWakeReasonLocalUser; @@ -2225,8 +2559,24 @@ void IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) #endif sleepCnt++; + thread_call_enter(updateConsoleUsersEntry); + changePowerStateToPriv(ON_STATE); } break; +#if !__i386__ && !__x86_64__ + case ON_STATE: { + if (previousPowerState != ON_STATE) + { + DLOG("Force re-evaluating aggressiveness\n"); + /* Force re-evaluate the aggressiveness values to set appropriate idle sleep timer */ + pmPowerStateQueue->submitPowerEvent( + kPowerEventPolicyStimulus, + (void *) kStimulusNoIdleSleepPreventers ); + } + break; + } + +#endif } } @@ -2248,6 +2598,7 @@ IOReturn IOPMrootDomain::requestPowerDomainState ( return super::requestPowerDomainState(0, childConnection, specification); } + //****************************************************************************** // updatePreventIdleSleepList // @@ -2303,10 +2654,13 @@ bool IOPMrootDomain::updatePreventIdleSleepList( changePowerStateTo(SLEEP_STATE); evaluatePolicy( kStimulusNoIdleSleepPreventers ); } + messageClient(kIOPMMessageIdleSleepPreventers, systemCapabilityNotifier, + &newCount, sizeof(newCount)); #if defined(__i386__) || defined(__x86_64__) if (addNotRemove && (service == wrangler) && !checkSystemCanSustainFullWake()) { + DLOG("Cannot cancel idle sleep\n"); return false; // do not idle-cancel } #endif @@ -2314,6 +2668,15 @@ bool IOPMrootDomain::updatePreventIdleSleepList( return true; } +//****************************************************************************** +// startSpinDump +//****************************************************************************** + +void IOPMrootDomain::startSpinDump(uint32_t spindumpKind) +{ + messageClients(kIOPMMessageLaunchBootSpinDump, (void *)(uintptr_t)spindumpKind); +} + //****************************************************************************** // preventSystemSleepListUpdate // @@ -2323,7 +2686,7 @@ bool IOPMrootDomain::updatePreventIdleSleepList( void IOPMrootDomain::updatePreventSystemSleepList( IOService * service, bool addNotRemove ) { - unsigned int oldCount; + unsigned int oldCount, newCount; ASSERT_GATED(); if (this == service) @@ -2335,6 +2698,17 @@ void IOPMrootDomain::updatePreventSystemSleepList( preventSystemSleepList->setObject(service); DLOG("prevent system sleep list: %s+ (%u)\n", service->getName(), preventSystemSleepList->getCount()); + if (!assertOnWakeSecs && gIOLastWakeAbsTime) { + AbsoluteTime now; + clock_usec_t microsecs; + clock_get_uptime(&now); + SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); + absolutetime_to_microtime(now, &assertOnWakeSecs, µsecs); + if (assertOnWakeReport) { + HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); + DLOG("Updated assertOnWake %lu\n", (unsigned long)assertOnWakeSecs); + } + } } else if (preventSystemSleepList->member(service)) { @@ -2349,43 +2723,101 @@ void IOPMrootDomain::updatePreventSystemSleepList( evaluatePolicy( kStimulusDarkWakeEvaluate ); } } + newCount = preventSystemSleepList->getCount(); + messageClient(kIOPMMessageSystemSleepPreventers, systemCapabilityNotifier, + &newCount, sizeof(newCount)); } -//****************************************************************************** -// tellChangeDown -// -// Override the superclass implementation to send a different message type. -//****************************************************************************** - -bool IOPMrootDomain::tellChangeDown( unsigned long stateNum ) +void IOPMrootDomain::copySleepPreventersList(OSArray **idleSleepList, OSArray **systemSleepList) { - DLOG("tellChangeDown %u->%u\n", - (uint32_t) getPowerState(), (uint32_t) stateNum); - if (SLEEP_STATE == stateNum) + OSCollectionIterator *iterator = NULL; + OSObject *object = NULL; + OSArray *array = NULL; + + if (!gIOPMWorkLoop->inGate()) { - // Legacy apps were already told in the full->dark transition - if (!ignoreTellChangeDown) - tracePoint( kIOPMTracePointSleepApplications ); + gIOPMWorkLoop->runAction( + OSMemberFunctionCast(IOWorkLoop::Action, this, + &IOPMrootDomain::IOPMrootDomain::copySleepPreventersList), + this, (void *)idleSleepList, (void *)systemSleepList); + return; + } + + if (idleSleepList && preventIdleSleepList && (preventIdleSleepList->getCount() != 0)) + { + iterator = OSCollectionIterator::withCollection(preventIdleSleepList); + array = OSArray::withCapacity(5); + + while ((object = iterator->getNextObject())) + { + IOService *service = OSDynamicCast(IOService, object); + if (object) + { + array->setObject(OSSymbol::withCString(service->getName())); + } + } + + iterator->release(); + *idleSleepList = array; + } + + if (systemSleepList && preventSystemSleepList && (preventSystemSleepList->getCount() != 0)) + { + iterator = OSCollectionIterator::withCollection(preventSystemSleepList); + array = OSArray::withCapacity(5); + + while ((object = iterator->getNextObject())) + { + IOService *service = OSDynamicCast(IOService, object); + if (object) + { + array->setObject(OSSymbol::withCString(service->getName())); + } + } + + iterator->release(); + *systemSleepList = array; + } +} + +//****************************************************************************** +// tellChangeDown +// +// Override the superclass implementation to send a different message type. +//****************************************************************************** + +bool IOPMrootDomain::tellChangeDown( unsigned long stateNum ) +{ + DLOG("tellChangeDown %u->%u\n", + (uint32_t) getPowerState(), (uint32_t) stateNum); + + if (SLEEP_STATE == stateNum) + { + // Legacy apps were already told in the full->dark transition + if (!ignoreTellChangeDown) + tracePoint( kIOPMTracePointSleepApplications ); else tracePoint( kIOPMTracePointSleepPriorityClients ); } - if ((SLEEP_STATE == stateNum) && !ignoreTellChangeDown) - { + if (!ignoreTellChangeDown) { userActivityAtSleep = userActivityCount; - hibernateAborted = false; DLOG("tellChangeDown::userActivityAtSleep %d\n", userActivityAtSleep); - // Direct callout into OSKext so it can disable kext unloads - // during sleep/wake to prevent deadlocks. - OSKextSystemSleepOrWake( kIOMessageSystemWillSleep ); + if (SLEEP_STATE == stateNum) { + hibernateAborted = false; + + // Direct callout into OSKext so it can disable kext unloads + // during sleep/wake to prevent deadlocks. + OSKextSystemSleepOrWake( kIOMessageSystemWillSleep ); - IOService::updateConsoleUsers(NULL, kIOMessageSystemWillSleep); + IOService::updateConsoleUsers(NULL, kIOMessageSystemWillSleep); - // Two change downs are sent by IOServicePM. Ignore the 2nd. - // But tellClientsWithResponse() must be called for both. - ignoreTellChangeDown = true; + // Two change downs are sent by IOServicePM. Ignore the 2nd. + // But tellClientsWithResponse() must be called for both. + ignoreTellChangeDown = true; + } } return super::tellClientsWithResponse( kIOMessageSystemWillSleep ); @@ -2467,9 +2899,12 @@ void IOPMrootDomain::askChangeDownDone( void IOPMrootDomain::systemDidNotSleep( void ) { + // reset console lock state + thread_call_enter(updateConsoleUsersEntry); + if (!wrangler) { - if (idleSeconds) + if (idleSleepEnabled) { // stay awake for at least idleSeconds startIdleSleepTimer(idleSeconds); @@ -2477,7 +2912,7 @@ void IOPMrootDomain::systemDidNotSleep( void ) } else { - if (sleepSlider && !userIsActive) + if (idleSleepEnabled && !userIsActive) { // Manually start the idle sleep timer besides waiting for // the user to become inactive. @@ -2487,6 +2922,30 @@ void IOPMrootDomain::systemDidNotSleep( void ) preventTransitionToUserActive(false); IOService::setAdvisoryTickleEnable( true ); + + // After idle revert and cancel, send a did-change message to powerd + // to balance the previous will-change message. Kernel clients do not + // need this since sleep cannot be canceled once they are notified. + + if (toldPowerdCapWillChange && systemCapabilityNotifier && + (_pendingCapability != _currentCapability) && + ((_systemMessageClientMask & kSystemMessageClientPowerd) != 0)) + { + // Differs from a real capability gain change where notifyRef != 0, + // but it is zero here since no response is expected. + + IOPMSystemCapabilityChangeParameters params; + + bzero(¶ms, sizeof(params)); + params.fromCapabilities = _pendingCapability; + params.toCapabilities = _currentCapability; + params.changeFlags = kIOPMSystemCapabilityDidChange; + + DLOG("MESG cap %x->%x did change\n", + params.fromCapabilities, params.toCapabilities); + messageClient(kIOMessageSystemCapabilityChange, systemCapabilityNotifier, + ¶ms, sizeof(params)); + } } //****************************************************************************** @@ -2524,7 +2983,6 @@ void IOPMrootDomain::tellNoChangeDown( unsigned long stateNum ) void IOPMrootDomain::tellChangeUp( unsigned long stateNum ) { - DLOG("tellChangeUp %u->%u\n", (uint32_t) getPowerState(), (uint32_t) stateNum); @@ -2549,12 +3007,31 @@ void IOPMrootDomain::tellChangeUp( unsigned long stateNum ) tellClients( kIOMessageSystemWillPowerOn ); } - tracePoint( kIOPMTracePointWakeApplications ); tellClients( kIOMessageSystemHasPoweredOn ); } } +#define CAP_WILL_CHANGE_TO_OFF(params, flag) \ + (((params)->changeFlags & kIOPMSystemCapabilityWillChange) && \ + ((params)->fromCapabilities & (flag)) && \ + (((params)->toCapabilities & (flag)) == 0)) + +#define CAP_DID_CHANGE_TO_ON(params, flag) \ + (((params)->changeFlags & kIOPMSystemCapabilityDidChange) && \ + ((params)->toCapabilities & (flag)) && \ + (((params)->fromCapabilities & (flag)) == 0)) + +#define CAP_DID_CHANGE_TO_OFF(params, flag) \ + (((params)->changeFlags & kIOPMSystemCapabilityDidChange) && \ + ((params)->fromCapabilities & (flag)) && \ + (((params)->toCapabilities & (flag)) == 0)) + +#define CAP_WILL_CHANGE_TO_ON(params, flag) \ + (((params)->changeFlags & kIOPMSystemCapabilityWillChange) && \ + ((params)->toCapabilities & (flag)) && \ + (((params)->fromCapabilities & (flag)) == 0)) + //****************************************************************************** // sysPowerDownHandler // @@ -2576,18 +3053,13 @@ IOReturn IOPMrootDomain::sysPowerDownHandler( if (messageType == kIOMessageSystemWillSleep) { #if HIBERNATION - uint32_t mem_only = 0; IOPowerStateChangeNotification *notify = - (IOPowerStateChangeNotification *)messageArgs; + (IOPowerStateChangeNotification *)messageArgs; - PE_parse_boot_argn("swd_mem_only", &mem_only, sizeof(mem_only)); - if ((mem_only != 1) && (gRootDomain->sleepWakeDebugIsWdogEnabled())) - { - notify->returnValue = 30 * 1000 * 1000; - thread_call_enter1( - gRootDomain->hibDebugSetupEntry, - (thread_call_param_t)(uintptr_t) notify->powerRef); - } + notify->returnValue = 30 * 1000 * 1000; + thread_call_enter1( + gRootDomain->swdDebugSetupEntry, + (thread_call_param_t)(uintptr_t) notify->powerRef); #endif } else if (messageType == kIOMessageSystemCapabilityChange) @@ -2606,12 +3078,14 @@ IOReturn IOPMrootDomain::sysPowerDownHandler( params->fromCapabilities, params->toCapabilities, params->changeFlags); - if ((params->changeFlags & kIOPMSystemCapabilityWillChange) && - (params->fromCapabilities & kIOPMSystemCapabilityCPU) && - (params->toCapabilities & kIOPMSystemCapabilityCPU) == 0) + if (CAP_WILL_CHANGE_TO_OFF(params, kIOPMSystemCapabilityCPU)) { // We will ack within 20 seconds params->maxWaitForReply = 20 * 1000 * 1000; + + // Remove EFI/BootRom's previous wake's failure data + PERemoveNVRAMProperty(kIOEFIBootRomFailureKey); + #if HIBERNATION gRootDomain->evaluateSystemSleepPolicyEarly(); @@ -2648,20 +3122,36 @@ IOReturn IOPMrootDomain::sysPowerDownHandler( gRootDomain->diskSyncCalloutEntry, (thread_call_param_t)(uintptr_t) params->notifyRef); } - else - if ((params->changeFlags & kIOPMSystemCapabilityDidChange) && - (params->toCapabilities & kIOPMSystemCapabilityCPU) && - (params->fromCapabilities & kIOPMSystemCapabilityCPU) == 0) - { #if HIBERNATION + else if (CAP_DID_CHANGE_TO_ON(params, kIOPMSystemCapabilityCPU)) + { // We will ack within 110 seconds params->maxWaitForReply = 110 * 1000 * 1000; thread_call_enter1( gRootDomain->diskSyncCalloutEntry, (thread_call_param_t)(uintptr_t) params->notifyRef); -#endif } + else if (CAP_WILL_CHANGE_TO_OFF(params, kIOPMSystemCapabilityGraphics) || + CAP_WILL_CHANGE_TO_ON(params, kIOPMSystemCapabilityGraphics)) + { + // WillChange for Full wake -> Darkwake + params->maxWaitForReply = 30 * 1000 * 1000; + thread_call_enter1( + gRootDomain->swdDebugSetupEntry, + (thread_call_param_t)(uintptr_t) params->notifyRef); + } + else if (CAP_DID_CHANGE_TO_OFF(params, kIOPMSystemCapabilityGraphics) || + CAP_DID_CHANGE_TO_ON(params, kIOPMSystemCapabilityGraphics)) + { + // DidChange for Full wake -> Darkwake + params->maxWaitForReply = 30 * 1000 * 1000; + thread_call_enter1( + gRootDomain->swdDebugTearDownEntry, + (thread_call_param_t)(uintptr_t) params->notifyRef); + + } +#endif ret = kIOReturnSuccess; } @@ -2844,13 +3334,41 @@ hibernate_should_abort(void) void IOPMrootDomain::willNotifyPowerChildren( IOPMPowerStateIndex newPowerState ) { -#if HIBERNATION + OSDictionary *dict; + OSNumber *secs; + if (SLEEP_STATE == newPowerState) { + if (!tasksSuspended) + { + AbsoluteTime deadline; + tasksSuspended = TRUE; + tasks_system_suspend(tasksSuspended); + + clock_interval_to_deadline(10, kSecondScale, &deadline); +#if !CONFIG_EMBEDDED + vm_pageout_wait(AbsoluteTime_to_scalar(&deadline)); +#endif /* !CONFIG_EMBEDDED */ + } + +#if HIBERNATION IOHibernateSystemSleep(); IOHibernateIOKitSleep(); - } #endif + if (gRootDomain->activitySinceSleep()) { + dict = OSDictionary::withCapacity(1); + secs = OSNumber::withNumber(1, 32); + + if (dict && secs) { + dict->setObject(gIOPMSettingDebugWakeRelativeKey, secs); + gRootDomain->setProperties(dict); + MSG("Reverting sleep with relative wake\n"); + } + if (dict) dict->release(); + if (secs) secs->release(); + } + + } } //****************************************************************************** @@ -3252,7 +3770,7 @@ IOReturn IOPMrootDomain::setPMSetting( fPMSettingsDict->setObject(type, object); // Prep all PMSetting objects with the given 'type' for callout. - array = (const OSArray *) settingsCallbacks->getObject(type); + array = OSDynamicCast(OSArray, settingsCallbacks->getObject(type)); if (!array || ((capacity = array->getCount()) == 0)) goto unlock_exit; @@ -3405,7 +3923,7 @@ IOReturn IOPMrootDomain::registerPMSettingController( PMSETTING_LOCK(); for (i=0; settings[i]; i++) { - list = (OSArray *) settingsCallbacks->getObject(settings[i]); + list = OSDynamicCast(OSArray, settingsCallbacks->getObject(settings[i])); if (!list) { // New array of callbacks for this setting list = OSArray::withCapacity(1); @@ -3470,7 +3988,7 @@ void IOPMrootDomain::deregisterPMSettingObject( PMSettingObject * pmso ) { while ((sym = OSDynamicCast(OSSymbol, iter->getNextObject()))) { - array = (OSArray *) settingsCallbacks->getObject(sym); + array = OSDynamicCast(OSArray, settingsCallbacks->getObject(sym)); index = array->getNextIndexOfObject(pmso, 0); if (-1 != index) { array->removeObject(index); @@ -3522,9 +4040,9 @@ void IOPMrootDomain::informCPUStateChange( varInfoStruct.varType = vBool; varInfoStruct.varInitValue = value; varInfoStruct.varCurValue = value; - strncpy( (char *)varInfoStruct.varName, + strlcpy( (char *)varInfoStruct.varName, (const char *)varNameStr, - strlen(varNameStr) + 1 ); + sizeof(varInfoStruct.varName)); // Set! pmCPUret = pmCPUControl( PMIOCSETVARINFO, (void *)&varInfoStruct ); @@ -3618,6 +4136,7 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( uint32_t standbyDelay = 0; uint32_t powerOffDelay = 0; uint32_t powerOffTimer = 0; + uint32_t standbyTimer = 0; uint32_t mismatch; bool standbyEnabled; bool powerOffEnabled; @@ -3637,9 +4156,11 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( && (getProperty(kIOPMAutoPowerOffEnabledKey) == kOSBooleanTrue)); if (!getSleepOption(kIOPMAutoPowerOffTimerKey, &powerOffTimer)) powerOffTimer = powerOffDelay; + if (!getSleepOption(kIOPMDeepSleepTimerKey, &standbyTimer)) + standbyTimer = standbyDelay; - DLOG("phase %d, standby %d delay %u, poweroff %d delay %u timer %u, hibernate 0x%x\n", - sleepPhase, standbyEnabled, standbyDelay, + DLOG("phase %d, standby %d delay %u timer %u, poweroff %d delay %u timer %u, hibernate 0x%x\n", + sleepPhase, standbyEnabled, standbyDelay, standbyTimer, powerOffEnabled, powerOffDelay, powerOffTimer, *hibMode); // pmset level overrides @@ -3675,10 +4196,15 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( currentFactors |= kIOPMSleepFactorACPower; if (lowBatteryCondition) currentFactors |= kIOPMSleepFactorBatteryLow; - if (!standbyDelay) + if (!standbyDelay || !standbyTimer) currentFactors |= kIOPMSleepFactorStandbyNoDelay; - if (!standbyEnabled) + if (standbyNixed || !standbyEnabled) currentFactors |= kIOPMSleepFactorStandbyDisabled; + if (resetTimers) + { + currentFactors |= kIOPMSleepFactorLocalUserActivity; + currentFactors &= ~kIOPMSleepFactorSleepTimerWake; + } if (getPMAssertionLevel(kIOPMDriverAssertionUSBExternalDeviceBit) != kIOPMDriverAssertionLevelOff) currentFactors |= kIOPMSleepFactorUSBExternalDevice; @@ -3710,6 +4236,8 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( currentFactors |= kIOPMSleepFactorLocalUserActivity; if (darkWakeHibernateError && !CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) currentFactors |= kIOPMSleepFactorHibernateFailed; + if (thermalWarningState) + currentFactors |= kIOPMSleepFactorThermalWarning; DLOG("sleep factors 0x%llx\n", currentFactors); @@ -3733,6 +4261,7 @@ bool IOPMrootDomain::evaluateSystemSleepPolicy( gSleepPolicyVars->sleepReason = lastSleepReason; gSleepPolicyVars->sleepPhase = sleepPhase; gSleepPolicyVars->standbyDelay = standbyDelay; + gSleepPolicyVars->standbyTimer = standbyTimer; gSleepPolicyVars->poweroffDelay = powerOffDelay; gSleepPolicyVars->scheduledAlarms = _scheduledAlarms | _userScheduledAlarm; gSleepPolicyVars->poweroffTimer = powerOffTimer; @@ -3921,36 +4450,56 @@ void IOPMrootDomain::evaluateSystemSleepPolicyFinal( void ) { IOPMSystemSleepParameters params; OSData * paramsData; - + bool wakeNow; // Evaluate sleep policy after sleeping drivers but before platform sleep. DLOG("%s\n", __FUNCTION__); bzero(¶ms, sizeof(params)); + wakeNow = false; if (evaluateSystemSleepPolicy(¶ms, kIOPMSleepPhase2, &hibernateMode)) { - if ((hibernateDisabled || hibernateAborted) && + if ((kIOPMSleepTypeStandby == params.sleepType) + && gIOHibernateStandbyDisabled && gSleepPolicyVars + && (!(kIOPMSleepFactorStandbyForced & gSleepPolicyVars->sleepFactors))) + { + standbyNixed = true; + wakeNow = true; + } + if (wakeNow + || ((hibernateDisabled || hibernateAborted) && (getSleepTypeAttributes(params.sleepType) & - kIOPMSleepAttributeHibernateSetup)) + kIOPMSleepAttributeHibernateSetup))) { // Final evaluation picked a state requiring hibernation, - // but hibernate setup was skipped. Arm a short sleep using + // but hibernate isn't going to proceed. Arm a short sleep using // the early non-hibernate sleep parameters. - // Set hibernateRetry flag to force hibernate setup on the - // next sleep. - bcopy(&gEarlySystemSleepParams, ¶ms, sizeof(params)); params.sleepType = kIOPMSleepTypeAbortedSleep; params.ecWakeTimer = 1; - hibernateRetry = true; - DLOG("wake in %u secs for hibernateDisabled %d, hibernateAborted %d\n", - params.ecWakeTimer, hibernateDisabled, hibernateAborted); + if (standbyNixed) + { + resetTimers = true; + } + else + { + // Set hibernateRetry flag to force hibernate setup on the + // next sleep. + hibernateRetry = true; + } + DLOG("wake in %u secs for hibernateDisabled %d, hibernateAborted %d, standbyNixed %d\n", + params.ecWakeTimer, hibernateDisabled, hibernateAborted, standbyNixed); } else { hibernateRetry = false; } + if (kIOPMSleepTypeAbortedSleep != params.sleepType) + { + resetTimers = false; + } + paramsData = OSData::withBytes(¶ms, sizeof(params)); if (paramsData) { @@ -4006,10 +4555,18 @@ bool IOPMrootDomain::getSleepOption( const char * key, uint32_t * option ) { obj = copyProperty(key); } - if (obj && (num = OSDynamicCast(OSNumber, obj))) + if (obj) { - *option = num->unsigned32BitValue(); - ok = true; + if ((num = OSDynamicCast(OSNumber, obj))) + { + *option = num->unsigned32BitValue(); + ok = true; + } + else if (OSDynamicCast(OSBoolean, obj)) + { + *option = (obj == kOSBooleanTrue) ? 1 : 0; + ok = true; + } } if (obj) @@ -4017,11 +4574,11 @@ bool IOPMrootDomain::getSleepOption( const char * key, uint32_t * option ) if (optionsProp) optionsProp->release(); - return true; + return ok; } #endif /* HIBERNATION */ -IOReturn IOPMrootDomain::getSystemSleepType( uint32_t * sleepType ) +IOReturn IOPMrootDomain::getSystemSleepType( uint32_t * sleepType, uint32_t * standbyTimer ) { #if HIBERNATION IOPMSystemSleepParameters params; @@ -4034,7 +4591,7 @@ IOReturn IOPMrootDomain::getSystemSleepType( uint32_t * sleepType ) OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::getSystemSleepType), (OSObject *) this, - (void *) sleepType); + (void *) sleepType, (void *) standbyTimer); return ret; } @@ -4045,6 +4602,11 @@ IOReturn IOPMrootDomain::getSystemSleepType( uint32_t * sleepType ) if (ok) { *sleepType = params.sleepType; + if (!getSleepOption(kIOPMDeepSleepTimerKey, standbyTimer) && + !getSleepOption(kIOPMDeepSleepDelayKey, standbyTimer)) { + DLOG("Standby delay is not set\n"); + *standbyTimer = 0; + } return kIOReturnSuccess; } #endif @@ -4066,6 +4628,7 @@ struct HaltRestartApplierContext { IOPMPowerFlags PowerFlags; UInt32 MessageType; UInt32 Counter; + const char * LogString; }; static void @@ -4073,8 +4636,8 @@ platformHaltRestartApplier( OSObject * object, void * context ) { IOPowerStateChangeNotification notify; HaltRestartApplierContext * ctx; - AbsoluteTime startTime; - UInt32 deltaTime; + AbsoluteTime startTime, elapsedTime; + uint32_t deltaTime; ctx = (HaltRestartApplierContext *) context; @@ -4086,7 +4649,7 @@ platformHaltRestartApplier( OSObject * object, void * context ) clock_get_uptime(&startTime); ctx->RootDomain->messageClient( ctx->MessageType, object, (void *)¬ify ); - deltaTime = computeDeltaTimeMS(&startTime); + deltaTime = computeDeltaTimeMS(&startTime, &elapsedTime); if ((deltaTime > kPMHaltTimeoutMS) || (gIOKitDebug & kIOLogPMRootDomain)) @@ -4100,20 +4663,27 @@ platformHaltRestartApplier( OSObject * object, void * context ) if (notifier) { LOG("%s handler %p took %u ms\n", - (ctx->MessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : - (ctx->MessageType == kIOMessageSystemPagingOff) ? "PagingOff" : "Restart", - OBFUSCATE(notifier->handler), (uint32_t) deltaTime ); + ctx->LogString, OBFUSCATE(notifier->handler), deltaTime); + halt_log_enter(ctx->LogString, (const void *) notifier->handler, elapsedTime); } } ctx->Counter++; } +static void quiescePowerTreeCallback( void * target, void * param ) +{ + IOLockLock(gPMHaltLock); + gPMQuiesced = true; + thread_wakeup(param); + IOLockUnlock(gPMHaltLock); +} + void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type ) { HaltRestartApplierContext ctx; - AbsoluteTime startTime; - UInt32 deltaTime; + AbsoluteTime startTime, elapsedTime; + uint32_t deltaTime; memset(&ctx, 0, sizeof(ctx)); ctx.RootDomain = this; @@ -4125,16 +4695,19 @@ void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type ) case kPEUPSDelayHaltCPU: ctx.PowerState = OFF_STATE; ctx.MessageType = kIOMessageSystemWillPowerOff; + ctx.LogString = "PowerOff"; break; case kPERestartCPU: ctx.PowerState = RESTART_STATE; ctx.MessageType = kIOMessageSystemWillRestart; + ctx.LogString = "Restart"; break; case kPEPagingOff: ctx.PowerState = ON_STATE; ctx.MessageType = kIOMessageSystemPagingOff; + ctx.LogString = "PagingOff"; IOService::updateConsoleUsers(NULL, kIOMessageSystemPagingOff); #if HIBERNATION IOHibernateSystemRestart(); @@ -4169,11 +4742,44 @@ void IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type ) IOCPURunPlatformHaltRestartActions(pe_type); - deltaTime = computeDeltaTimeMS(&startTime); - LOG("%s all drivers took %u ms\n", - (ctx.MessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : - (ctx.MessageType == kIOMessageSystemPagingOff) ? "PagingOff" : "Restart", - (uint32_t) deltaTime ); + // Wait for PM to quiesce + if ((kPEPagingOff != pe_type) && gPMHaltLock) + { + AbsoluteTime quiesceTime = mach_absolute_time(); + + IOLockLock(gPMHaltLock); + gPMQuiesced = false; + if (quiescePowerTree(this, &quiescePowerTreeCallback, &gPMQuiesced) == + kIOReturnSuccess) + { + while (!gPMQuiesced) + { + IOLockSleep(gPMHaltLock, &gPMQuiesced, THREAD_UNINT); + } + } + IOLockUnlock(gPMHaltLock); + deltaTime = computeDeltaTimeMS(&quiesceTime, &elapsedTime); + DLOG("PM quiesce took %u ms\n", deltaTime); + halt_log_enter("Quiesce", NULL, elapsedTime); + } + + deltaTime = computeDeltaTimeMS(&startTime, &elapsedTime); + LOG("%s all drivers took %u ms\n", ctx.LogString, deltaTime); + + halt_log_enter(ctx.LogString, NULL, elapsedTime); + if (gHaltLog) gHaltLog[gHaltLogPos] = 0; + + deltaTime = computeDeltaTimeMS(&gHaltStartTime, &elapsedTime); + LOG("%s total %u ms\n", ctx.LogString, deltaTime); + + if (gHaltLog && gHaltTimeMaxLog && (deltaTime >= gHaltTimeMaxLog)) + { + printf("%s total %d ms:%s\n", ctx.LogString, deltaTime, gHaltLog); + } + if (gHaltLog && gHaltTimeMaxPanic && (deltaTime >= gHaltTimeMaxPanic)) + { + panic("%s total %d ms:%s\n", ctx.LogString, deltaTime, gHaltLog); + } } //****************************************************************************** @@ -4425,6 +5031,7 @@ void IOPMrootDomain::handleOurPowerChangeStart( _systemTransitionType = kSystemTransitionNone; _systemMessageClientMask = 0; capabilityLoss = false; + toldPowerdCapWillChange = false; if (lowBatteryCondition) { @@ -4483,15 +5090,6 @@ void IOPMrootDomain::handleOurPowerChangeStart( _pendingCapability = 0; capabilityLoss = true; - // Clear previous stats - IOLockLock(pmStatsLock); - if (pmStatsAppResponses) - { - pmStatsAppResponses->release(); - pmStatsAppResponses = OSArray::withCapacity(5); - } - IOLockUnlock(pmStatsLock); - } else if (kSystemTransitionNewCapClient != _systemTransitionType) { @@ -4529,6 +5127,16 @@ void IOPMrootDomain::handleOurPowerChangeStart( // Full to Dark transition. if (CAP_LOSS(kIOPMSystemCapabilityGraphics)) { + // Clear previous stats + IOLockLock(pmStatsLock); + if (pmStatsAppResponses) + { + pmStatsAppResponses->release(); + pmStatsAppResponses = OSArray::withCapacity(5); + } + IOLockUnlock(pmStatsLock); + + tracePoint( kIOPMTracePointDarkWakeEntry ); *inOutChangeFlags |= kIOPMSyncTellPowerDown; _systemMessageClientMask = kSystemMessageClientPowerd | @@ -4548,6 +5156,12 @@ void IOPMrootDomain::handleOurPowerChangeStart( // Publish a UUID for the Sleep --> Wake cycle handlePublishSleepWakeUUID(true); + if (sleepDelaysReport) { + clock_get_uptime(&ts_sleepStart); + DLOG("sleepDelaysReport f->9 start at 0x%llx\n", ts_sleepStart); + } + + wranglerTickled = false; } } @@ -4557,19 +5171,26 @@ void IOPMrootDomain::handleOurPowerChangeStart( { // Beginning of a system sleep transition. // Cancellation is still possible. - tracePoint( kIOPMTracePointSleepStarted, sleepReason ); + tracePoint( kIOPMTracePointSleepStarted ); _systemMessageClientMask = kSystemMessageClientAll; if ((_currentCapability & kIOPMSystemCapabilityGraphics) == 0) _systemMessageClientMask &= ~kSystemMessageClientLegacyApp; if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0) _systemMessageClientMask &= ~kSystemMessageClientKernel; +#if HIBERNATION + gIOHibernateState = 0; +#endif // Record the reason for dark wake back to sleep // System may not have ever achieved full wake publishSleepReason = true; lastSleepReason = sleepReason; + if (sleepDelaysReport) { + clock_get_uptime(&ts_sleepStart); + DLOG("sleepDelaysReport 9->0 start at 0x%llx\n", ts_sleepStart); + } } // 3. System wake. @@ -4660,10 +5281,20 @@ void IOPMrootDomain::handleOurPowerChangeDone( if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics) && CAP_CURRENT(kIOPMSystemCapabilityCPU)) { +#if !CONFIG_EMBEDDED pmPowerStateQueue->submitPowerEvent( kPowerEventPolicyStimulus, (void *) kStimulusDarkWakeReentry, _systemStateGeneration ); +#else + // On embedded, there are no factors that can prolong a + // "darkWake" when a power down is vetoed. We need to + // promote to "fullWake" at least once so that factors + // that prevent idle sleep can assert themselves if required + pmPowerStateQueue->submitPowerEvent( + kPowerEventPolicyStimulus, + (void *) kStimulusDarkWakeActivityTickle); +#endif } // Revert device desire to max. @@ -4685,8 +5316,14 @@ void IOPMrootDomain::handleOurPowerChangeDone( { // Going dark, reset full wake state // userIsActive will be cleared by wrangler powering down - wranglerTickled = false; fullWakeReason = kFullWakeReasonNone; + + if (ts_sleepStart) { + clock_get_uptime(&wake2DarkwakeDelay); + SUB_ABSOLUTETIME(&wake2DarkwakeDelay, &ts_sleepStart); + DLOG("sleepDelaysReport f->9 end 0x%llx\n", wake2DarkwakeDelay); + ts_sleepStart = 0; + } } } @@ -4770,6 +5407,9 @@ void IOPMrootDomain::handleOurPowerChangeDone( darkWakePostTickle = false; reportUserInput(); } + else if (wranglerTickled) { + requestFullWake( kFullWakeReasonLocalUser ); + } // Reset tracepoint at completion of capability change, // completion of wake transition, and aborted sleep transition. @@ -4780,13 +5420,24 @@ void IOPMrootDomain::handleOurPowerChangeDone( (changeFlags & kIOPMNotDone))) { setProperty(kIOPMSystemCapabilitiesKey, _currentCapability, 64); - tracePoint( kIOPMTracePointSystemUp, 0 ); + tracePoint( kIOPMTracePointSystemUp ); } _systemTransitionType = kSystemTransitionNone; _systemMessageClientMask = 0; + toldPowerdCapWillChange = false; logGraphicsClamp = false; + + if (lowBatteryCondition) { + privateSleepSystem (kIOPMSleepReasonLowPower); + } + else if ((fullWakeReason == kFullWakeReasonDisplayOn) && (!displayPowerOnRequested)) { + // Request for full wake is removed while system is waking up to full wake + DLOG("DisplayOn fullwake request is removed\n"); + handleDisplayPowerOn(); + } + } } @@ -4923,7 +5574,7 @@ void IOPMrootDomain::overridePowerChangeForUIService( uint64_t nsec; clock_get_uptime(&now); - SUB_ABSOLUTETIME(&now, &systemWakeTime); + SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); absolutetime_to_nanoseconds(now, &nsec); if (kIOLogPMRootDomain & gIOKitDebug) MSG("Graphics suppressed %u ms\n", @@ -4947,7 +5598,8 @@ void IOPMrootDomain::handleActivityTickleForDisplayWrangler( clock_get_uptime(&userActivityTime); bool aborting = ((lastSleepReason == kIOPMSleepReasonIdle) - || (lastSleepReason == kIOPMSleepReasonMaintenance)); + || (lastSleepReason == kIOPMSleepReasonMaintenance) + || (lastSleepReason == kIOPMSleepReasonSoftware)); if (aborting) { userActivityCount++; DLOG("display wrangler tickled1 %d lastSleepReason %d\n", @@ -5015,6 +5667,13 @@ void IOPMrootDomain::handleUpdatePowerClientForDisplayWrangler( evaluatePolicy( kStimulusLeaveUserActiveState ); } } + + if (newPowerState <= kWranglerPowerStateSleep) { + evaluatePolicy( kStimulusDisplayWranglerSleep ); + } + else if (newPowerState == kWranglerPowerStateMax) { + evaluatePolicy( kStimulusDisplayWranglerWake ); + } #endif } @@ -5102,7 +5761,11 @@ class IOPMServiceInterestNotifier: public _IOServiceInterestNotifier protected: uint32_t ackTimeoutCnt; + uint32_t msgType; // Message pending ack + uint64_t uuid0; + uint64_t uuid1; + const OSSymbol *identifier; }; OSDefineMetaClassAndStructors(IOPMServiceInterestNotifier, _IOServiceInterestNotifier) @@ -5132,11 +5795,13 @@ IONotifier * IOPMrootDomain::registerInterest( if (!notifier) return NULL; if (notifier->init()) { - rc = super::registerInterestForNotifer(notifier, typeOfInterest, handler, target, ref); + rc = super::registerInterestForNotifier(notifier, typeOfInterest, handler, target, ref); } if (rc != kIOReturnSuccess) { notifier->release(); notifier = 0; + + return NULL; } if (pmPowerStateQueue) { @@ -5158,6 +5823,28 @@ IONotifier * IOPMrootDomain::registerInterest( } } + OSData *data = NULL; + uint8_t *uuid = NULL; + OSKext *kext = OSKext::lookupKextWithAddress((vm_address_t)handler); + if (kext) { + data = kext->copyUUID(); + } + if (data && (data->getLength() == sizeof(uuid_t))) { + uuid = (uint8_t *)(data->getBytesNoCopy()); + + notifier->uuid0 = ((uint64_t)(uuid[0]) << 56) | ((uint64_t)(uuid[1]) << 48) | ((uint64_t)(uuid[2]) << 40)| + ((uint64_t)(uuid[3]) << 32) | ((uint64_t)(uuid[4]) << 24) | ((uint64_t)(uuid[5]) << 16) | + ((uint64_t)(uuid[6]) << 8) | (uuid[7]); + notifier->uuid1 = ((uint64_t)(uuid[8]) << 56) | ((uint64_t)(uuid[9]) << 48) | ((uint64_t)(uuid[10]) << 40)| + ((uint64_t)(uuid[11]) << 32) | ((uint64_t)(uuid[12]) << 24) | ((uint64_t)(uuid[13]) << 16) | + ((uint64_t)(uuid[14]) << 8) | (uuid[15]); + + notifier->identifier = kext->getIdentifier(); + + } + if (kext) kext->release(); + if (data) data->release(); + return notifier; } @@ -5173,7 +5860,9 @@ bool IOPMrootDomain::systemMessageFilter( bool isCapMsg = (context->messageType == kIOMessageSystemCapabilityChange); bool isCapClient = false; bool allow = false; + IOPMServiceInterestNotifier *notifier; + notifier = OSDynamicCast(IOPMServiceInterestNotifier, (OSObject *)object); do { if ((kSystemTransitionNewCapClient == _systemTransitionType) && (!isCapMsg || !_joinedCapabilityClients || @@ -5213,6 +5902,12 @@ bool IOPMrootDomain::systemMessageFilter( capArgs->changeFlags = kIOPMSystemCapabilityWillChange; else capArgs->changeFlags = kIOPMSystemCapabilityDidChange; + + if ((object == (void *) systemCapabilityNotifier) && + context->isPreChange) + { + toldPowerdCapWillChange = true; + } } // Capability change messages only go to the PM configd plugin. @@ -5225,6 +5920,7 @@ bool IOPMrootDomain::systemMessageFilter( { // app has not replied yet, wait for it *((OSObject **) arg3) = kOSBooleanFalse; + } allow = true; @@ -5256,8 +5952,9 @@ bool IOPMrootDomain::systemMessageFilter( { if ((object == (OSObject *) systemCapabilityNotifier) && CAP_HIGHEST(kIOPMSystemCapabilityGraphics) && - (fullToDarkReason == kIOPMSleepReasonIdle)) + (fullToDarkReason == kIOPMSleepReasonIdle)) { allow = true; + } break; } @@ -5274,16 +5971,15 @@ bool IOPMrootDomain::systemMessageFilter( if ((context->notifyType == kNotifyApps) && (_systemMessageClientMask & kSystemMessageClientLegacyApp)) { - IOPMServiceInterestNotifier *notify; allow = true; - if ((notify = OSDynamicCast(IOPMServiceInterestNotifier, (OSObject *)object)) - && arg3) { - - if (notify->ackTimeoutCnt >= 3) - *((OSObject **) arg3) = kOSBooleanFalse; - else - *((OSObject **) arg3) = kOSBooleanTrue; + if (notifier) { + if (arg3) { + if (notifier->ackTimeoutCnt >= 3) + *((OSObject **) arg3) = kOSBooleanFalse; + else + *((OSObject **) arg3) = kOSBooleanTrue; + } } } else if ((context->notifyType == kNotifyPriority) && @@ -5305,6 +6001,9 @@ bool IOPMrootDomain::systemMessageFilter( _joinedCapabilityClients = 0; } } + if (notifier) { + notifier->msgType = context->messageType; + } return allow; } @@ -5425,7 +6124,9 @@ bool IOPMrootDomain::displayWranglerMatchPublished( IONotifier * notifier __unused) { #if !NO_KERNEL_HID - // found the display wrangler, now install a handler + // found the display wrangler, check for any display assertions already created + gRootDomain->evaluateWranglerAssertions(); + // install a handler if( !newService->registerInterest( gIOGeneralInterest, &displayWranglerNotification, target, 0) ) { @@ -5488,13 +6189,16 @@ void IOPMrootDomain::reportUserInput( void ) { #if !NO_KERNEL_HID OSIterator * iter; + OSDictionary * matching; if(!wrangler) { - iter = getMatchingServices(serviceMatching("IODisplayWrangler")); + matching = serviceMatching("IODisplayWrangler"); + iter = getMatchingServices(matching); + if (matching) matching->release(); if(iter) { - wrangler = (IOService *) iter->getNextObject(); + wrangler = OSDynamicCast(IOService, iter->getNextObject()); iter->release(); } } @@ -5550,11 +6254,8 @@ bool IOPMrootDomain::latchDisplayWranglerTickle( bool latch ) void IOPMrootDomain::setDisplayPowerOn( uint32_t options ) { - if (checkSystemCanSustainFullWake()) - { - pmPowerStateQueue->submitPowerEvent( kPowerEventSetDisplayPowerOn, - (void *) 0, options ); - } + pmPowerStateQueue->submitPowerEvent( kPowerEventSetDisplayPowerOn, + (void *) 0, options ); } // MARK: - @@ -5622,9 +6323,9 @@ bool IOPMrootDomain::checkSystemSleepAllowed( IOOptionBits options, break; #endif - if (lowBatteryCondition) + if (lowBatteryCondition || thermalWarningState) { - break; // always sleep on low battery + break; // always sleep on low battery or when in thermal warning state } if (sleepReason == kIOPMSleepReasonDarkWakeThermalEmergency) @@ -5698,7 +6399,7 @@ bool IOPMrootDomain::checkSystemCanSleep( uint32_t sleepReason ) bool IOPMrootDomain::checkSystemCanSustainFullWake( void ) { #if !NO_KERNEL_HID - if (lowBatteryCondition) + if (lowBatteryCondition || thermalWarningState) { // Low battery wake, or received a low battery notification // while system is awake. This condition will persist until @@ -5722,6 +6423,19 @@ bool IOPMrootDomain::checkSystemCanSustainFullWake( void ) return true; } +//****************************************************************************** +// mustHibernate +//****************************************************************************** + +#if HIBERNATION + +bool IOPMrootDomain::mustHibernate( void ) +{ + return (lowBatteryCondition || thermalWarningState); +} + +#endif /* HIBERNATION */ + //****************************************************************************** // adjustPowerState // @@ -5734,12 +6448,12 @@ bool IOPMrootDomain::checkSystemCanSustainFullWake( void ) void IOPMrootDomain::adjustPowerState( bool sleepASAP ) { - DLOG("adjustPowerState ps %u, asap %d, slider %ld\n", - (uint32_t) getPowerState(), sleepASAP, sleepSlider); + DLOG("adjustPowerState ps %u, asap %d, idleSleepEnabled %d\n", + (uint32_t) getPowerState(), sleepASAP, idleSleepEnabled); ASSERT_GATED(); - if ((sleepSlider == 0) || !checkSystemSleepEnabled()) + if ((!idleSleepEnabled) || !checkSystemSleepEnabled()) { changePowerStateToPriv(ON_STATE); } @@ -5749,6 +6463,41 @@ void IOPMrootDomain::adjustPowerState( bool sleepASAP ) } } +void IOPMrootDomain::handleDisplayPowerOn( ) +{ + if (!wrangler) return; + if (displayPowerOnRequested) + { + if (!checkSystemCanSustainFullWake()) return; + + // Force wrangler to max power state. If system is in dark wake + // this alone won't raise the wrangler's power state. + + wrangler->changePowerStateForRootDomain(kWranglerPowerStateMax); + + // System in dark wake, always requesting full wake should + // not have any bad side-effects, even if the request fails. + + if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics)) + { + setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeNotification); + requestFullWake( kFullWakeReasonDisplayOn ); + } + } + else + { + // Relenquish desire to power up display. + // Must first transition to state 1 since wrangler doesn't + // power off the displays at state 0. At state 0 the root + // domain is removed from the wrangler's power client list. + + wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin + 1); + wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin); + + } + +} + //****************************************************************************** // dispatchPowerEvent // @@ -5758,20 +6507,22 @@ void IOPMrootDomain::adjustPowerState( bool sleepASAP ) void IOPMrootDomain::dispatchPowerEvent( uint32_t event, void * arg0, uint64_t arg1 ) { - DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); ASSERT_GATED(); switch (event) { case kPowerEventFeatureChanged: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); messageClients(kIOPMMessageFeatureChange, this); break; case kPowerEventReceivedPowerNotification: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); handlePowerNotification( (UInt32)(uintptr_t) arg0 ); break; case kPowerEventSystemBootCompleted: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (systemBooting) { systemBooting = false; @@ -5811,6 +6562,7 @@ void IOPMrootDomain::dispatchPowerEvent( break; case kPowerEventSystemShutdown: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (kOSBooleanTrue == (OSBoolean *) arg0) { /* We set systemShutdown = true during shutdown @@ -5825,18 +6577,20 @@ void IOPMrootDomain::dispatchPowerEvent( systemShutdown = true; } else { /* - A shutdown was initiated, but then the shutdown - was cancelled, clearing systemShutdown to false here. - */ + A shutdown was initiated, but then the shutdown + was cancelled, clearing systemShutdown to false here. + */ systemShutdown = false; } break; case kPowerEventUserDisabledSleep: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); userDisabledAllSleep = (kOSBooleanTrue == (OSBoolean *) arg0); break; case kPowerEventRegisterSystemCapabilityClient: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (systemCapabilityNotifier) { systemCapabilityNotifier->release(); @@ -5848,8 +6602,10 @@ void IOPMrootDomain::dispatchPowerEvent( systemCapabilityNotifier->retain(); } /* intentional fall-through */ + [[clang::fallthrough]]; case kPowerEventRegisterKernelCapabilityClient: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (!_joinedCapabilityClients) _joinedCapabilityClients = OSSet::withCapacity(8); if (arg0) @@ -5865,6 +6621,7 @@ void IOPMrootDomain::dispatchPowerEvent( break; case kPowerEventPolicyStimulus: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (arg0) { int stimulus = (uintptr_t) arg0; @@ -5873,6 +6630,7 @@ void IOPMrootDomain::dispatchPowerEvent( break; case kPowerEventAssertionCreate: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (pmAssertions) { pmAssertions->handleCreateAssertion((OSData *)arg0); } @@ -5880,52 +6638,40 @@ void IOPMrootDomain::dispatchPowerEvent( case kPowerEventAssertionRelease: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (pmAssertions) { pmAssertions->handleReleaseAssertion(arg1); } break; case kPowerEventAssertionSetLevel: + DMSG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (pmAssertions) { pmAssertions->handleSetAssertionLevel(arg1, (IOPMDriverAssertionLevel)(uintptr_t)arg0); } break; case kPowerEventQueueSleepWakeUUID: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); handleQueueSleepWakeUUID((OSObject *)arg0); break; case kPowerEventPublishSleepWakeUUID: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); handlePublishSleepWakeUUID((bool)arg0); break; case kPowerEventSetDisplayPowerOn: + DLOG("power event %u args %p 0x%llx\n", event, OBFUSCATE(arg0), arg1); if (!wrangler) break; if (arg1 != 0) { - // Force wrangler to max power state. If system is in dark wake - // this alone won't raise the wrangler's power state. - - wrangler->changePowerStateForRootDomain(kWranglerPowerStateMax); - - // System in dark wake, always requesting full wake should - // not have any bad side-effects, even if the request fails. - - if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics)) - { - setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeNotification); - requestFullWake( kFullWakeReasonDisplayOn ); - } + displayPowerOnRequested = true; } else { - // Relenquish desire to power up display. - // Must first transition to state 1 since wrangler doesn't - // power off the displays at state 0. At state 0 the root - // domain is removed from the wrangler's power client list. - - wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin + 1); - wrangler->changePowerStateForRootDomain(kWranglerPowerStateMin); + displayPowerOnRequested = false; } + handleDisplayPowerOn(); break; } } @@ -5962,6 +6708,24 @@ IOReturn IOPMrootDomain::systemPowerEventOccurred( return attempt; } +void IOPMrootDomain::setThermalState(OSObject *value) +{ + OSNumber * num; + + if (gIOPMWorkLoop->inGate() == false) { + gIOPMWorkLoop->runAction( + OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setThermalState), + (OSObject *)this, + (void *)value); + + return; + } + if (value && (num = OSDynamicCast(OSNumber, value))) { + thermalWarningState = ((num->unsigned32BitValue() == kIOPMThermalLevelWarning) || + (num->unsigned32BitValue() == kIOPMThermalLevelTrap)) ? 1 : 0; + } +} + IOReturn IOPMrootDomain::systemPowerEventOccurred( const OSSymbol *event, OSObject *value) @@ -6001,8 +6765,13 @@ exit: // UNLOCK if (featuresDictLock) IOLockUnlock(featuresDictLock); - if (shouldUpdate) + if (shouldUpdate) { + if (event && + event->isEqualTo(kIOPMThermalLevelWarningKey)) { + setThermalState(value); + } messageClients (kIOPMMessageSystemPowerEventOccurred, (void *)NULL); + } return kIOReturnSuccess; } @@ -6046,15 +6815,13 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) } /* - * Sleep if system is in dark wake + * Forward DW thermal notification to client, if system is not going to sleep */ - if (msg & kIOPMDWOverTemp) + if ((msg & kIOPMDWOverTemp) && (_systemTransitionType != kSystemTransitionSleep)) { DLOG("DarkWake thermal limits message received!\n"); - // Inform cap client that we're going to sleep messageClients(kIOPMMessageDarkWakeThermalEmergency); - } /* @@ -6079,6 +6846,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMClamshellOpened) { + DLOG("Clamshell opened\n"); // Received clamshel open message from clamshell controlling driver // Update our internal state and tell general interest clients clamshellClosed = false; @@ -6110,6 +6878,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMClamshellClosed) { + DLOG("Clamshell closed\n"); // Received clamshel open message from clamshell controlling driver // Update our internal state and tell general interest clients clamshellClosed = true; @@ -6132,6 +6901,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMSetDesktopMode) { + DLOG("Desktop mode\n"); desktopMode = (0 != (msg & kIOPMSetValue)); msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue); @@ -6185,6 +6955,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMEnableClamshell) { + DLOG("Clamshell enabled\n"); // Re-evaluate the lid state // System should sleep on external display disappearance // in lid closed operation. @@ -6204,6 +6975,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMDisableClamshell) { + DLOG("Clamshell disabled\n"); clamshellDisabled = true; sendClientClamshellNotification(); } @@ -6224,6 +6996,7 @@ void IOPMrootDomain::handlePowerNotification( UInt32 msg ) */ if (msg & kIOPMPowerButton) { + DLOG("Powerbutton press\n"); if (!wranglerAsleep) { OSString *pbs = OSString::withCString("DisablePowerButtonSleep"); @@ -6259,7 +7032,6 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) uint32_t u32; } flags; - DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); ASSERT_GATED(); flags.u32 = 0; @@ -6267,20 +7039,22 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) switch (stimulus) { case kStimulusDisplayWranglerSleep: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); if (!wranglerAsleep) { // first transition to wrangler sleep or lower - wranglerAsleep = true; flags.bit.displaySleep = true; } break; case kStimulusDisplayWranglerWake: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); displayIdleForDemandSleep = false; wranglerAsleep = false; break; case kStimulusEnterUserActiveState: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); if (_preventUserActive) { DLOG("user active dropped\n"); @@ -6292,9 +7066,13 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) userWasActive = true; // Stay awake after dropping demand for display power on - if (kFullWakeReasonDisplayOn == fullWakeReason) + if (kFullWakeReasonDisplayOn == fullWakeReason) { fullWakeReason = fFullWakeReasonDisplayOnAndLocalUser; + DLOG("User activity while in notification wake\n"); + changePowerStateWithOverrideTo( ON_STATE, 0); + } + kdebugTrace(kPMLogUserActiveState, 0, 1, 0); setProperty(gIOPMUserIsActiveKey, kOSBooleanTrue); messageClients(kIOPMMessageUserIsActiveChanged); } @@ -6302,12 +7080,14 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; case kStimulusLeaveUserActiveState: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); if (userIsActive) { userIsActive = false; clock_get_uptime(&userBecameInactiveTime); flags.bit.userBecameInactive = true; + kdebugTrace(kPMLogUserActiveState, 0, 0, 0); setProperty(gIOPMUserIsActiveKey, kOSBooleanFalse); messageClients(kIOPMMessageUserIsActiveChanged); } @@ -6315,6 +7095,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) case kStimulusAggressivenessChanged: { + DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); unsigned long minutesToIdleSleep = 0; unsigned long minutesToDisplayDim = 0; unsigned long minutesDelta = 0; @@ -6330,8 +7111,6 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) DLOG("idle time -> %ld secs (ena %d)\n", idleSeconds, (minutesToIdleSleep != 0)); - if (0x7fffffff == minutesToIdleSleep) - minutesToIdleSleep = idleSeconds; // How long to wait before sleeping the system once // the displays turns off is indicated by 'extraSleepDelay'. @@ -6341,11 +7120,15 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) else if ( minutesToIdleSleep == minutesToDisplayDim ) minutesDelta = 1; - if ((sleepSlider == 0) && (minutesToIdleSleep != 0)) - flags.bit.idleSleepEnabled = true; + if ((!idleSleepEnabled) && (minutesToIdleSleep != 0)) + idleSleepEnabled = flags.bit.idleSleepEnabled = true; - if ((sleepSlider != 0) && (minutesToIdleSleep == 0)) + if ((idleSleepEnabled) && (minutesToIdleSleep == 0)) { flags.bit.idleSleepDisabled = true; + idleSleepEnabled = false; + } + if (0x7fffffff == minutesToIdleSleep) + minutesToIdleSleep = idleSeconds; if (((minutesDelta != extraSleepDelay) || (userActivityTime != userActivityTime_prev)) && @@ -6365,6 +7148,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) } break; case kStimulusDemandSystemSleep: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); displayIdleForDemandSleep = true; if (wrangler && wranglerIdleSettings) { @@ -6381,10 +7165,12 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; case kStimulusAllowSystemSleepChanged: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); flags.bit.adjustPowerState = true; break; case kStimulusDarkWakeActivityTickle: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); // arg == true implies real and not self generated wrangler tickle. // Update wake type on PM work loop instead of the tickle thread to // eliminate the possibility of an early tickle clobbering the wake @@ -6408,6 +7194,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) case kStimulusDarkWakeEntry: case kStimulusDarkWakeReentry: + DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); // Any system transitions since the last dark wake transition // will invalid the stimulus. @@ -6424,6 +7211,10 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) { clock_get_uptime(&userBecameInactiveTime); flags.bit.evaluateDarkWake = true; + if (activitySinceSleep()) { + DLOG("User activity recorded while going to darkwake\n"); + reportUserInput(); + } } // Always accelerate disk spindown while in dark wake, @@ -6435,6 +7226,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; case kStimulusDarkWakeEvaluate: + DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); if (systemDarkWake) { flags.bit.evaluateDarkWake = true; @@ -6442,6 +7234,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) break; case kStimulusNoIdleSleepPreventers: + DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg); flags.bit.adjustPowerState = true; break; @@ -6503,6 +7296,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) (kFullWakeReasonDisplayOn == fullWakeReason)) { // kIOPMSleepReasonMaintenance? + DLOG("Display sleep while in notification wake\n"); changePowerStateWithOverrideTo( SLEEP_STATE, kIOPMSleepReasonMaintenance ); } @@ -6525,7 +7319,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) DLOG("user inactive\n"); } - if (!userIsActive && sleepSlider) + if (!userIsActive && idleSleepEnabled) { startIdleSleepTimer(getTimeToIdleSleep()); } @@ -6540,10 +7334,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) if (!wrangler) { changePowerStateToPriv(ON_STATE); - if (idleSeconds) - { - startIdleSleepTimer( idleSeconds ); - } + startIdleSleepTimer( idleSeconds ); } else { @@ -6575,7 +7366,7 @@ void IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) if (!wrangler) { changePowerStateToPriv(ON_STATE); - if (idleSeconds) + if (idleSleepEnabled) { // stay awake for at least idleSeconds startIdleSleepTimer(idleSeconds); @@ -6667,7 +7458,7 @@ void IOPMrootDomain::requestFullWake( FullWakeReason reason ) uint64_t nsec; clock_get_uptime(&now); - SUB_ABSOLUTETIME(&now, &systemWakeTime); + SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); absolutetime_to_nanoseconds(now, &nsec); MSG("full wake %s (reason %u) %u ms\n", promotion ? "promotion" : "request", @@ -6689,6 +7480,8 @@ void IOPMrootDomain::willEnterFullWake( void ) { hibernateRetry = false; sleepToStandby = false; + standbyNixed = false; + resetTimers = false; sleepTimerMaintenance = false; _systemMessageClientMask = kSystemMessageClientPowerd | @@ -6751,8 +7544,20 @@ void IOPMrootDomain::evaluateAssertions(IOPMDriverAssertionType newAssertions, I } } - if (changedBits & kIOPMDriverAssertionCPUBit) + if (changedBits & kIOPMDriverAssertionCPUBit) { evaluatePolicy(kStimulusDarkWakeEvaluate); + if (!assertOnWakeSecs && gIOLastWakeAbsTime) { + AbsoluteTime now; + clock_usec_t microsecs; + clock_get_uptime(&now); + SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); + absolutetime_to_microtime(now, &assertOnWakeSecs, µsecs); + if (assertOnWakeReport) { + HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); + DLOG("Updated assertOnWake %lu\n", (unsigned long)assertOnWakeSecs); + } + } + } if (changedBits & kIOPMDriverAssertionReservedBit7) { bool value = (newAssertions & kIOPMDriverAssertionReservedBit7) ? true : false; @@ -6767,6 +7572,22 @@ void IOPMrootDomain::evaluateAssertions(IOPMDriverAssertionType newAssertions, I } } +void IOPMrootDomain::evaluateWranglerAssertions() +{ + if (gIOPMWorkLoop->inGate() == false) { + gIOPMWorkLoop->runAction( + OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::evaluateWranglerAssertions), + (OSObject *)this); + + return; + } + + if (pmAssertions->getActivatedAssertions() & kIOPMDriverAssertionPreventDisplaySleepBit) { + DLOG("wrangler setIgnoreIdleTimer\(1) on matching\n"); + wrangler->setIgnoreIdleTimer( true ); + } +} + // MARK: - // MARK: Statistics @@ -6829,7 +7650,7 @@ void IOPMrootDomain::pmStatsRecordApplicationResponse( const char *name, int messageType, uint32_t delay_ms, - int app_pid, + uint64_t id, OSObject *object, IOPMPowerStateIndex powerState) { @@ -6844,23 +7665,37 @@ void IOPMrootDomain::pmStatsRecordApplicationResponse( if (object && (notify = OSDynamicCast(IOPMServiceInterestNotifier, object))) { - if (response->isEqualTo(gIOPMStatsApplicationResponseTimedOut)) + if (response->isEqualTo(gIOPMStatsResponseTimedOut)) notify->ackTimeoutCnt++; else notify->ackTimeoutCnt = 0; } - if (response->isEqualTo(gIOPMStatsApplicationResponsePrompt) || + if (response->isEqualTo(gIOPMStatsResponsePrompt) || (_systemTransitionType == kSystemTransitionNone) || (_systemTransitionType == kSystemTransitionNewCapClient)) return; - responseDescription = OSDictionary::withCapacity(5); - if (responseDescription) - { - if (response) { - responseDescription->setObject(_statsResponseTypeKey, response); + if (response->isEqualTo(gIOPMStatsDriverPSChangeSlow)) { + kdebugTrace(kPMLogDrvPSChangeDelay, id, messageType, delay_ms); + } + else if (notify) { + // User space app or kernel capability client + if (id) { + kdebugTrace(kPMLogAppResponseDelay, id, notify->msgType, delay_ms); + } + else { + kdebugTrace(kPMLogDrvResponseDelay, notify->uuid0, messageType, delay_ms); + } + notify->msgType = 0; + } + + responseDescription = OSDictionary::withCapacity(5); + if (responseDescription) + { + if (response) { + responseDescription->setObject(_statsResponseTypeKey, response); } msgNum = OSNumber::withNumber(messageType, 32); @@ -6869,6 +7704,10 @@ void IOPMrootDomain::pmStatsRecordApplicationResponse( msgNum->release(); } + if (!name && notify && notify->identifier) { + name = notify->identifier->getCStringNoCopy(); + } + if (name && (strlen(name) > 0)) { appname = OSSymbol::withCString(name); @@ -6878,8 +7717,11 @@ void IOPMrootDomain::pmStatsRecordApplicationResponse( } } - if (app_pid != -1) { - pidNum = OSNumber::withNumber(app_pid, 32); + if (!id && notify) { + id = notify->uuid0; + } + if (id != 0) { + pidNum = OSNumber::withNumber(id, 64); if (pidNum) { responseDescription->setObject(_statsPIDKey, pidNum); pidNum->release(); @@ -6895,7 +7737,7 @@ void IOPMrootDomain::pmStatsRecordApplicationResponse( if (response->isEqualTo(gIOPMStatsDriverPSChangeSlow)) { powerCaps = OSNumber::withNumber(powerState, 32); -#if !defined(__i386__) && !defined(__x86_64__) +#if !defined(__i386__) && !defined(__x86_64__) && (DEVELOPMENT || DEBUG) IOLog("%s::powerStateChange type(%d) to(%lu) async took %d ms\n", name, messageType, powerState, delay_ms); @@ -6958,6 +7800,8 @@ IOReturn IOPMrootDomain::callPlatformFunction( void * param1, void * param2, void * param3, void * param4 ) { + uint32_t bootFailureCode = 0xffffffff; + unsigned int len = sizeof(bootFailureCode); if (pmTracer && functionName && functionName->isEqualTo(kIOPMRegisterNVRAMTracePointHandlerKey) && !pmTracer->tracePointHandler && !pmTracer->tracePointTarget) @@ -6969,9 +7813,15 @@ IOReturn IOPMrootDomain::callPlatformFunction( pmTracer->tracePointTarget = (void *) param2; tracePointPCI = (uint32_t)(uintptr_t) param3; tracePointPhases = (uint32_t)(uintptr_t) param4; + if ((tracePointPhases & 0xff) == kIOPMTracePointSystemSleep) { + if (!PEReadNVRAMProperty(kIOEFIBootRomFailureKey, &bootFailureCode, &len)) { + MSG("Failed to read failure code from NVRam\n"); + } + // Failure code from EFI/BootRom is a four byte structure + tracePointPCI = OSSwapBigToHostInt32(bootFailureCode); + } statusCode = (((uint64_t)tracePointPCI) << 32) | tracePointPhases; - if ((tracePointPhases >> 24) != kIOPMTracePointSystemUp) - { + if ((tracePointPhases & 0xff) != kIOPMTracePointSystemUp) { MSG("Sleep failure code 0x%08x 0x%08x\n", tracePointPCI, tracePointPhases); } @@ -6999,6 +7849,18 @@ IOReturn IOPMrootDomain::callPlatformFunction( functionName, waitForFunction, param1, param2, param3, param4); } +void IOPMrootDomain::kdebugTrace(uint32_t event, uint64_t id, + uintptr_t param1, uintptr_t param2, uintptr_t param3) +{ + uint32_t code = IODBG_POWER(event); + uint64_t regId = id; + if (regId == 0) { + regId = getRegistryEntryID(); + } + IOTimeStampConstant(code, (uintptr_t) regId, param1, param2, param3); +} + + void IOPMrootDomain::tracePoint( uint8_t point ) { if (systemBooting) return; @@ -7006,45 +7868,196 @@ void IOPMrootDomain::tracePoint( uint8_t point ) if (kIOPMTracePointWakeCapabilityClients == point) acceptSystemWakeEvents(false); - PMDebug(kPMLogSleepWakeTracePoint, point, 0); + kdebugTrace(kPMLogSleepWakeTracePoint, 0, point, 0); pmTracer->tracePoint(point); } -void IOPMrootDomain::tracePoint( uint8_t point, uint8_t data ) +void IOPMrootDomain::traceDetail(OSObject *object) { - if (systemBooting) return; + IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); + if (!notifier) { + DLOG("Unknown notifier\n"); + return; + } + + if (!systemBooting) { + pmTracer->traceDetail( notifier->uuid0 >> 32 ); + kdebugTrace(kPMLogSleepWakeMessage, pmTracer->getTracePhase(), notifier->msgType, notifier->uuid0, notifier->uuid1); + if (notifier->identifier) { + DLOG("trace point 0x%02x msg 0x%x to %s\n", pmTracer->getTracePhase(), notifier->msgType, + notifier->identifier->getCStringNoCopy()); + } + else { + DLOG("trace point 0x%02x msg 0x%x\n", pmTracer->getTracePhase(), notifier->msgType); + } + } - PMDebug(kPMLogSleepWakeTracePoint, point, data); - pmTracer->tracePoint(point, data); } -void IOPMrootDomain::traceDetail( uint32_t detail ) + +void IOPMrootDomain::traceAckDelay(OSObject *object, uint32_t response, uint32_t delay_ms) +{ + IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); + if (!notifier) { + DLOG("Unknown notifier\n"); + return; + } + + if (!systemBooting) { + kdebugTrace(kPMLogDrvResponseDelay, notifier->uuid0, notifier->uuid1, response, delay_ms); + if (notifier->identifier) { + DLOG("Response from %s took %d ms(response:%d)\n", + notifier->identifier->getCStringNoCopy(), delay_ms, response); + } + else { + DLOG("Response from kext UUID %llx-%llx took %d ms(response:%d)\n", + notifier->uuid0, notifier->uuid1, delay_ms, response); + } + } +} + +void IOPMrootDomain::traceDetail(uint32_t msgType, uint32_t msgIndex, uint32_t delay) { - if (!systemBooting) + if (!systemBooting) { + uint32_t detail = ((msgType & 0xffff) << 16) | (delay & 0xffff); pmTracer->traceDetail( detail ); + kdebugTrace(kPMLogSleepWakeTracePoint, pmTracer->getTracePhase(), msgType, delay); + DLOG("trace point 0x%02x msgType 0x%x detail 0x%08x\n", pmTracer->getTracePhase(), msgType, delay); + } } +void IOPMrootDomain::configureReportGated(uint64_t channel_id, uint64_t action, void *result) +{ + size_t reportSize; + void **report = NULL; + uint32_t bktCnt; + uint32_t bktSize; + uint32_t *clientCnt; + + ASSERT_GATED(); + + report = NULL; + if (channel_id == kAssertDelayChID) { + report = &assertOnWakeReport; + bktCnt = kAssertDelayBcktCnt; + bktSize = kAssertDelayBcktSize; + clientCnt = &assertOnWakeClientCnt; + } + else if (channel_id == kSleepDelaysChID) { + report = &sleepDelaysReport; + bktCnt = kSleepDelaysBcktCnt; + bktSize = kSleepDelaysBcktSize; + clientCnt = &sleepDelaysClientCnt; + } + + switch (action) + { + case kIOReportEnable: + + if (*report) { + (*clientCnt)++; + break; + } + + reportSize = HISTREPORT_BUFSIZE(bktCnt); + *report = IOMalloc(reportSize); + if (*report == NULL) { + break; + } + bzero(*report, reportSize); + HISTREPORT_INIT(bktCnt, bktSize, *report, reportSize, + getRegistryEntryID(), channel_id, kIOReportCategoryPower); + + if (channel_id == kAssertDelayChID) + assertOnWakeSecs = 0; + + break; + + case kIOReportDisable: + if (*clientCnt == 0) { + break; + } + if (*clientCnt == 1) + { + IOFree(*report, HISTREPORT_BUFSIZE(bktCnt)); + *report = NULL; + } + (*clientCnt)--; + + if (channel_id == kAssertDelayChID) + assertOnWakeSecs = -1; // Invalid value to prevent updates + + break; + + case kIOReportGetDimensions: + if (*report) { + HISTREPORT_UPDATERES(*report, kIOReportGetDimensions, result); + } + break; + } + + return; +} + IOReturn IOPMrootDomain::configureReport(IOReportChannelList *channelList, IOReportConfigureAction action, void *result, void *destination) { unsigned cnt; - if (action != kIOReportGetDimensions) goto exit; + uint64_t configAction = (uint64_t)action; for (cnt = 0; cnt < channelList->nchannels; cnt++) { if ( (channelList->channels[cnt].channel_id == kSleepCntChID) || (channelList->channels[cnt].channel_id == kDarkWkCntChID) || (channelList->channels[cnt].channel_id == kUserWkCntChID) ) { + if (action != kIOReportGetDimensions) continue; SIMPLEREPORT_UPDATERES(kIOReportGetDimensions, result); } + else if ((channelList->channels[cnt].channel_id == kAssertDelayChID) || + (channelList->channels[cnt].channel_id == kSleepDelaysChID)) { + gIOPMWorkLoop->runAction( + OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::configureReportGated), + (OSObject *)this, (void *)channelList->channels[cnt].channel_id, + (void *)configAction, (void *)result); + } } -exit: return super::configureReport(channelList, action, result, destination); } +IOReturn IOPMrootDomain::updateReportGated(uint64_t ch_id, void *result, IOBufferMemoryDescriptor *dest) +{ + + uint32_t size2cpy; + void *data2cpy; + void **report; + + ASSERT_GATED(); + + report = NULL; + if (ch_id == kAssertDelayChID) { + report = &assertOnWakeReport; + } + else if (ch_id == kSleepDelaysChID) { + report = &sleepDelaysReport; + } + + if (*report == NULL) { + return kIOReturnNotOpen; + } + + HISTREPORT_UPDATEPREP(*report, data2cpy, size2cpy); + if (size2cpy > (dest->getCapacity() - dest->getLength()) ) { + return kIOReturnOverrun; + } + + HISTREPORT_UPDATERES(*report, kIOReportCopyChannelData, result); + dest->appendBytes(data2cpy, size2cpy); + + return kIOReturnSuccess; +} IOReturn IOPMrootDomain::updateReport(IOReportChannelList *channelList, IOReportUpdateAction action, @@ -7063,7 +8076,15 @@ IOReturn IOPMrootDomain::updateReport(IOReportChannelList *channelList, for (cnt = 0; cnt < channelList->nchannels; cnt++) { ch_id = channelList->channels[cnt].channel_id ; - if ((ch_id == kSleepCntChID) || + if ((ch_id == kAssertDelayChID) || (ch_id == kSleepDelaysChID)) { + gIOPMWorkLoop->runAction( + OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::updateReportGated), + (OSObject *)this, (void *)ch_id, + (void *)result, (void *)dest); + continue; + + } + else if ((ch_id == kSleepCntChID) || (ch_id == kDarkWkCntChID) || (ch_id == kUserWkCntChID)) { SIMPLEREPORT_INIT(buf, sizeof(buf), getRegistryEntryID(), ch_id, kIOReportCategoryPower); } @@ -7115,10 +8136,12 @@ PMTraceWorker *PMTraceWorker::tracer(IOPMrootDomain *owner) // this dictionary lazily. me->owner = owner; me->pciDeviceBitMappings = NULL; - me->pciMappingLock = IOLockAlloc(); + me->pmTraceWorkerLock = IOLockAlloc(); me->tracePhase = kIOPMTracePointSystemUp; - me->loginWindowPhase = 0; me->traceData32 = 0; + me->loginWindowData = 0; + me->coreDisplayData = 0; + me->coreGraphicsData = 0; return me; } @@ -7128,8 +8151,10 @@ void PMTraceWorker::RTC_TRACE(void) { uint32_t wordA; - wordA = (tracePhase << 24) | (loginWindowPhase << 16) | - (traceData8 << 8); + IOLockLock(pmTraceWorkerLock); + wordA = (loginWindowData << 24) | (coreDisplayData << 16) | + (coreGraphicsData << 8) | tracePhase; + IOLockUnlock(pmTraceWorkerLock); tracePointHandler( tracePointTarget, traceData32, wordA ); _LOG("RTC_TRACE wrote 0x%08x 0x%08x\n", traceData32, wordA); @@ -7141,7 +8166,7 @@ int PMTraceWorker::recordTopLevelPCIDevice(IOService * pciDevice) const OSSymbol * deviceName; int index = -1; - IOLockLock(pciMappingLock); + IOLockLock(pmTraceWorkerLock); if (!pciDeviceBitMappings) { @@ -7168,7 +8193,7 @@ int PMTraceWorker::recordTopLevelPCIDevice(IOService * pciDevice) addedToRegistry = owner->setProperty("PCITopLevel", this); exit: - IOLockUnlock(pciMappingLock); + IOLockUnlock(pmTraceWorkerLock); return index; } @@ -7177,9 +8202,9 @@ bool PMTraceWorker::serialize(OSSerialize *s) const bool ok = false; if (pciDeviceBitMappings) { - IOLockLock(pciMappingLock); + IOLockLock(pmTraceWorkerLock); ok = pciDeviceBitMappings->serialize(s); - IOLockUnlock(pciMappingLock); + IOLockUnlock(pmTraceWorkerLock); } return ok; } @@ -7196,35 +8221,32 @@ void PMTraceWorker::tracePoint(uint8_t phase) RTC_TRACE(); } -void PMTraceWorker::tracePoint(uint8_t phase, uint8_t data8) -{ - // clear trace detail when phase begins - if (tracePhase != phase) - traceData32 = 0; - - tracePhase = phase; - traceData8 = data8; - - DLOG("trace point 0x%02x 0x%02x\n", tracePhase, traceData8); - RTC_TRACE(); -} - void PMTraceWorker::traceDetail(uint32_t detail) { - if (kIOPMTracePointSleepPriorityClients != tracePhase) + if (detail == traceData32) { return; - + } traceData32 = detail; - DLOG("trace point 0x%02x detail 0x%08x\n", tracePhase, traceData32); - RTC_TRACE(); } -void PMTraceWorker::traceLoginWindowPhase(uint8_t phase) +void PMTraceWorker::traceComponentWakeProgress(uint32_t component, uint32_t data) { - loginWindowPhase = phase; - - DLOG("loginwindow tracepoint 0x%02x\n", loginWindowPhase); + switch (component) { + case kIOPMLoginWindowProgress: + loginWindowData = data & kIOPMLoginWindowProgressMask; + break; + case kIOPMCoreDisplayProgress: + coreDisplayData = data & kIOPMCoreDisplayProgressMask; + break; + case kIOPMCoreGraphicsProgress: + coreGraphicsData = data & kIOPMCoreGraphicsProgressMask; + break; + default: + return; + } + + DLOG("component trace point 0x%02x data 0x%08x\n", component, data); RTC_TRACE(); } @@ -7257,12 +8279,14 @@ void PMTraceWorker::tracePCIPowerChange( traceData32 |= bitMask; _LOG("PMTrace: Device %s started - bit %2d mask 0x%08x => 0x%08x\n", service->getName(), bitNum, bitMask, traceData32); + owner->kdebugTrace(kPMLogPCIDevChangeStart, service->getRegistryEntryID(), traceData32, 0); } else { traceData32 &= ~bitMask; _LOG("PMTrace: Device %s finished - bit %2d mask 0x%08x => 0x%08x\n", service->getName(), bitNum, bitMask, traceData32); + owner->kdebugTrace(kPMLogPCIDevChangeDone, service->getRegistryEntryID(), traceData32, 0); } DLOG("trace point 0x%02x detail 0x%08x\n", tracePhase, traceData32); @@ -7272,11 +8296,20 @@ void PMTraceWorker::tracePCIPowerChange( uint64_t PMTraceWorker::getPMStatusCode( ) { - return (((uint64_t)traceData32 << 32) | (tracePhase << 24) | - (loginWindowPhase << 16) | (traceData8 << 8)); + return (((uint64_t)traceData32 << 32) | ((uint64_t)tracePhase)); } +uint8_t PMTraceWorker::getTracePhase() +{ + return tracePhase; +} + +uint32_t PMTraceWorker::getTraceData() +{ + return traceData32; +} + // MARK: - // MARK: PMHaltWorker @@ -7285,14 +8318,6 @@ uint64_t PMTraceWorker::getPMStatusCode( ) // //****************************************************************************** -static unsigned int gPMHaltBusyCount; -static unsigned int gPMHaltIdleCount; -static int gPMHaltDepth; -static unsigned long gPMHaltEvent; -static IOLock * gPMHaltLock = 0; -static OSArray * gPMHaltArray = 0; -static const OSSymbol * gPMHaltClientAcknowledgeKey = 0; - PMHaltWorker * PMHaltWorker::worker( void ) { PMHaltWorker * me; @@ -7379,7 +8404,7 @@ void PMHaltWorker::work( PMHaltWorker * me ) { IOService * service; OSSet * inner; - AbsoluteTime startTime; + AbsoluteTime startTime, elapsedTime; UInt32 deltaTime; bool timeout; @@ -7393,7 +8418,7 @@ void PMHaltWorker::work( PMHaltWorker * me ) inner = (OSSet *)gPMHaltArray->getObject(me->depth); if (inner) { - service = (IOService *)inner->getAnyObject(); + service = OSDynamicCast(IOService, inner->getAnyObject()); if (service) { service->retain(); @@ -7415,7 +8440,7 @@ void PMHaltWorker::work( PMHaltWorker * me ) me->timeout = false; IOLockUnlock(me->lock); - service->systemWillShutdown( gPMHaltEvent ); + service->systemWillShutdown( gPMHaltMessageType ); // Wait for driver acknowledgement IOLockLock(me->lock); @@ -7428,15 +8453,19 @@ void PMHaltWorker::work( PMHaltWorker * me ) IOLockUnlock(me->lock); } - deltaTime = computeDeltaTimeMS(&startTime); + deltaTime = computeDeltaTimeMS(&startTime, &elapsedTime); if ((deltaTime > kPMHaltTimeoutMS) || timeout || (gIOKitDebug & kIOLogPMRootDomain)) { - LOG("%s driver %s (%p) took %u ms\n", - (gPMHaltEvent == kIOMessageSystemWillPowerOff) ? + LOG("%s driver %s (0x%llx) took %u ms\n", + (gPMHaltMessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : "Restart", - service->getName(), OBFUSCATE(service), + service->getName(), service->getRegistryEntryID(), (uint32_t) deltaTime ); + halt_log_enter( + (gPMHaltMessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : "Restart", + OSMemberFunctionCast(const void *, service, &IOService::systemWillShutdown), + elapsedTime); } service->release(); @@ -7466,7 +8495,7 @@ void PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now ) { me->timeout = true; MSG("%s still waiting on %s\n", - (gPMHaltEvent == kIOMessageSystemWillPowerOff) ? + (gPMHaltMessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : "Restart", me->service->getName()); } @@ -7474,7 +8503,6 @@ void PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now ) IOLockUnlock(me->lock); } - //****************************************************************************** // acknowledgeSystemWillShutdown // @@ -7515,7 +8543,7 @@ void IOPMrootDomain::acknowledgeSystemWillShutdown( IOService * from ) //****************************************************************************** static void -notifySystemShutdown( IOService * root, unsigned long event ) +notifySystemShutdown( IOService * root, uint32_t messageType ) { #define PLACEHOLDER ((OSSet *)gPMHaltArray) IORegistryIterator * iter; @@ -7533,7 +8561,7 @@ notifySystemShutdown( IOService * root, unsigned long event ) void * baseFunc; bool ok; - DLOG("%s event = %lx\n", __FUNCTION__, event); + DLOG("%s msgType = 0x%x\n", __FUNCTION__, messageType); baseFunc = OSMemberFunctionCast(void *, root, &IOService::systemWillShutdown); @@ -7567,7 +8595,7 @@ notifySystemShutdown( IOService * root, unsigned long event ) if (!gPMHaltClientAcknowledgeKey) goto done; } - gPMHaltEvent = event; + gPMHaltMessageType = messageType; // Depth-first walk of PM plane @@ -7797,18 +8825,18 @@ OSObject * IOPMrootDomain::copyProperty( const char * aKey) const if (!strncmp(aKey, kIOPMSleepWakeWdogRebootKey, sizeof(kIOPMSleepWakeWdogRebootKey))) { if (swd_flags & SWD_BOOT_BY_SW_WDOG) - return OSBoolean::withBoolean(true); + return kOSBooleanTrue; else - return OSBoolean::withBoolean(false); + return kOSBooleanFalse; } if (!strncmp(aKey, kIOPMSleepWakeWdogLogsValidKey, sizeof(kIOPMSleepWakeWdogLogsValidKey))) { if (swd_flags & SWD_VALID_LOGS) - return OSBoolean::withBoolean(true); + return kOSBooleanTrue; else - return OSBoolean::withBoolean(false); + return kOSBooleanFalse; } @@ -7819,16 +8847,16 @@ OSObject * IOPMrootDomain::copyProperty( const char * aKey) const */ if (!strcmp(aKey, "DesktopMode")) { if (desktopMode) - return OSBoolean::withBoolean(true); + return kOSBooleanTrue; else - return OSBoolean::withBoolean(false); + return kOSBooleanFalse; } if (!strcmp(aKey, "DisplayIdleForDemandSleep")) { if (displayIdleForDemandSleep) { - return OSBoolean::withBoolean(true); + return kOSBooleanTrue; } else { - return OSBoolean::withBoolean(false); + return kOSBooleanFalse; } } @@ -7836,8 +8864,12 @@ OSObject * IOPMrootDomain::copyProperty( const char * aKey) const { OSArray * array = 0; WAKEEVENT_LOCK(); - if (_systemWakeEventsArray && _systemWakeEventsArray->getCount()) - array = OSArray::withArray(_systemWakeEventsArray); + if (_systemWakeEventsArray && _systemWakeEventsArray->getCount()) { + OSCollection *collection = _systemWakeEventsArray->copyCollection(); + if (collection && !(array = OSDynamicCast(OSArray, collection))) { + collection->release(); + } + } WAKEEVENT_UNLOCK(); return array; } @@ -7847,13 +8879,30 @@ OSObject * IOPMrootDomain::copyProperty( const char * aKey) const OSArray * array = 0; IOLockLock(pmStatsLock); if (pmStatsAppResponses && pmStatsAppResponses->getCount()) { - array = OSArray::withArray(pmStatsAppResponses); + OSCollection *collection = pmStatsAppResponses->copyCollection(); + if (collection && !(array = OSDynamicCast(OSArray, collection))) { + collection->release(); + } pmStatsAppResponses->flushCollection(); } IOLockUnlock(pmStatsLock); return array; } + if (!strcmp(aKey, kIOPMIdleSleepPreventersKey)) + { + OSArray *idleSleepList = NULL; + gRootDomain->copySleepPreventersList(&idleSleepList, NULL); + return idleSleepList; + } + + if (!strcmp(aKey, kIOPMSystemSleepPreventersKey)) + { + OSArray *systemSleepList = NULL; + gRootDomain->copySleepPreventersList(NULL, &systemSleepList); + return systemSleepList; + } + return NULL; } @@ -7889,6 +8938,29 @@ void IOPMrootDomain::acceptSystemWakeEvents( bool accept ) else { _acceptSystemWakeEvents = false; +#if CONFIG_EMBEDDED + logWakeReason = gWakeReasonSysctlRegistered; +#if DEVELOPMENT + static int panic_allowed = -1; + + if ((panic_allowed == -1) && + (PE_parse_boot_argn("swd_wakereason_panic", &panic_allowed, sizeof(panic_allowed)) == false)) { + panic_allowed = 1; + } + + if (panic_allowed) { + size_t i = 0; + // Panic if wake reason is null or empty + for (i = 0; (i < strlen(gWakeReasonString)); i++) { + if ((gWakeReasonString[i] != ' ') && (gWakeReasonString[i] != '\t')) + break; + } + if (i >= strlen(gWakeReasonString)) { + panic("Wake reason is empty\n"); + } + } +#endif +#endif } WAKEEVENT_UNLOCK(); @@ -7944,6 +9016,9 @@ void IOPMrootDomain::claimSystemWakeEvent( // Lazy registration until the platform driver stops registering // the same name. gWakeReasonSysctlRegistered = true; +#if CONFIG_EMBEDDED + sysctl_register_oid(&sysctl__kern_wakereason); +#endif } if (_acceptSystemWakeEvents) { @@ -8578,50 +9653,67 @@ IOReturn IOPMrootDomain::restartWithStackshot() if ((swd_flags & SWD_WDOG_ENABLED) == 0) return kIOReturnError; - takeStackshot(true, true); + takeStackshot(true, true, false); return kIOReturnSuccess; } void IOPMrootDomain::sleepWakeDebugTrig(bool wdogTrigger) { - takeStackshot(wdogTrigger, false); + takeStackshot(wdogTrigger, false, false); } -void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog) +void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog, bool isSpinDump) { swd_hdr * hdr = NULL; addr64_t data[3]; - uint32_t wdog_panic = 0; + int wdog_panic = -1; + int stress_rack = -1; int cnt = 0; pid_t pid = 0; + kern_return_t kr = KERN_SUCCESS; uint32_t flags; char * dstAddr; uint32_t size; uint32_t bytesRemaining; + unsigned bytesWritten = 0; + unsigned totalBytes = 0; unsigned int len; OSString * UUIDstring = NULL; uint64_t code; IOMemoryMap * logBufMap = NULL; - swd_stackshot_hdr *stackshotHdr = NULL; - if ( kIOSleepWakeWdogOff & gIOKitDebug ) - return; + + uint32_t bufSize; + uint32_t initialStackSize; + + if (isSpinDump) { + if (_systemTransitionType != kSystemTransitionSleep && + _systemTransitionType != kSystemTransitionWake) + return; + } else { + if ( kIOSleepWakeWdogOff & gIOKitDebug ) + return; + } if (wdogTrigger) { - if (PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic)) && - (wdog_panic == 1)) { - // If boot-arg is set to panic on sleep/wake hang, call panic - panic("Sleep/Wake hang detected\n"); + PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic)); + PE_parse_boot_argn("stress-rack", &stress_rack, sizeof(stress_rack)); + if ((wdog_panic == 1) || (stress_rack == 1)) { + // If boot-arg specifies to panic then panic. + panic("Sleep/Wake hang detected"); return; } else if (swd_flags & SWD_BOOT_BY_SW_WDOG) { // If current boot is due to this watch dog trigger restart in previous boot, // then don't trigger again until at least 1 successful sleep & wake. - sleepCnt = displayWakeCnt = 1; - if (!(sleepCnt && displayWakeCnt)) { + if (!(sleepCnt && (displayWakeCnt || darkWakeCnt))) { IOLog("Shutting down due to repeated Sleep/Wake failures\n"); + if (!tasksSuspended) { + tasksSuspended = TRUE; + tasks_system_suspend(true); + } PEHaltRestart(kPEHaltCPU); return; } @@ -8629,19 +9721,39 @@ void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog) } - if (sleepWakeDebugIsWdogEnabled() == false) - return; + if (isSpinDump) { + if (gSpinDumpBufferFull) + return; + if (swd_spindump_buffer == NULL) { + sleepWakeDebugSpinDumpMemAlloc(); + if (swd_spindump_buffer == NULL) return; + } + + bufSize = SWD_SPINDUMP_SIZE; + initialStackSize = SWD_INITIAL_SPINDUMP_SIZE; + } else { + if (sleepWakeDebugIsWdogEnabled() == false) + return; + + if (swd_buffer == NULL) { + sleepWakeDebugMemAlloc(); + if (swd_buffer == NULL) return; + } - if (swd_buffer == NULL) { - sleepWakeDebugMemAlloc(); - if (swd_buffer == NULL) return; + bufSize = SWD_BUF_SIZE; + initialStackSize = SWD_INITIAL_STACK_SIZE; } if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) return; + if (isSpinDump) { + hdr = (swd_hdr *)swd_spindump_buffer; + } + else { + hdr = (swd_hdr *)swd_buffer; + } - hdr = (swd_hdr *)swd_buffer; memset(hdr->UUID, 0x20, sizeof(hdr->UUID)); if ((UUIDstring = OSDynamicCast(OSString, getProperty(kIOPMSleepWakeUUIDKey))) != NULL ) { @@ -8656,63 +9768,72 @@ void IOPMrootDomain::takeStackshot(bool wdogTrigger, bool isOSXWatchdog) } dstAddr = (char*)hdr + hdr->spindump_offset; - bytesRemaining = SWD_BUF_SIZE - hdr->spindump_offset; + bytesRemaining = bufSize - hdr->spindump_offset; /* if AppleOSXWatchdog triggered the stackshot, set the flag in the heaer */ hdr->is_osx_watchdog = isOSXWatchdog; DLOG("Taking snapshot. bytesRemaining: %d\n", bytesRemaining); - while (bytesRemaining > sizeof(swd_stackshot_hdr)) { - - stackshotHdr = (swd_stackshot_hdr *)dstAddr; - stackshotHdr->magic = SWD_STACKSHOTHDR_MAGIC; - stackshotHdr->size = 0; - bytesRemaining -= sizeof(swd_stackshot_hdr); - dstAddr += sizeof(swd_stackshot_hdr); + flags = STACKSHOT_KCDATA_FORMAT|STACKSHOT_NO_IO_STATS|STACKSHOT_SAVE_KEXT_LOADINFO; + while (kr == KERN_SUCCESS) { - if (isOSXWatchdog) { - pid = -1; - size = bytesRemaining; - flags = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO; - } - else if (cnt == 0) { - /* + if (cnt == 0) { + /* * Take stackshot of all process on first sample. Size is restricted * to SWD_INITIAL_STACK_SIZE */ pid = -1; - size = (bytesRemaining > SWD_INITIAL_STACK_SIZE) ? SWD_INITIAL_STACK_SIZE : bytesRemaining; - flags = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO|STACKSHOT_SAVE_KERNEL_FRAMES_ONLY; + size = (bytesRemaining > initialStackSize) ? initialStackSize : bytesRemaining; + flags |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY; } else { /* Take sample of kernel threads only */ pid = 0; size = bytesRemaining; - flags = 0; } - stack_snapshot_from_kernel(pid, dstAddr, size, flags, &stackshotHdr->size); + kr = stack_snapshot_from_kernel(pid, dstAddr, size, flags, 0, &bytesWritten); + DLOG("stack_snapshot_from_kernel returned 0x%x. pid: %d bufsize:0x%x flags:0x%x bytesWritten: %d\n", + kr, pid, size, flags, bytesWritten); + if (kr == KERN_INSUFFICIENT_BUFFER_SIZE) { + if (pid == -1) { + // Insufficient buffer when trying to take stackshot of user & kernel space threads. + // Continue to take stackshot of just kernel threads + ++cnt; + kr = KERN_SUCCESS; + continue; + } + else if (totalBytes == 0) { + MSG("Failed to get stackshot(0x%x) bufsize:0x%x flags:0x%x\n", kr, size, flags); + } + } - dstAddr += stackshotHdr->size; - bytesRemaining -= stackshotHdr->size; + dstAddr += bytesWritten; + totalBytes += bytesWritten; + bytesRemaining -= bytesWritten; - DLOG("Sample: %d size: %d bytesRemaining: %d\n", cnt, stackshotHdr->size, bytesRemaining); - if ((stackshotHdr->size == 0) || (++cnt == 10)) + if (++cnt == 10) { break; + } IOSleep(10); // 10 ms } - hdr->spindump_size = (SWD_BUF_SIZE - bytesRemaining - hdr->spindump_offset); + hdr->spindump_size = (bufSize - bytesRemaining - hdr->spindump_offset); - memset(hdr->cps, 0x20, sizeof(hdr->cps)); - snprintf(hdr->cps, sizeof(hdr->cps), "\ncps: %d", ((IOService*)this)->getPowerState()); + memset(hdr->spindump_status, 0x20, sizeof(hdr->spindump_status)); code = pmTracer->getPMStatusCode(); memset(hdr->PMStatusCode, 0x20, sizeof(hdr->PMStatusCode)); snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: %08x %08x", (uint32_t)((code >> 32) & 0xffffffff), (uint32_t)(code & 0xffffffff)); memset(hdr->reason, 0x20, sizeof(hdr->reason)); + if (isSpinDump) { + snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: PSC Delay\n\n"); + gRootDomain->swd_lock = 0; + gSpinDumpBufferFull = true; + return; + } snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n"); @@ -8735,8 +9856,13 @@ exit: gRootDomain->swd_lock = 0; if (wdogTrigger) { - IOLog("Restarting to collect Sleep wake debug logs\n"); - PEHaltRestart(kPERestartCPU); + IOLog("Restarting to collect Sleep wake debug logs\n"); + if (!tasksSuspended) { + tasksSuspended = TRUE; + tasks_system_suspend(true); + } + + PEHaltRestart(kPERestartCPU); } else { logBufMap = sleepWakeDebugRetrieve(); @@ -8788,12 +9914,48 @@ void IOPMrootDomain::sleepWakeDebugMemAlloc( ) hdr->spindump_offset = sizeof(swd_hdr); swd_buffer = (void *)hdr; + swd_memDesc = memDesc; DLOG("SleepWake debug buffer size:0x%x spindump offset:0x%x\n", hdr->alloc_size, hdr->spindump_offset); exit: gRootDomain->swd_lock = 0; } +void IOPMrootDomain::sleepWakeDebugSpinDumpMemAlloc( ) +{ + vm_size_t size = SWD_SPINDUMP_SIZE; + + swd_hdr *hdr = NULL; + + IOBufferMemoryDescriptor *memDesc = NULL; + + if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) + return; + + memDesc = IOBufferMemoryDescriptor::inTaskWithOptions( + kernel_task, kIODirectionIn|kIOMemoryMapperNone, + SWD_SPINDUMP_SIZE); + + if (memDesc == NULL) + { + DLOG("Failed to allocate Memory descriptor for sleepWake debug spindump\n"); + goto exit; + } + + + hdr = (swd_hdr *)memDesc->getBytesNoCopy(); + memset(hdr, 0, sizeof(swd_hdr)); + + hdr->signature = SWD_HDR_SIGNATURE; + hdr->alloc_size = size; + + hdr->spindump_offset = sizeof(swd_hdr); + swd_spindump_buffer = (void *)hdr; + +exit: + gRootDomain->swd_lock = 0; +} + void IOPMrootDomain::sleepWakeDebugEnableWdog() { swd_flags |= SWD_WDOG_ENABLED; @@ -8804,7 +9966,28 @@ void IOPMrootDomain::sleepWakeDebugEnableWdog() bool IOPMrootDomain::sleepWakeDebugIsWdogEnabled() { return ((swd_flags & SWD_WDOG_ENABLED) && - !systemBooting && !systemShutdown); + !systemBooting && !systemShutdown && !gWillShutdown); +} + +void IOPMrootDomain::sleepWakeDebugSaveSpinDumpFile() +{ + swd_hdr *hdr = NULL; + errno_t error = EIO; + + if (swd_spindump_buffer && gSpinDumpBufferFull) { + hdr = (swd_hdr *)swd_spindump_buffer; + + error = sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayStacks.dump", + (char*)hdr+hdr->spindump_offset, hdr->spindump_size); + + if (error) return; + + sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayLog.dump", + (char*)hdr+offsetof(swd_hdr, UUID), + sizeof(swd_hdr)-offsetof(swd_hdr, UUID)); + + gSpinDumpBufferFull = false; + } } errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len) @@ -8819,6 +10002,7 @@ errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0) { IOLog("Failed to open the file %s\n", name); + swd_flags |= SWD_FILEOP_ERROR; goto exit; } VATTR_INIT(&va); @@ -8827,6 +10011,7 @@ errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int if (vp->v_type != VREG || vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) { IOLog("Bailing as this is not a regular file\n"); + swd_flags |= SWD_FILEOP_ERROR; goto exit; } VATTR_INIT(&va); @@ -8834,12 +10019,17 @@ errno_t IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int vnode_setattr(vp, &va, ctx); - error = vn_rdwr(UIO_WRITE, vp, buf, len, 0, - UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, vfs_context_proc(ctx)); - if (error != 0) - IOLog("Failed to save sleep wake log. err 0x%x\n", error); - else - DLOG("Saved %d bytes to file %s\n",len, name); + if (buf != NULL) { + error = vn_rdwr(UIO_WRITE, vp, buf, len, 0, + UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, vfs_context_proc(ctx)); + if (error != 0) { + IOLog("Failed to save sleep wake log. err 0x%x\n", error); + swd_flags |= SWD_FILEOP_ERROR; + } + else { + DLOG("Saved %d bytes to file %s\n",len, name); + } + } exit: if (vp) vnode_close(vp, FWRITE, ctx); @@ -8869,7 +10059,8 @@ errno_t IOPMrootDomain::sleepWakeDebugCopyFile( if (vnode_open(dstFname, (O_CREAT | FWRITE | O_NOFOLLOW), S_IRUSR|S_IRGRP|S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0) { - DLOG("Failed to open the file %s\n", dstFname); + IOLog("Failed to open the file %s\n", dstFname); + swd_flags |= SWD_FILEOP_ERROR; goto exit; } VATTR_INIT(&va); @@ -8877,7 +10068,8 @@ errno_t IOPMrootDomain::sleepWakeDebugCopyFile( /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) { - DLOG("Bailing as this is not a regular file\n"); + IOLog("Bailing as this is not a regular file\n"); + swd_flags |= SWD_FILEOP_ERROR; goto exit; } VATTR_INIT(&va); @@ -8889,13 +10081,14 @@ errno_t IOPMrootDomain::sleepWakeDebugCopyFile( bytesToRead = (round_page(numBytes) > tmpBufSize) ? tmpBufSize : round_page(numBytes); readFileOffset = trunc_page(srcOffset); - DLOG("Read file (numBytes:0x%llx)\n", bytesToRead); + DLOG("Read file (numBytes:0x%llx offset:0x%llx)\n", bytesToRead, readFileOffset); error = vn_rdwr(UIO_READ, srcVp, tmpBuf, bytesToRead, readFileOffset, UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, vfs_context_ucred(srcCtx), (int *) 0, vfs_context_proc(srcCtx)); if (error) { - DLOG("Failed to read file(numBytes:0x%llx)\n", bytesToRead); + IOLog("Failed to read file(numBytes:0x%llx)\n", bytesToRead); + swd_flags |= SWD_FILEOP_ERROR; break; } @@ -8906,12 +10099,14 @@ errno_t IOPMrootDomain::sleepWakeDebugCopyFile( if (crc) { newcrc = crc32(newcrc, (void *)srcDataOffset, bytesToWrite); } + DLOG("Write file (numBytes:0x%llx offset:0x%llx)\n", bytesToWrite, writeFileOffset); error = vn_rdwr(UIO_WRITE, vp, (char *)srcDataOffset, bytesToWrite, writeFileOffset, UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx)); if (error) { - DLOG("Failed to write file(numBytes:0x%llx)\n", bytesToWrite); + IOLog("Failed to write file(numBytes:0x%llx)\n", bytesToWrite); + swd_flags |= SWD_FILEOP_ERROR; break; } @@ -8921,26 +10116,19 @@ errno_t IOPMrootDomain::sleepWakeDebugCopyFile( } if (crc != newcrc) { - swd_stackshot_hdr *shdr = (swd_stackshot_hdr *)tmpBuf;; - - /* Set statckshot size to 0 if crc doesn't match */ - shdr->magic = SWD_STACKSHOTHDR_MAGIC; - shdr->size = 0; + /* Set stackshot size to 0 if crc doesn't match */ + VATTR_INIT(&va); + VATTR_SET(&va, va_data_size, 0); + vnode_setattr(vp, &va, ctx); - assert(tmpBufSize > sizeof(swd_stackshot_hdr)); - bytesToWrite = round_page(sizeof(swd_stackshot_hdr)); - vn_rdwr(UIO_WRITE, vp, (char *)tmpBuf, bytesToWrite, 0, - UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT, - vfs_context_ucred(ctx), (int *) 0, - vfs_context_proc(ctx)); - - DLOG("CRC check failed. expected:0x%x actual:0x%x\n", crc, newcrc); + IOLog("CRC check failed. expected:0x%x actual:0x%x\n", crc, newcrc); + swd_flags |= SWD_DATA_CRC_ERROR; error = EFAULT; } exit: if (vp) { error = vnode_close(vp, FWRITE, ctx); - DLOG("vnode_close returned 0x%x\n", error); + DLOG("vnode_close on file %s returned 0x%x\n",dstFname, error); } if (ctx) vfs_context_rele(ctx); @@ -8948,14 +10136,77 @@ exit: +} +uint32_t IOPMrootDomain::checkForValidDebugData(const char *fname, vfs_context_t *ctx, + void *tmpBuf, struct vnode **vp) +{ + int rc; + uint64_t hdrOffset; + uint32_t error = 0; + + struct vnode_attr va; + IOHibernateImageHeader *imageHdr; + + *vp = NULL; + if (vnode_open(fname, (FREAD | O_NOFOLLOW), 0, + VNODE_LOOKUP_NOFOLLOW, vp, *ctx) != 0) + { + DMSG("sleepWakeDebugDumpFromFile: Failed to open the file %s\n", fname); + goto err; + } + VATTR_INIT(&va); + VATTR_WANTED(&va, va_nlink); + VATTR_WANTED(&va, va_data_alloc); + if ((*vp)->v_type != VREG || + vnode_getattr((*vp), &va, *ctx) || va.va_nlink != 1) { + IOLog("sleepWakeDebugDumpFromFile: Bailing as %s is not a regular file\n", fname); + error = SWD_FILEOP_ERROR; + goto err; + } + + /* Read the sleepimage file header */ + rc = vn_rdwr(UIO_READ, *vp, (char *)tmpBuf, round_page(sizeof(IOHibernateImageHeader)), 0, + UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, + vfs_context_ucred(*ctx), (int *) 0, + vfs_context_proc(*ctx)); + if (rc != 0) { + IOLog("sleepWakeDebugDumpFromFile: Failed to read header size %llu(rc=%d) from %s\n", + mach_vm_round_page(sizeof(IOHibernateImageHeader)), rc, fname); + error = SWD_FILEOP_ERROR; + goto err; + } + + imageHdr = ((IOHibernateImageHeader *)tmpBuf); + if (imageHdr->signature != kIOHibernateHeaderDebugDataSignature) { + IOLog("sleepWakeDebugDumpFromFile: File %s header has unexpected value 0x%x\n", + fname, imageHdr->signature); + error = SWD_HDR_SIGNATURE_ERROR; + goto err; + } + + /* Sleep/Wake debug header(swd_hdr) is at the beggining of the second block */ + hdrOffset = imageHdr->deviceBlockSize; + if (hdrOffset + sizeof(swd_hdr) >= va.va_data_alloc) { + IOLog("sleepWakeDebugDumpFromFile: header is crossing file size(0x%llx) in file %s\n", + va.va_data_alloc, fname); + error = SWD_HDR_SIZE_ERROR; + goto err; + } + + return 0; + +err: + if (*vp) vnode_close(*vp, FREAD, *ctx); + *vp = NULL; + + return error; } void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) { - +#if HIBERNATION int rc; char hibernateFilename[MAXPATHLEN+1]; - char PMStatusCode[100]; void *tmpBuf; swd_hdr *hdr = NULL; uint32_t stacksSize, logSize; @@ -8967,10 +10218,9 @@ void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) OSNumber *failStat = NULL; struct vnode *vp = NULL; vfs_context_t ctx = NULL; + const char *stacksFname, *logFname; - struct vnode_attr va; IOBufferMemoryDescriptor *tmpBufDesc = NULL; - IOHibernateImageHeader *imageHdr; DLOG("sleepWakeDebugDumpFromFile\n"); if ((swd_flags & SWD_LOGS_IN_FILE) == 0) @@ -8980,20 +10230,6 @@ void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) return; - hibernateFilename[0] = 0; - if ((obj = copyProperty(kIOHibernateFileKey))) - { - if ((str = OSDynamicCast(OSString, obj))) - strlcpy(hibernateFilename, str->getCStringNoCopy(), - sizeof(hibernateFilename)); - obj->release(); - } - if (!hibernateFilename[0]) { - DMSG("sleepWakeDebugDumpFromFile: Failed to hib file name\n"); - goto exit; - } - DLOG("sleepWakeDebugDumpFromFile: Hib file name %s\n", hibernateFilename); - /* Allocate a temp buffer to copy data between files */ tmpBufSize = 2*4096; tmpBufDesc = IOBufferMemoryDescriptor:: @@ -9008,53 +10244,47 @@ void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) tmpBuf = tmpBufDesc->getBytesNoCopy(); ctx = vfs_context_create(vfs_context_current()); - if (vnode_open(hibernateFilename, (FREAD | O_NOFOLLOW), 0, - VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0) - { - DMSG("sleepWakeDebugDumpFromFile: Failed to open the hibernate file %s\n", hibernateFilename); - goto exit; - } - VATTR_INIT(&va); - VATTR_WANTED(&va, va_nlink); - VATTR_WANTED(&va, va_data_alloc); - if (vp->v_type != VREG || - vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) { - DMSG("sleepWakeDebugDumpFromFile: Bailing as this is not a regular file\n"); - goto exit; - } - /* Read the sleepimage file header */ - rc = vn_rdwr(UIO_READ, vp, (char *)tmpBuf, round_page(sizeof(IOHibernateImageHeader)), 0, - UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, - vfs_context_ucred(ctx), (int *) 0, - vfs_context_proc(ctx)); - if (rc != 0) { - DMSG("sleepWakeDebugDumpFromFile: Failed to read header size %lu(rc=%d)\n", round_page(sizeof(IOHibernateImageHeader)), rc); - goto exit; - } + /* First check if 'kSleepWakeStackBinFilename' has valid data */ + swd_flags |= checkForValidDebugData(kSleepWakeStackBinFilename, &ctx, tmpBuf, &vp); + if (vp == NULL) { + /* Check if the debug data is saved to hibernation file */ + hibernateFilename[0] = 0; + if ((obj = copyProperty(kIOHibernateFileKey))) + { + if ((str = OSDynamicCast(OSString, obj))) + strlcpy(hibernateFilename, str->getCStringNoCopy(), + sizeof(hibernateFilename)); + obj->release(); + } + if (!hibernateFilename[0]) { + DMSG("sleepWakeDebugDumpFromFile: Failed to get hibernation file name\n"); + goto exit; + } - imageHdr = ((IOHibernateImageHeader *)tmpBuf); - if (imageHdr->signature != kIOHibernateHeaderDebugDataSignature) { - DMSG("sleepWakeDebugDumpFromFile: File header has unexpected value 0x%x\n", imageHdr->signature); - goto exit; + swd_flags |= checkForValidDebugData(hibernateFilename, &ctx, tmpBuf, &vp); + if (vp == NULL) { + DMSG("sleepWakeDebugDumpFromFile: No valid debug data is found\n"); + goto exit; + } + DLOG("Getting SW Stacks image from file %s\n", hibernateFilename); } - - /* Sleep/Wake debug header(swd_hdr) is at the beggining of the second block */ - hdrOffset = imageHdr->deviceBlockSize; - if (hdrOffset + sizeof(swd_hdr) >= va.va_data_alloc) { - DMSG("sleepWakeDebugDumpFromFile: header is crossing file size(0x%llx)\n", va.va_data_alloc); - goto exit; + else { + DLOG("Getting SW Stacks image from file %s\n", kSleepWakeStackBinFilename); } - DLOG("Reading swd_hdr len 0x%lx offset 0x%lx\n", round_page(sizeof(swd_hdr)), trunc_page(hdrOffset)); + hdrOffset = ((IOHibernateImageHeader *)tmpBuf)->deviceBlockSize; + + DLOG("Reading swd_hdr len 0x%llx offset 0x%lx\n", mach_vm_round_page(sizeof(swd_hdr)), trunc_page(hdrOffset)); /* Read the sleep/wake debug header(swd_hdr) */ rc = vn_rdwr(UIO_READ, vp, (char *)tmpBuf, round_page(sizeof(swd_hdr)), trunc_page(hdrOffset), UIO_SYSSPACE, IO_SKIP_ENCRYPTION|IO_SYNC|IO_NODELOCKED|IO_UNIT|IO_NOCACHE, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx)); if (rc != 0) { - DMSG("sleepWakeDebugDumpFromFile: Failed to debug read header size %lu. rc=%d\n", - round_page(sizeof(swd_hdr)), rc); + DMSG("sleepWakeDebugDumpFromFile: Failed to debug read header size %llu. rc=%d\n", + mach_vm_round_page(sizeof(swd_hdr)), rc); + swd_flags |= SWD_FILEOP_ERROR; goto exit; } @@ -9063,6 +10293,7 @@ void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) (hdr->spindump_offset > SWD_BUF_SIZE) || (hdr->spindump_size > SWD_BUF_SIZE)) { DMSG("sleepWakeDebugDumpFromFile: Invalid data in debug header. sign:0x%x size:0x%x spindump_offset:0x%x spindump_size:0x%x\n", hdr->signature, hdr->alloc_size, hdr->spindump_offset, hdr->spindump_size); + swd_flags |= SWD_BUF_SIZE_ERROR; goto exit; } stacksSize = hdr->spindump_size; @@ -9071,15 +10302,17 @@ void IOPMrootDomain::sleepWakeDebugDumpFromFile( ) stacksOffset = hdrOffset + hdr->spindump_offset; logOffset = hdrOffset + offsetof(swd_hdr, UUID); logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID); + stacksFname = getDumpStackFilename(hdr); + logFname = getDumpLogFilename(hdr); error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, stacksOffset, - getDumpStackFilename(hdr), stacksSize, hdr->crc); + stacksFname, stacksSize, hdr->crc); if (error == EFAULT) { DMSG("sleepWakeDebugDumpFromFile: Stackshot CRC doesn't match\n"); goto exit; } error = sleepWakeDebugCopyFile(vp, ctx, (char *)tmpBuf, tmpBufSize, logOffset, - getDumpLogFilename(hdr), logSize, 0); + logFname, logSize, 0); if (error) { DMSG("sleepWakeDebugDumpFromFile: Failed to write the log file(0x%x)\n", error); goto exit; @@ -9089,6 +10322,11 @@ exit: // Write just the SleepWakeLog.dump with failure code uint64_t fcode = 0; const char *fname; + swd_hdr hdrCopy; + char *offset = NULL; + int size; + + hdr = &hdrCopy; if (swd_flags & SWD_BOOT_BY_SW_WDOG) { failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey)); fcode = failStat->unsigned64BitValue(); @@ -9097,17 +10335,24 @@ exit: else { fname = kAppleOSXWatchdogLogFilename; } - memset(PMStatusCode, 0x20, sizeof(PMStatusCode)); // Fill with spaces - PMStatusCode[sizeof(PMStatusCode)-1] = 0xa; // And an end-of-line at the end - snprintf(PMStatusCode, sizeof(PMStatusCode)-1, "Code: 0x%llx", fcode); - sleepWakeDebugSaveFile(fname, PMStatusCode, sizeof(PMStatusCode)); + + offset = (char*)hdr+offsetof(swd_hdr, UUID); + size = sizeof(swd_hdr)-offsetof(swd_hdr, UUID); + memset(offset, 0x20, size); // Fill with spaces + + + snprintf(hdr->spindump_status, sizeof(hdr->spindump_status), "\nstatus: 0x%x", swd_flags); + snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: 0x%llx", fcode); + snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n"); + sleepWakeDebugSaveFile(fname, offset, size); + } gRootDomain->swd_lock = 0; if (vp) vnode_close(vp, FREAD, ctx); if (ctx) vfs_context_rele(ctx); if (tmpBufDesc) tmpBufDesc->release(); - +#endif /* HIBERNATION */ } void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *logBufMap) @@ -9119,7 +10364,6 @@ void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *logBufMap) errno_t error = EIO; uint64_t bufSize = 0; swd_hdr *hdr = NULL; - char PMStatusCode[100]; OSNumber *failStat = NULL; if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) @@ -9135,7 +10379,8 @@ void IOPMrootDomain::sleepWakeDebugDumpFromMem(IOMemoryMap *logBufMap) bufSize = logBufMap->getLength(); if (bufSize <= sizeof(swd_hdr)) { - IOLog("SleepWake log buffer contents are invalid\n"); + IOLog("SleepWake log buffer size is invalid\n"); + swd_flags |= SWD_BUF_SIZE_ERROR; goto exit; } @@ -9158,13 +10403,10 @@ exit: // Write just the SleepWakeLog.dump with failure code uint64_t fcode = 0; const char *sname, *lname; - swd_stackshot_hdr shdr; - - /* Try writing an empty stacks file */ - shdr.magic = SWD_STACKSHOTHDR_MAGIC; - shdr.size = 0; - + swd_hdr hdrCopy; + /* Try writing an empty stacks file */ + hdr = &hdrCopy; if (swd_flags & SWD_BOOT_BY_SW_WDOG) { failStat = OSDynamicCast(OSNumber, getProperty(kIOPMSleepWakeFailureCodeKey)); fcode = failStat->unsigned64BitValue(); @@ -9176,12 +10418,19 @@ exit: sname= kAppleOSXWatchdogStackFilename; } - sleepWakeDebugSaveFile(sname, (char*)(&shdr), sizeof(shdr)); - memset(PMStatusCode, 0x20, sizeof(PMStatusCode)); // Fill with spaces - PMStatusCode[sizeof(PMStatusCode)-1] = 0xa; // And an end-of-line at the end - snprintf(PMStatusCode, sizeof(PMStatusCode)-1, "Code: 0x%llx", fcode); - sleepWakeDebugSaveFile(lname, PMStatusCode, sizeof(PMStatusCode)); + sleepWakeDebugSaveFile(sname, NULL, 0); + + logOffset = (char*)hdr+offsetof(swd_hdr, UUID); + logSize = sizeof(swd_hdr)-offsetof(swd_hdr, UUID); + memset(logOffset, 0x20, logSize); // Fill with spaces + + + snprintf(hdr->spindump_status, sizeof(hdr->spindump_status), "\nstatus: 0x%x", swd_flags); + snprintf(hdr->PMStatusCode, sizeof(hdr->PMStatusCode), "\nCode: 0x%llx", fcode); + snprintf(hdr->reason, sizeof(hdr->reason), "\nStackshot reason: Watchdog\n\n"); + sleepWakeDebugSaveFile(lname, logOffset, logSize); } + gRootDomain->swd_lock = 0; } @@ -9191,7 +10440,7 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) IOMemoryDescriptor * desc = NULL; IOMemoryMap * logBufMap = NULL; - uint32_t len; + uint32_t len = INT_MAX; addr64_t data[3]; uint64_t bufSize = 0; uint64_t crc = 0; @@ -9220,8 +10469,9 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) goto exit; } } - else if (len == sizeof(addr64_t)*3) + else if (len == sizeof(addr64_t)*3) { PEReadNVRAMProperty(kIOSleepWakeDebugKey, data, &len); + } else { DLOG("Invalid sleepWakeDebug note length(%d)\n", len); goto exit; @@ -9237,7 +10487,8 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) paddr = data[2]; if ( (bufSize <= sizeof(swd_hdr)) ||(bufSize > SWD_BUF_SIZE) || (crc == 0) ) { - IOLog("SleepWake log buffer contents are invalid\n"); + IOLog("SleepWake log buffer size is invalid\n"); + swd_flags |= SWD_BUF_SIZE_ERROR; return NULL; } @@ -9250,6 +10501,7 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) if (desc == NULL) { IOLog("Fail to map SleepWake log buffer\n"); + swd_flags |= SWD_INTERNAL_FAILURE; goto exit; } @@ -9260,13 +10512,15 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) if ( (logBufMap->getLength() <= sizeof(swd_hdr)) || (vaddr == NULL) ) { IOLog("Fail to map SleepWake log buffer\n"); + swd_flags |= SWD_INTERNAL_FAILURE; goto exit; } hdr = (swd_hdr *)vaddr; if (hdr->spindump_offset+hdr->spindump_size > bufSize) { - IOLog("SleepWake log buffer contents are invalid\n"); + IOLog("SleepWake log header size is invalid\n"); + swd_flags |= SWD_HDR_SIZE_ERROR; goto exit; } @@ -9275,6 +10529,7 @@ IOMemoryMap *IOPMrootDomain::sleepWakeDebugRetrieve( ) hdr->spindump_size); if (newcrc != crc) { IOLog("SleepWake log buffer contents are invalid\n"); + swd_flags |= SWD_DATA_CRC_ERROR; goto exit; } @@ -9298,9 +10553,19 @@ exit: void IOPMrootDomain::sleepWakeDebugTrig(bool restart) { + uint32_t wdog_panic = 1; + + if (restart) { + if (PE_parse_boot_argn("swd_panic", &wdog_panic, sizeof(wdog_panic)) && + (wdog_panic == 0)) { + return; + } + panic("Sleep/Wake hang detected"); + return; + } } -void IOPMrootDomain::takeStackshot(bool restart, bool isOSXWatchdog) +void IOPMrootDomain::takeStackshot(bool restart, bool isOSXWatchdog, bool isSpinDump) { #pragma unused(restart) #pragma unused(isOSXWatchdog)