| 1 | /* |
| 2 | * Copyright (c) 1998-2021 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * This file contains Original Code and/or Modifications of Original Code |
| 7 | * as defined in and that are subject to the Apple Public Source License |
| 8 | * Version 2.0 (the 'License'). You may not use this file except in |
| 9 | * compliance with the License. The rights granted to you under the License |
| 10 | * may not be used to create, or enable the creation or redistribution of, |
| 11 | * unlawful or unlicensed copies of an Apple operating system, or to |
| 12 | * circumvent, violate, or enable the circumvention or violation of, any |
| 13 | * terms of an Apple operating system software license agreement. |
| 14 | * |
| 15 | * Please obtain a copy of the License at |
| 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
| 17 | * |
| 18 | * The Original Code and all software distributed under the License are |
| 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 23 | * Please see the License for the specific language governing rights and |
| 24 | * limitations under the License. |
| 25 | * |
| 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 27 | */ |
| 28 | |
| 29 | #define IOKIT_ENABLE_SHARED_PTR |
| 30 | |
| 31 | #include <libkern/c++/OSAllocation.h> |
| 32 | #include <libkern/c++/OSKext.h> |
| 33 | #include <libkern/c++/OSMetaClass.h> |
| 34 | #include <libkern/OSAtomic.h> |
| 35 | #include <libkern/OSDebug.h> |
| 36 | #include <IOKit/IOWorkLoop.h> |
| 37 | #include <IOKit/IOCommandGate.h> |
| 38 | #include <IOKit/IOTimerEventSource.h> |
| 39 | #include <IOKit/IOPlatformExpert.h> |
| 40 | #include <IOKit/IOCPU.h> |
| 41 | #include <IOKit/IOPlatformActions.h> |
| 42 | #include <IOKit/IOKitDebug.h> |
| 43 | #include <IOKit/IOTimeStamp.h> |
| 44 | #include <IOKit/pwr_mgt/IOPMlog.h> |
| 45 | #include <IOKit/pwr_mgt/RootDomain.h> |
| 46 | #include <IOKit/pwr_mgt/IOPMPrivate.h> |
| 47 | #include <IOKit/IODeviceTreeSupport.h> |
| 48 | #include <IOKit/IOMessage.h> |
| 49 | #include <IOKit/IOReturn.h> |
| 50 | #include <IOKit/IONVRAM.h> |
| 51 | #include "RootDomainUserClient.h" |
| 52 | #include "IOKit/pwr_mgt/IOPowerConnection.h" |
| 53 | #include "IOPMPowerStateQueue.h" |
| 54 | #include <IOKit/IOCatalogue.h> |
| 55 | #include <IOKit/IOReportMacros.h> |
| 56 | #include <IOKit/IOLib.h> |
| 57 | #include <IOKit/IOKitKeys.h> |
| 58 | #include <IOKit/IOUserServer.h> |
| 59 | #include "IOKitKernelInternal.h" |
| 60 | #if HIBERNATION |
| 61 | #include <IOKit/IOHibernatePrivate.h> |
| 62 | #endif /* HIBERNATION */ |
| 63 | #include <console/video_console.h> |
| 64 | #include <sys/syslog.h> |
| 65 | #include <sys/sysctl.h> |
| 66 | #include <sys/vnode.h> |
| 67 | #include <sys/vnode_internal.h> |
| 68 | #include <sys/fcntl.h> |
| 69 | #include <os/log.h> |
| 70 | #include <pexpert/protos.h> |
| 71 | #include <AssertMacros.h> |
| 72 | |
| 73 | #include <sys/time.h> |
| 74 | #include "IOServicePrivate.h" // _IOServiceInterestNotifier |
| 75 | #include "IOServicePMPrivate.h" |
| 76 | |
| 77 | #include <libkern/zlib.h> |
| 78 | #include <os/cpp_util.h> |
| 79 | #include <os/atomic_private.h> |
| 80 | #include <libkern/c++/OSBoundedArrayRef.h> |
| 81 | |
| 82 | #if DEVELOPMENT || DEBUG |
| 83 | #include <os/system_event_log.h> |
| 84 | #endif /* DEVELOPMENT || DEBUG */ |
| 85 | |
| 86 | __BEGIN_DECLS |
| 87 | #include <mach/shared_region.h> |
| 88 | #include <kern/clock.h> |
| 89 | __END_DECLS |
| 90 | |
| 91 | #if defined(__i386__) || defined(__x86_64__) |
| 92 | __BEGIN_DECLS |
| 93 | #include "IOPMrootDomainInternal.h" |
| 94 | const char *processor_to_datastring(const char *prefix, processor_t target_processor); |
| 95 | __END_DECLS |
| 96 | #endif |
| 97 | |
| 98 | #define kIOPMrootDomainClass "IOPMrootDomain" |
| 99 | #define LOG_PREFIX "PMRD: " |
| 100 | |
| 101 | |
| 102 | #define MSG(x...) \ |
| 103 | do { kprintf(LOG_PREFIX x); IOLog(x); } while (false) |
| 104 | |
| 105 | #define LOG(x...) \ |
| 106 | do { kprintf(LOG_PREFIX x); } while (false) |
| 107 | |
| 108 | #if DEVELOPMENT || DEBUG |
| 109 | #define DEBUG_LOG(x...) do { \ |
| 110 | if (kIOLogPMRootDomain & gIOKitDebug) \ |
| 111 | kprintf(LOG_PREFIX x); \ |
| 112 | os_log_debug(OS_LOG_DEFAULT, LOG_PREFIX x); \ |
| 113 | } while (false) |
| 114 | #else |
| 115 | #define DEBUG_LOG(x...) |
| 116 | #endif |
| 117 | |
| 118 | #define DLOG(x...) do { \ |
| 119 | if (kIOLogPMRootDomain & gIOKitDebug) \ |
| 120 | IOLog(LOG_PREFIX x); \ |
| 121 | else \ |
| 122 | os_log(OS_LOG_DEFAULT, LOG_PREFIX x); \ |
| 123 | } while (false) |
| 124 | |
| 125 | #define DMSG(x...) do { \ |
| 126 | if (kIOLogPMRootDomain & gIOKitDebug) { \ |
| 127 | kprintf(LOG_PREFIX x); \ |
| 128 | } \ |
| 129 | } while (false) |
| 130 | |
| 131 | |
| 132 | #define _LOG(x...) |
| 133 | |
| 134 | #define CHECK_THREAD_CONTEXT |
| 135 | #ifdef CHECK_THREAD_CONTEXT |
| 136 | static IOWorkLoop * gIOPMWorkLoop = NULL; |
| 137 | #define ASSERT_GATED() \ |
| 138 | do { \ |
| 139 | if (gIOPMWorkLoop && gIOPMWorkLoop->inGate() != true) { \ |
| 140 | panic("RootDomain: not inside PM gate"); \ |
| 141 | } \ |
| 142 | } while(false) |
| 143 | #else |
| 144 | #define ASSERT_GATED() |
| 145 | #endif /* CHECK_THREAD_CONTEXT */ |
| 146 | |
| 147 | #define CAP_LOSS(c) \ |
| 148 | (((_pendingCapability & (c)) == 0) && \ |
| 149 | ((_currentCapability & (c)) != 0)) |
| 150 | |
| 151 | #define CAP_GAIN(c) \ |
| 152 | (((_currentCapability & (c)) == 0) && \ |
| 153 | ((_pendingCapability & (c)) != 0)) |
| 154 | |
| 155 | #define CAP_CHANGE(c) \ |
| 156 | (((_currentCapability ^ _pendingCapability) & (c)) != 0) |
| 157 | |
| 158 | #define CAP_CURRENT(c) \ |
| 159 | ((_currentCapability & (c)) != 0) |
| 160 | |
| 161 | #define CAP_HIGHEST(c) \ |
| 162 | ((_highestCapability & (c)) != 0) |
| 163 | |
| 164 | #define CAP_PENDING(c) \ |
| 165 | ((_pendingCapability & (c)) != 0) |
| 166 | |
| 167 | // rdar://problem/9157444 |
| 168 | #if defined(__i386__) || defined(__x86_64__) |
| 169 | #define DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY 20 |
| 170 | #endif |
| 171 | |
| 172 | // Event types for IOPMPowerStateQueue::submitPowerEvent() |
| 173 | enum { |
| 174 | kPowerEventFeatureChanged = 1, // 1 |
| 175 | kPowerEventReceivedPowerNotification, // 2 |
| 176 | kPowerEventSystemBootCompleted, // 3 |
| 177 | kPowerEventSystemShutdown, // 4 |
| 178 | kPowerEventUserDisabledSleep, // 5 |
| 179 | kPowerEventRegisterSystemCapabilityClient, // 6 |
| 180 | kPowerEventRegisterKernelCapabilityClient, // 7 |
| 181 | kPowerEventPolicyStimulus, // 8 |
| 182 | kPowerEventAssertionCreate, // 9 |
| 183 | kPowerEventAssertionRelease, // 10 |
| 184 | kPowerEventAssertionSetLevel, // 11 |
| 185 | kPowerEventQueueSleepWakeUUID, // 12 |
| 186 | kPowerEventPublishSleepWakeUUID, // 13 |
| 187 | kPowerEventSetDisplayPowerOn, // 14 |
| 188 | kPowerEventPublishWakeType, // 15 |
| 189 | kPowerEventAOTEvaluate // 16 |
| 190 | }; |
| 191 | |
| 192 | // For evaluatePolicy() |
| 193 | // List of stimuli that affects the root domain policy. |
| 194 | enum { |
| 195 | kStimulusDisplayWranglerSleep, // 0 |
| 196 | kStimulusDisplayWranglerWake, // 1 |
| 197 | kStimulusAggressivenessChanged, // 2 |
| 198 | kStimulusDemandSystemSleep, // 3 |
| 199 | kStimulusAllowSystemSleepChanged, // 4 |
| 200 | kStimulusDarkWakeActivityTickle, // 5 |
| 201 | kStimulusDarkWakeEntry, // 6 |
| 202 | kStimulusDarkWakeReentry, // 7 |
| 203 | kStimulusDarkWakeEvaluate, // 8 |
| 204 | kStimulusNoIdleSleepPreventers, // 9 |
| 205 | kStimulusEnterUserActiveState, // 10 |
| 206 | kStimulusLeaveUserActiveState // 11 |
| 207 | }; |
| 208 | |
| 209 | // Internal power state change reasons |
| 210 | // Must be less than kIOPMSleepReasonClamshell=101 |
| 211 | enum { |
| 212 | kCPSReasonNone = 0, // 0 |
| 213 | kCPSReasonInit, // 1 |
| 214 | kCPSReasonWake, // 2 |
| 215 | kCPSReasonIdleSleepPrevent, // 3 |
| 216 | kCPSReasonIdleSleepAllow, // 4 |
| 217 | kCPSReasonPowerOverride, // 5 |
| 218 | kCPSReasonPowerDownCancel, // 6 |
| 219 | kCPSReasonAOTExit, // 7 |
| 220 | kCPSReasonAdjustPowerState, // 8 |
| 221 | kCPSReasonDarkWakeCannotSleep, // 9 |
| 222 | kCPSReasonIdleSleepEnabled, // 10 |
| 223 | kCPSReasonEvaluatePolicy, // 11 |
| 224 | kCPSReasonSustainFullWake, // 12 |
| 225 | kCPSReasonPMInternals = (kIOPMSleepReasonClamshell - 1) |
| 226 | }; |
| 227 | |
| 228 | extern "C" { |
| 229 | IOReturn OSKextSystemSleepOrWake( UInt32 ); |
| 230 | } |
| 231 | extern "C" ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va); |
| 232 | extern "C" addr64_t kvtophys(vm_offset_t va); |
| 233 | extern "C" boolean_t kdp_has_polled_corefile(); |
| 234 | |
| 235 | static void idleSleepTimerExpired( thread_call_param_t, thread_call_param_t ); |
| 236 | static void notifySystemShutdown( IOService * root, uint32_t messageType ); |
| 237 | static void handleAggressivesFunction( thread_call_param_t, thread_call_param_t ); |
| 238 | static void pmEventTimeStamp(uint64_t *recordTS); |
| 239 | static void powerButtonUpCallout( thread_call_param_t, thread_call_param_t ); |
| 240 | static void powerButtonDownCallout( thread_call_param_t, thread_call_param_t ); |
| 241 | static OSPtr<const OSSymbol> copyKextIdentifierWithAddress(vm_address_t address); |
| 242 | |
| 243 | static int IOPMConvertSecondsToCalendar(clock_sec_t secs, IOPMCalendarStruct * dt); |
| 244 | static clock_sec_t IOPMConvertCalendarToSeconds(const IOPMCalendarStruct * dt); |
| 245 | #define YMDTF "%04d/%02d/%d %02d:%02d:%02d" |
| 246 | #define YMDT(cal) ((int)(cal)->year), (cal)->month, (cal)->day, (cal)->hour, (cal)->minute, (cal)->second |
| 247 | |
| 248 | // "IOPMSetSleepSupported" callPlatformFunction name |
| 249 | static OSSharedPtr<const OSSymbol> sleepSupportedPEFunction; |
| 250 | static OSSharedPtr<const OSSymbol> sleepMessagePEFunction; |
| 251 | static OSSharedPtr<const OSSymbol> gIOPMWakeTypeUserKey; |
| 252 | |
| 253 | static OSSharedPtr<const OSSymbol> gIOPMPSExternalConnectedKey; |
| 254 | static OSSharedPtr<const OSSymbol> gIOPMPSExternalChargeCapableKey; |
| 255 | static OSSharedPtr<const OSSymbol> gIOPMPSBatteryInstalledKey; |
| 256 | static OSSharedPtr<const OSSymbol> gIOPMPSIsChargingKey; |
| 257 | static OSSharedPtr<const OSSymbol> gIOPMPSAtWarnLevelKey; |
| 258 | static OSSharedPtr<const OSSymbol> gIOPMPSAtCriticalLevelKey; |
| 259 | static OSSharedPtr<const OSSymbol> gIOPMPSCurrentCapacityKey; |
| 260 | static OSSharedPtr<const OSSymbol> gIOPMPSMaxCapacityKey; |
| 261 | static OSSharedPtr<const OSSymbol> gIOPMPSDesignCapacityKey; |
| 262 | static OSSharedPtr<const OSSymbol> gIOPMPSTimeRemainingKey; |
| 263 | static OSSharedPtr<const OSSymbol> gIOPMPSAmperageKey; |
| 264 | static OSSharedPtr<const OSSymbol> gIOPMPSVoltageKey; |
| 265 | static OSSharedPtr<const OSSymbol> gIOPMPSCycleCountKey; |
| 266 | static OSSharedPtr<const OSSymbol> gIOPMPSMaxErrKey; |
| 267 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterInfoKey; |
| 268 | static OSSharedPtr<const OSSymbol> gIOPMPSLocationKey; |
| 269 | static OSSharedPtr<const OSSymbol> gIOPMPSErrorConditionKey; |
| 270 | static OSSharedPtr<const OSSymbol> gIOPMPSManufacturerKey; |
| 271 | static OSSharedPtr<const OSSymbol> gIOPMPSManufactureDateKey; |
| 272 | static OSSharedPtr<const OSSymbol> gIOPMPSModelKey; |
| 273 | static OSSharedPtr<const OSSymbol> gIOPMPSSerialKey; |
| 274 | static OSSharedPtr<const OSSymbol> gIOPMPSLegacyBatteryInfoKey; |
| 275 | static OSSharedPtr<const OSSymbol> gIOPMPSBatteryHealthKey; |
| 276 | static OSSharedPtr<const OSSymbol> gIOPMPSHealthConfidenceKey; |
| 277 | static OSSharedPtr<const OSSymbol> gIOPMPSCapacityEstimatedKey; |
| 278 | static OSSharedPtr<const OSSymbol> gIOPMPSBatteryChargeStatusKey; |
| 279 | static OSSharedPtr<const OSSymbol> gIOPMPSBatteryTemperatureKey; |
| 280 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsKey; |
| 281 | static OSSharedPtr<const OSSymbol> gIOPMPSChargerConfigurationKey; |
| 282 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsIDKey; |
| 283 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsWattsKey; |
| 284 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsRevisionKey; |
| 285 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsSerialNumberKey; |
| 286 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsFamilyKey; |
| 287 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsAmperageKey; |
| 288 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsDescriptionKey; |
| 289 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsPMUConfigurationKey; |
| 290 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsSourceIDKey; |
| 291 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsErrorFlagsKey; |
| 292 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsSharedSourceKey; |
| 293 | static OSSharedPtr<const OSSymbol> gIOPMPSAdapterDetailsCloakedKey; |
| 294 | static OSSharedPtr<const OSSymbol> gIOPMPSInvalidWakeSecondsKey; |
| 295 | static OSSharedPtr<const OSSymbol> gIOPMPSPostChargeWaitSecondsKey; |
| 296 | static OSSharedPtr<const OSSymbol> gIOPMPSPostDishargeWaitSecondsKey; |
| 297 | |
| 298 | #define kIOSleepSupportedKey "IOSleepSupported" |
| 299 | #define kIOPMSystemCapabilitiesKey "System Capabilities" |
| 300 | #define kIOPMSystemDefaultOverrideKey "SystemPowerProfileOverrideDict" |
| 301 | |
| 302 | #define kIORequestWranglerIdleKey "IORequestIdle" |
| 303 | #define kDefaultWranglerIdlePeriod 1000 // in milliseconds |
| 304 | |
| 305 | #define kIOSleepWakeFailureString "SleepWakeFailureString" |
| 306 | #define kIOEFIBootRomFailureKey "wake-failure" |
| 307 | #define kIOSleepWakeFailurePanic "SleepWakeFailurePanic" |
| 308 | |
| 309 | #define kRD_AllPowerSources (kIOPMSupportedOnAC \ |
| 310 | | kIOPMSupportedOnBatt \ |
| 311 | | kIOPMSupportedOnUPS) |
| 312 | |
| 313 | #define kLocalEvalClamshellCommand (1 << 15) |
| 314 | #define kIdleSleepRetryInterval (3 * 60 * 1000) |
| 315 | |
| 316 | // Minimum time in milliseconds after AP wake that we allow idle timer to expire. |
| 317 | // We impose this minimum to avoid race conditions in the AP wake path where |
| 318 | // userspace clients are not able to acquire power assertions before the idle timer expires. |
| 319 | #define kMinimumTimeBeforeIdleSleep 1000 |
| 320 | |
| 321 | #define DISPLAY_WRANGLER_PRESENT (!NO_KERNEL_HID) |
| 322 | |
| 323 | enum { |
| 324 | kWranglerPowerStateMin = 0, |
| 325 | kWranglerPowerStateSleep = 2, |
| 326 | kWranglerPowerStateDim = 3, |
| 327 | kWranglerPowerStateMax = 4 |
| 328 | }; |
| 329 | |
| 330 | enum { |
| 331 | OFF_STATE = 0, |
| 332 | RESTART_STATE = 1, |
| 333 | SLEEP_STATE = 2, |
| 334 | AOT_STATE = 3, |
| 335 | ON_STATE = 4, |
| 336 | NUM_POWER_STATES |
| 337 | }; |
| 338 | |
| 339 | const char * |
| 340 | getPowerStateString( uint32_t state ) |
| 341 | { |
| 342 | #define POWER_STATE(x) {(uint32_t) x, #x} |
| 343 | |
| 344 | static const IONamedValue powerStates[] = { |
| 345 | POWER_STATE( OFF_STATE ), |
| 346 | POWER_STATE( RESTART_STATE ), |
| 347 | POWER_STATE( SLEEP_STATE ), |
| 348 | POWER_STATE( AOT_STATE ), |
| 349 | POWER_STATE( ON_STATE ), |
| 350 | { .value: 0, NULL } |
| 351 | }; |
| 352 | return IOFindNameForValue(value: state, namedValueArray: powerStates); |
| 353 | } |
| 354 | |
| 355 | #define ON_POWER kIOPMPowerOn |
| 356 | #define RESTART_POWER kIOPMRestart |
| 357 | #define SLEEP_POWER kIOPMAuxPowerOn |
| 358 | |
| 359 | static IOPMPowerState |
| 360 | ourPowerStates[NUM_POWER_STATES] = |
| 361 | { |
| 362 | { .version = 1, |
| 363 | .capabilityFlags = 0, |
| 364 | .outputPowerCharacter = 0, |
| 365 | .inputPowerRequirement = 0 }, |
| 366 | { .version = 1, |
| 367 | .capabilityFlags = kIOPMRestartCapability, |
| 368 | .outputPowerCharacter = kIOPMRestart, |
| 369 | .inputPowerRequirement = RESTART_POWER }, |
| 370 | { .version = 1, |
| 371 | .capabilityFlags = kIOPMSleepCapability, |
| 372 | .outputPowerCharacter = kIOPMSleep, |
| 373 | .inputPowerRequirement = SLEEP_POWER }, |
| 374 | { .version = 1, |
| 375 | .capabilityFlags = kIOPMAOTCapability, |
| 376 | .outputPowerCharacter = kIOPMAOTPower, |
| 377 | .inputPowerRequirement = ON_POWER }, |
| 378 | { .version = 1, |
| 379 | .capabilityFlags = kIOPMPowerOn, |
| 380 | .outputPowerCharacter = kIOPMPowerOn, |
| 381 | .inputPowerRequirement = ON_POWER }, |
| 382 | }; |
| 383 | |
| 384 | #define kIOPMRootDomainWakeTypeSleepService "SleepService" |
| 385 | #define kIOPMRootDomainWakeTypeMaintenance "Maintenance" |
| 386 | #define kIOPMRootDomainWakeTypeSleepTimer "SleepTimer" |
| 387 | #define kIOPMrootDomainWakeTypeLowBattery "LowBattery" |
| 388 | #define kIOPMRootDomainWakeTypeUser "User" |
| 389 | #define kIOPMRootDomainWakeTypeAlarm "Alarm" |
| 390 | #define kIOPMRootDomainWakeTypeNetwork "Network" |
| 391 | #define kIOPMRootDomainWakeTypeHIDActivity "HID Activity" |
| 392 | #define kIOPMRootDomainWakeTypeNotification "Notification" |
| 393 | #define kIOPMRootDomainWakeTypeHibernateError "HibernateError" |
| 394 | |
| 395 | // Special interest that entitles the interested client from receiving |
| 396 | // all system messages. Only used by powerd. |
| 397 | // |
| 398 | #define kIOPMSystemCapabilityInterest "IOPMSystemCapabilityInterest" |
| 399 | |
| 400 | // Entitlement required for root domain clients |
| 401 | #define kRootDomainEntitlementSetProperty "com.apple.private.iokit.rootdomain-set-property" |
| 402 | |
| 403 | #define WAKEEVENT_LOCK() IOLockLock(wakeEventLock) |
| 404 | #define WAKEEVENT_UNLOCK() IOLockUnlock(wakeEventLock) |
| 405 | |
| 406 | /* |
| 407 | * Aggressiveness |
| 408 | */ |
| 409 | #define AGGRESSIVES_LOCK() IOLockLock(featuresDictLock) |
| 410 | #define AGGRESSIVES_UNLOCK() IOLockUnlock(featuresDictLock) |
| 411 | |
| 412 | #define kAggressivesMinValue 1 |
| 413 | |
| 414 | const char * |
| 415 | getAggressivenessTypeString( uint32_t type ) |
| 416 | { |
| 417 | #define AGGRESSIVENESS_TYPE(x) {(uint32_t) x, #x} |
| 418 | |
| 419 | static const IONamedValue aggressivenessTypes[] = { |
| 420 | AGGRESSIVENESS_TYPE( kPMGeneralAggressiveness ), |
| 421 | AGGRESSIVENESS_TYPE( kPMMinutesToDim ), |
| 422 | AGGRESSIVENESS_TYPE( kPMMinutesToSpinDown ), |
| 423 | AGGRESSIVENESS_TYPE( kPMMinutesToSleep ), |
| 424 | AGGRESSIVENESS_TYPE( kPMEthernetWakeOnLANSettings ), |
| 425 | AGGRESSIVENESS_TYPE( kPMSetProcessorSpeed ), |
| 426 | AGGRESSIVENESS_TYPE( kPMPowerSource), |
| 427 | AGGRESSIVENESS_TYPE( kPMMotionSensor ), |
| 428 | AGGRESSIVENESS_TYPE( kPMLastAggressivenessType ), |
| 429 | { .value: 0, NULL } |
| 430 | }; |
| 431 | return IOFindNameForValue(value: type, namedValueArray: aggressivenessTypes); |
| 432 | } |
| 433 | |
| 434 | enum { |
| 435 | kAggressivesStateBusy = 0x01, |
| 436 | kAggressivesStateQuickSpindown = 0x02 |
| 437 | }; |
| 438 | |
| 439 | struct AggressivesRecord { |
| 440 | uint32_t flags; |
| 441 | uint32_t type; |
| 442 | uint32_t value; |
| 443 | }; |
| 444 | |
| 445 | struct AggressivesRequest { |
| 446 | queue_chain_t chain; |
| 447 | uint32_t options; |
| 448 | uint32_t dataType; |
| 449 | union { |
| 450 | OSSharedPtr<IOService> service; |
| 451 | AggressivesRecord record; |
| 452 | } data; |
| 453 | }; |
| 454 | |
| 455 | enum { |
| 456 | kAggressivesRequestTypeService = 1, |
| 457 | kAggressivesRequestTypeRecord |
| 458 | }; |
| 459 | |
| 460 | enum { |
| 461 | kAggressivesOptionSynchronous = 0x00000001, |
| 462 | kAggressivesOptionQuickSpindownEnable = 0x00000100, |
| 463 | kAggressivesOptionQuickSpindownDisable = 0x00000200, |
| 464 | kAggressivesOptionQuickSpindownMask = 0x00000300 |
| 465 | }; |
| 466 | |
| 467 | enum { |
| 468 | kAggressivesRecordFlagModified = 0x00000001, |
| 469 | kAggressivesRecordFlagMinValue = 0x00000002 |
| 470 | }; |
| 471 | |
| 472 | // System Sleep Preventers |
| 473 | |
| 474 | enum { |
| 475 | kPMUserDisabledAllSleep = 1, |
| 476 | kPMSystemRestartBootingInProgress, |
| 477 | kPMConfigPreventSystemSleep, |
| 478 | kPMChildPreventSystemSleep, |
| 479 | kPMCPUAssertion, |
| 480 | kPMPCIUnsupported, |
| 481 | kPMDKNotReady, |
| 482 | }; |
| 483 | |
| 484 | const char * |
| 485 | getSystemSleepPreventerString( uint32_t preventer ) |
| 486 | { |
| 487 | #define SYSTEM_SLEEP_PREVENTER(x) {(int) x, #x} |
| 488 | static const IONamedValue systemSleepPreventers[] = { |
| 489 | SYSTEM_SLEEP_PREVENTER( kPMUserDisabledAllSleep ), |
| 490 | SYSTEM_SLEEP_PREVENTER( kPMSystemRestartBootingInProgress ), |
| 491 | SYSTEM_SLEEP_PREVENTER( kPMConfigPreventSystemSleep ), |
| 492 | SYSTEM_SLEEP_PREVENTER( kPMChildPreventSystemSleep ), |
| 493 | SYSTEM_SLEEP_PREVENTER( kPMCPUAssertion ), |
| 494 | SYSTEM_SLEEP_PREVENTER( kPMPCIUnsupported ), |
| 495 | SYSTEM_SLEEP_PREVENTER( kPMDKNotReady ), |
| 496 | { .value: 0, NULL } |
| 497 | }; |
| 498 | return IOFindNameForValue(value: preventer, namedValueArray: systemSleepPreventers); |
| 499 | } |
| 500 | |
| 501 | // gDarkWakeFlags |
| 502 | enum { |
| 503 | kDarkWakeFlagPromotionNone = 0x0000, |
| 504 | kDarkWakeFlagPromotionEarly = 0x0001, // promote before gfx clamp |
| 505 | kDarkWakeFlagPromotionLate = 0x0002, // promote after gfx clamp |
| 506 | kDarkWakeFlagPromotionMask = 0x0003, |
| 507 | kDarkWakeFlagAlarmIsDark = 0x0100, |
| 508 | kDarkWakeFlagAudioNotSuppressed = 0x0200, |
| 509 | kDarkWakeFlagUserWakeWorkaround = 0x1000 |
| 510 | }; |
| 511 | |
| 512 | // gClamshellFlags |
| 513 | // The workaround for 9157444 is enabled at compile time using the |
| 514 | // DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY macro and is not represented below. |
| 515 | enum { |
| 516 | kClamshell_WAR_38378787 = 0x00000001, |
| 517 | kClamshell_WAR_47715679 = 0x00000002, |
| 518 | kClamshell_WAR_58009435 = 0x00000004 |
| 519 | }; |
| 520 | |
| 521 | // acceptSystemWakeEvents() |
| 522 | enum { |
| 523 | kAcceptSystemWakeEvents_Disable = 0, |
| 524 | kAcceptSystemWakeEvents_Enable, |
| 525 | kAcceptSystemWakeEvents_Reenable |
| 526 | }; |
| 527 | |
| 528 | static IOPMrootDomain * gRootDomain; |
| 529 | static IORootParent * gPatriarch; |
| 530 | static IONotifier * gSysPowerDownNotifier = NULL; |
| 531 | static UInt32 gSleepOrShutdownPending = 0; |
| 532 | static UInt32 gWillShutdown = 0; |
| 533 | static UInt32 gPagingOff = 0; |
| 534 | static UInt32 gSleepWakeUUIDIsSet = false; |
| 535 | static uint32_t gAggressivesState = 0; |
| 536 | uint32_t gHaltTimeMaxLog; |
| 537 | uint32_t gHaltTimeMaxPanic; |
| 538 | IOLock * gHaltLogLock; |
| 539 | static char * gHaltLog; |
| 540 | enum { kHaltLogSize = 2048 }; |
| 541 | static size_t gHaltLogPos; |
| 542 | static uint64_t gHaltStartTime; |
| 543 | static char gKextNameBuf[64]; |
| 544 | static size_t gKextNamePos; |
| 545 | static bool gKextNameEnd; |
| 546 | |
| 547 | uuid_string_t bootsessionuuid_string; |
| 548 | |
| 549 | #if defined(XNU_TARGET_OS_OSX) |
| 550 | #if DISPLAY_WRANGLER_PRESENT |
| 551 | static uint32_t gDarkWakeFlags = kDarkWakeFlagPromotionNone; |
| 552 | #elif defined(__arm64__) |
| 553 | // Enable temporary full wake promotion workarounds |
| 554 | static uint32_t gDarkWakeFlags = kDarkWakeFlagUserWakeWorkaround; |
| 555 | #else |
| 556 | // Enable full wake promotion workarounds |
| 557 | static uint32_t gDarkWakeFlags = kDarkWakeFlagUserWakeWorkaround; |
| 558 | #endif |
| 559 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
| 560 | static uint32_t gDarkWakeFlags = kDarkWakeFlagPromotionEarly; |
| 561 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
| 562 | |
| 563 | static uint32_t gNoIdleFlag = 0; |
| 564 | static uint32_t gSleepDisabledFlag = 0; |
| 565 | static uint32_t gSwdPanic = 1; |
| 566 | static uint32_t gSwdSleepTimeout = 0; |
| 567 | static uint32_t gSwdWakeTimeout = 0; |
| 568 | static uint32_t gSwdSleepWakeTimeout = 0; |
| 569 | static PMStatsStruct gPMStats; |
| 570 | #if DEVELOPMENT || DEBUG |
| 571 | static uint32_t swd_panic_phase; |
| 572 | #endif |
| 573 | |
| 574 | static uint32_t gClamshellFlags = 0 |
| 575 | #if defined(__i386__) || defined(__x86_64__) |
| 576 | | kClamshell_WAR_58009435 |
| 577 | #endif |
| 578 | ; |
| 579 | |
| 580 | #if HIBERNATION |
| 581 | |
| 582 | #if defined(__arm64__) |
| 583 | static IOReturn |
| 584 | defaultSleepPolicyHandler(void *ctx, const IOPMSystemSleepPolicyVariables *vars, IOPMSystemSleepParameters *params) |
| 585 | { |
| 586 | uint32_t sleepType = kIOPMSleepTypeDeepIdle; |
| 587 | |
| 588 | assert(vars->signature == kIOPMSystemSleepPolicySignature); |
| 589 | assert(vars->version == kIOPMSystemSleepPolicyVersion); |
| 590 | |
| 591 | // Hibernation enabled and either user forced hibernate or low battery sleep |
| 592 | if ((vars->hibernateMode & kIOHibernateModeOn) && |
| 593 | (((vars->hibernateMode & kIOHibernateModeSleep) == 0) || |
| 594 | (vars->sleepFactors & kIOPMSleepFactorBatteryLow))) { |
| 595 | sleepType = kIOPMSleepTypeHibernate; |
| 596 | } |
| 597 | params->version = kIOPMSystemSleepParametersVersion; |
| 598 | params->sleepType = sleepType; |
| 599 | return kIOReturnSuccess; |
| 600 | } |
| 601 | static IOPMSystemSleepPolicyHandler gSleepPolicyHandler = &defaultSleepPolicyHandler; |
| 602 | #else /* defined(__arm64__) */ |
| 603 | static IOPMSystemSleepPolicyHandler gSleepPolicyHandler = NULL; |
| 604 | #endif /* defined(__arm64__) */ |
| 605 | |
| 606 | static IOPMSystemSleepPolicyVariables * gSleepPolicyVars = NULL; |
| 607 | static void * gSleepPolicyTarget; |
| 608 | #endif |
| 609 | |
| 610 | struct timeval gIOLastSleepTime; |
| 611 | struct timeval gIOLastWakeTime; |
| 612 | AbsoluteTime gIOLastWakeAbsTime; |
| 613 | AbsoluteTime gIOLastSleepAbsTime; |
| 614 | |
| 615 | struct timeval gIOLastUserSleepTime; |
| 616 | |
| 617 | static char gWakeReasonString[128]; |
| 618 | static char gBootReasonString[80]; |
| 619 | static char gShutdownReasonString[80]; |
| 620 | static bool gWakeReasonSysctlRegistered = false; |
| 621 | static bool gBootReasonSysctlRegistered = false; |
| 622 | static bool gShutdownReasonSysctlRegistered = false; |
| 623 | static bool gWillShutdownSysctlRegistered = false; |
| 624 | static AbsoluteTime gUserActiveAbsTime; |
| 625 | static AbsoluteTime gUserInactiveAbsTime; |
| 626 | |
| 627 | #if defined(__i386__) || defined(__x86_64__) || (defined(__arm64__) && HIBERNATION) |
| 628 | static bool gSpinDumpBufferFull = false; |
| 629 | #endif |
| 630 | |
| 631 | z_stream swd_zs; |
| 632 | vm_offset_t swd_zs_zmem; |
| 633 | //size_t swd_zs_zsize; |
| 634 | size_t swd_zs_zoffset; |
| 635 | #if defined(__i386__) || defined(__x86_64__) |
| 636 | IOCPU *currentShutdownTarget = NULL; |
| 637 | #endif |
| 638 | |
| 639 | static unsigned int gPMHaltBusyCount; |
| 640 | static unsigned int gPMHaltIdleCount; |
| 641 | static int gPMHaltDepth; |
| 642 | static uint32_t gPMHaltMessageType; |
| 643 | static IOLock * gPMHaltLock = NULL; |
| 644 | static OSSharedPtr<OSArray> gPMHaltArray; |
| 645 | static OSSharedPtr<const OSSymbol> gPMHaltClientAcknowledgeKey; |
| 646 | static bool gPMQuiesced; |
| 647 | |
| 648 | // Constants used as arguments to IOPMrootDomain::informCPUStateChange |
| 649 | #define kCPUUnknownIndex 9999999 |
| 650 | enum { |
| 651 | kInformAC = 0, |
| 652 | kInformLid = 1, |
| 653 | kInformableCount = 2 |
| 654 | }; |
| 655 | |
| 656 | OSSharedPtr<const OSSymbol> gIOPMStatsResponseTimedOut; |
| 657 | OSSharedPtr<const OSSymbol> gIOPMStatsResponseCancel; |
| 658 | OSSharedPtr<const OSSymbol> gIOPMStatsResponseSlow; |
| 659 | OSSharedPtr<const OSSymbol> gIOPMStatsResponsePrompt; |
| 660 | OSSharedPtr<const OSSymbol> gIOPMStatsDriverPSChangeSlow; |
| 661 | |
| 662 | #define kBadPMFeatureID 0 |
| 663 | |
| 664 | /* |
| 665 | * PMSettingHandle |
| 666 | * Opaque handle passed to clients of registerPMSettingController() |
| 667 | */ |
| 668 | class PMSettingHandle : public OSObject |
| 669 | { |
| 670 | OSDeclareFinalStructors( PMSettingHandle ); |
| 671 | friend class PMSettingObject; |
| 672 | |
| 673 | private: |
| 674 | PMSettingObject *pmso; |
| 675 | void free(void) APPLE_KEXT_OVERRIDE; |
| 676 | }; |
| 677 | |
| 678 | /* |
| 679 | * PMSettingObject |
| 680 | * Internal object to track each PM setting controller |
| 681 | */ |
| 682 | class PMSettingObject : public OSObject |
| 683 | { |
| 684 | OSDeclareFinalStructors( PMSettingObject ); |
| 685 | friend class IOPMrootDomain; |
| 686 | |
| 687 | private: |
| 688 | queue_head_t calloutQueue; |
| 689 | thread_t waitThread; |
| 690 | IOPMrootDomain *parent; |
| 691 | PMSettingHandle *pmsh; |
| 692 | IOPMSettingControllerCallback func; |
| 693 | OSObject *target; |
| 694 | uintptr_t refcon; |
| 695 | OSDataAllocation<uint32_t> publishedFeatureID; |
| 696 | uint32_t settingCount; |
| 697 | bool disabled; |
| 698 | |
| 699 | void free(void) APPLE_KEXT_OVERRIDE; |
| 700 | |
| 701 | public: |
| 702 | static PMSettingObject *pmSettingObject( |
| 703 | IOPMrootDomain *parent_arg, |
| 704 | IOPMSettingControllerCallback handler_arg, |
| 705 | OSObject *target_arg, |
| 706 | uintptr_t refcon_arg, |
| 707 | uint32_t supportedPowerSources, |
| 708 | const OSSymbol *settings[], |
| 709 | OSObject **handle_obj); |
| 710 | |
| 711 | IOReturn dispatchPMSetting(const OSSymbol *type, OSObject *object); |
| 712 | void clientHandleFreed(void); |
| 713 | }; |
| 714 | |
| 715 | struct PMSettingCallEntry { |
| 716 | queue_chain_t link; |
| 717 | thread_t thread; |
| 718 | }; |
| 719 | |
| 720 | #define PMSETTING_LOCK() IOLockLock(settingsCtrlLock) |
| 721 | #define PMSETTING_UNLOCK() IOLockUnlock(settingsCtrlLock) |
| 722 | #define PMSETTING_WAIT(p) IOLockSleep(settingsCtrlLock, p, THREAD_UNINT) |
| 723 | #define PMSETTING_WAKEUP(p) IOLockWakeup(settingsCtrlLock, p, true) |
| 724 | |
| 725 | /* |
| 726 | * PMTraceWorker |
| 727 | * Internal helper object for logging trace points to RTC |
| 728 | * IOPMrootDomain and only IOPMrootDomain should instantiate |
| 729 | * exactly one of these. |
| 730 | */ |
| 731 | |
| 732 | typedef void (*IOPMTracePointHandler)( |
| 733 | void * target, uint32_t code, uint32_t data ); |
| 734 | |
| 735 | class PMTraceWorker : public OSObject |
| 736 | { |
| 737 | OSDeclareDefaultStructors(PMTraceWorker); |
| 738 | public: |
| 739 | typedef enum { kPowerChangeStart, kPowerChangeCompleted } change_t; |
| 740 | |
| 741 | static OSPtr<PMTraceWorker> tracer( IOPMrootDomain * ); |
| 742 | void tracePCIPowerChange(change_t, IOService *, uint32_t, uint32_t); |
| 743 | void tracePoint(uint8_t phase); |
| 744 | void traceDetail(uint32_t detail); |
| 745 | void traceComponentWakeProgress(uint32_t component, uint32_t data); |
| 746 | int recordTopLevelPCIDevice(IOService *); |
| 747 | void RTC_TRACE(void); |
| 748 | virtual bool serialize(OSSerialize *s) const APPLE_KEXT_OVERRIDE; |
| 749 | |
| 750 | IOPMTracePointHandler tracePointHandler; |
| 751 | void * tracePointTarget; |
| 752 | uint64_t getPMStatusCode(); |
| 753 | uint8_t getTracePhase(); |
| 754 | uint32_t getTraceData(); |
| 755 | private: |
| 756 | IOPMrootDomain *owner; |
| 757 | IOLock *pmTraceWorkerLock; |
| 758 | OSSharedPtr<OSArray> pciDeviceBitMappings; |
| 759 | |
| 760 | uint8_t addedToRegistry; |
| 761 | uint8_t tracePhase; |
| 762 | uint32_t traceData32; |
| 763 | uint8_t loginWindowData; |
| 764 | uint8_t coreDisplayData; |
| 765 | uint8_t coreGraphicsData; |
| 766 | }; |
| 767 | |
| 768 | /* |
| 769 | * this should be treated as POD, as it's byte-copied around |
| 770 | * and we cannot rely on d'tor firing at the right time |
| 771 | */ |
| 772 | struct PMAssertStruct { |
| 773 | IOPMDriverAssertionID id; |
| 774 | IOPMDriverAssertionType assertionBits; |
| 775 | uint64_t createdTime; |
| 776 | uint64_t modifiedTime; |
| 777 | const OSSymbol *ownerString; |
| 778 | IOService *ownerService; |
| 779 | uint64_t registryEntryID; |
| 780 | IOPMDriverAssertionLevel level; |
| 781 | uint64_t assertCPUStartTime; |
| 782 | uint64_t assertCPUDuration; |
| 783 | }; |
| 784 | OSDefineValueObjectForDependentType(PMAssertStruct) |
| 785 | |
| 786 | /* |
| 787 | * PMAssertionsTracker |
| 788 | * Tracks kernel and user space PM assertions |
| 789 | */ |
| 790 | class PMAssertionsTracker : public OSObject |
| 791 | { |
| 792 | OSDeclareFinalStructors(PMAssertionsTracker); |
| 793 | public: |
| 794 | static PMAssertionsTracker *pmAssertionsTracker( IOPMrootDomain * ); |
| 795 | |
| 796 | IOReturn createAssertion(IOPMDriverAssertionType, IOPMDriverAssertionLevel, IOService *, const char *, IOPMDriverAssertionID *); |
| 797 | IOReturn releaseAssertion(IOPMDriverAssertionID); |
| 798 | IOReturn setAssertionLevel(IOPMDriverAssertionID, IOPMDriverAssertionLevel); |
| 799 | IOReturn setUserAssertionLevels(IOPMDriverAssertionType); |
| 800 | |
| 801 | OSSharedPtr<OSArray> copyAssertionsArray(void); |
| 802 | IOPMDriverAssertionType getActivatedAssertions(void); |
| 803 | IOPMDriverAssertionLevel getAssertionLevel(IOPMDriverAssertionType); |
| 804 | |
| 805 | IOReturn handleCreateAssertion(OSValueObject<PMAssertStruct> *); |
| 806 | IOReturn handleReleaseAssertion(IOPMDriverAssertionID); |
| 807 | IOReturn handleSetAssertionLevel(IOPMDriverAssertionID, IOPMDriverAssertionLevel); |
| 808 | IOReturn handleSetUserAssertionLevels(void * arg0); |
| 809 | void publishProperties(void); |
| 810 | void reportCPUBitAccounting(void); |
| 811 | PMAssertStruct *detailsForID(IOPMDriverAssertionID, int *); |
| 812 | |
| 813 | private: |
| 814 | uint32_t tabulateProducerCount; |
| 815 | uint32_t tabulateConsumerCount; |
| 816 | |
| 817 | uint64_t maxAssertCPUDuration; |
| 818 | uint64_t maxAssertCPUEntryId; |
| 819 | |
| 820 | void tabulate(void); |
| 821 | void updateCPUBitAccounting(PMAssertStruct * assertStruct); |
| 822 | |
| 823 | IOPMrootDomain *owner; |
| 824 | OSSharedPtr<OSArray> assertionsArray; |
| 825 | IOLock *assertionsArrayLock; |
| 826 | IOPMDriverAssertionID issuingUniqueID __attribute__((aligned(8)));/* aligned for atomic access */ |
| 827 | IOPMDriverAssertionType assertionsKernel; |
| 828 | IOPMDriverAssertionType assertionsUser; |
| 829 | IOPMDriverAssertionType assertionsCombined; |
| 830 | }; |
| 831 | |
| 832 | OSDefineMetaClassAndFinalStructors(PMAssertionsTracker, OSObject); |
| 833 | |
| 834 | /* |
| 835 | * PMHaltWorker |
| 836 | * Internal helper object for Shutdown/Restart notifications. |
| 837 | */ |
| 838 | #define kPMHaltMaxWorkers 8 |
| 839 | #define kPMHaltTimeoutMS 100 |
| 840 | |
| 841 | class PMHaltWorker : public OSObject |
| 842 | { |
| 843 | OSDeclareFinalStructors( PMHaltWorker ); |
| 844 | |
| 845 | public: |
| 846 | IOService * service;// service being worked on |
| 847 | AbsoluteTime startTime; // time when work started |
| 848 | int depth; // work on nubs at this PM-tree depth |
| 849 | int visits; // number of nodes visited (debug) |
| 850 | IOLock * lock; |
| 851 | bool timeout;// service took too long |
| 852 | |
| 853 | static PMHaltWorker * worker( void ); |
| 854 | static void main( void * arg, wait_result_t waitResult ); |
| 855 | static void work( PMHaltWorker * me ); |
| 856 | static void checkTimeout( PMHaltWorker * me, AbsoluteTime * now ); |
| 857 | virtual void free( void ) APPLE_KEXT_OVERRIDE; |
| 858 | }; |
| 859 | |
| 860 | OSDefineMetaClassAndFinalStructors( PMHaltWorker, OSObject ) |
| 861 | |
| 862 | |
| 863 | #define super IOService |
| 864 | OSDefineMetaClassAndFinalStructors(IOPMrootDomain, IOService) |
| 865 | |
| 866 | boolean_t |
| 867 | IOPMRootDomainGetWillShutdown(void) |
| 868 | { |
| 869 | return gWillShutdown != 0; |
| 870 | } |
| 871 | |
| 872 | static void |
| 873 | IOPMRootDomainWillShutdown(void) |
| 874 | { |
| 875 | if (OSCompareAndSwap(0, 1, &gWillShutdown)) { |
| 876 | IOService::willShutdown(); |
| 877 | for (int i = 0; i < 100; i++) { |
| 878 | if (OSCompareAndSwap(0, 1, &gSleepOrShutdownPending)) { |
| 879 | break; |
| 880 | } |
| 881 | IOSleep( milliseconds: 100 ); |
| 882 | } |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | extern "C" IONotifier * |
| 887 | registerSleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) |
| 888 | { |
| 889 | return gRootDomain->registerInterest( typeOfInterest: gIOGeneralInterest, handler, target: self, ref ).detach(); |
| 890 | } |
| 891 | |
| 892 | extern "C" IONotifier * |
| 893 | registerPrioritySleepWakeInterest(IOServiceInterestHandler handler, void * self, void * ref) |
| 894 | { |
| 895 | return gRootDomain->registerInterest( typeOfInterest: gIOPriorityPowerStateInterest, handler, target: self, ref ).detach(); |
| 896 | } |
| 897 | |
| 898 | extern "C" IOReturn |
| 899 | acknowledgeSleepWakeNotification(void * PMrefcon) |
| 900 | { |
| 901 | return gRootDomain->allowPowerChange(refcon: (unsigned long)PMrefcon ); |
| 902 | } |
| 903 | |
| 904 | extern "C" IOReturn |
| 905 | vetoSleepWakeNotification(void * PMrefcon) |
| 906 | { |
| 907 | return gRootDomain->cancelPowerChange(refcon: (unsigned long)PMrefcon ); |
| 908 | } |
| 909 | |
| 910 | extern "C" IOReturn |
| 911 | rootDomainRestart( void ) |
| 912 | { |
| 913 | return gRootDomain->restartSystem(); |
| 914 | } |
| 915 | |
| 916 | extern "C" IOReturn |
| 917 | rootDomainShutdown( void ) |
| 918 | { |
| 919 | return gRootDomain->shutdownSystem(); |
| 920 | } |
| 921 | |
| 922 | static void |
| 923 | halt_log_putc(char c) |
| 924 | { |
| 925 | if (gHaltLogPos >= (kHaltLogSize - 2)) { |
| 926 | return; |
| 927 | } |
| 928 | gHaltLog[gHaltLogPos++] = c; |
| 929 | } |
| 930 | |
| 931 | extern "C" void |
| 932 | _doprnt_log(const char *fmt, |
| 933 | va_list *argp, |
| 934 | void (*putc)(char), |
| 935 | int radix); |
| 936 | |
| 937 | static int |
| 938 | halt_log(const char *fmt, ...) |
| 939 | { |
| 940 | va_list listp; |
| 941 | |
| 942 | va_start(listp, fmt); |
| 943 | _doprnt_log(fmt, argp: &listp, putc: &halt_log_putc, radix: 16); |
| 944 | va_end(listp); |
| 945 | |
| 946 | return 0; |
| 947 | } |
| 948 | |
| 949 | extern "C" void |
| 950 | halt_log_enter(const char * what, const void * pc, uint64_t time) |
| 951 | { |
| 952 | uint64_t nano, millis; |
| 953 | |
| 954 | if (!gHaltLog) { |
| 955 | return; |
| 956 | } |
| 957 | absolutetime_to_nanoseconds(abstime: time, result: &nano); |
| 958 | millis = nano / NSEC_PER_MSEC; |
| 959 | if (millis < 100) { |
| 960 | return; |
| 961 | } |
| 962 | |
| 963 | IOLockLock(gHaltLogLock); |
| 964 | if (pc) { |
| 965 | halt_log(fmt: "%s: %qd ms @ 0x%lx, " , what, millis, VM_KERNEL_UNSLIDE(pc)); |
| 966 | OSKext::printKextsInBacktrace(addr: (vm_offset_t *) &pc, cnt: 1, printf_func: &halt_log, |
| 967 | flags: OSKext::kPrintKextsLock | OSKext::kPrintKextsUnslide | OSKext::kPrintKextsTerse); |
| 968 | } else { |
| 969 | halt_log(fmt: "%s: %qd ms\n" , what, millis); |
| 970 | } |
| 971 | |
| 972 | gHaltLog[gHaltLogPos] = 0; |
| 973 | IOLockUnlock(gHaltLogLock); |
| 974 | } |
| 975 | |
| 976 | extern uint32_t gFSState; |
| 977 | |
| 978 | extern "C" void |
| 979 | IOSystemShutdownNotification(int howto, int stage) |
| 980 | { |
| 981 | uint64_t startTime; |
| 982 | |
| 983 | if (kIOSystemShutdownNotificationStageRootUnmount == stage) { |
| 984 | #if defined(XNU_TARGET_OS_OSX) |
| 985 | uint64_t nano, millis; |
| 986 | startTime = mach_absolute_time(); |
| 987 | IOService::getPlatform()->waitQuiet(timeout: 30 * NSEC_PER_SEC); |
| 988 | absolutetime_to_nanoseconds(abstime: mach_absolute_time() - startTime, result: &nano); |
| 989 | millis = nano / NSEC_PER_MSEC; |
| 990 | if (gHaltTimeMaxLog && (millis >= gHaltTimeMaxLog)) { |
| 991 | printf("waitQuiet() for unmount %qd ms\n" , millis); |
| 992 | } |
| 993 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
| 994 | return; |
| 995 | } |
| 996 | |
| 997 | if (kIOSystemShutdownNotificationTerminateDEXTs == stage) { |
| 998 | uint64_t nano, millis; |
| 999 | startTime = mach_absolute_time(); |
| 1000 | IOServicePH::systemHalt(howto); |
| 1001 | absolutetime_to_nanoseconds(abstime: mach_absolute_time() - startTime, result: &nano); |
| 1002 | millis = nano / NSEC_PER_MSEC; |
| 1003 | if (true || (gHaltTimeMaxLog && (millis >= gHaltTimeMaxLog))) { |
| 1004 | printf("IOServicePH::systemHalt took %qd ms\n" , millis); |
| 1005 | } |
| 1006 | return; |
| 1007 | } |
| 1008 | |
| 1009 | assert(kIOSystemShutdownNotificationStageProcessExit == stage); |
| 1010 | |
| 1011 | IOLockLock(gHaltLogLock); |
| 1012 | if (!gHaltLog) { |
| 1013 | gHaltLog = IONewData(char, (vm_size_t)kHaltLogSize); |
| 1014 | gHaltStartTime = mach_absolute_time(); |
| 1015 | if (gHaltLog) { |
| 1016 | halt_log_putc(c: '\n'); |
| 1017 | } |
| 1018 | } |
| 1019 | IOLockUnlock(gHaltLogLock); |
| 1020 | |
| 1021 | startTime = mach_absolute_time(); |
| 1022 | IOPMRootDomainWillShutdown(); |
| 1023 | halt_log_enter(what: "IOPMRootDomainWillShutdown" , NULL, time: mach_absolute_time() - startTime); |
| 1024 | #if HIBERNATION |
| 1025 | startTime = mach_absolute_time(); |
| 1026 | IOHibernateSystemPostWake(true); |
| 1027 | halt_log_enter("IOHibernateSystemPostWake" , NULL, mach_absolute_time() - startTime); |
| 1028 | #endif |
| 1029 | if (OSCompareAndSwap(0, 1, &gPagingOff)) { |
| 1030 | gRootDomain->handlePlatformHaltRestart(pe_type: kPEPagingOff); |
| 1031 | } |
| 1032 | } |
| 1033 | |
| 1034 | extern "C" int sync_internal(void); |
| 1035 | |
| 1036 | /* |
| 1037 | * A device is always in the highest power state which satisfies its driver, |
| 1038 | * its policy-maker, and any power children it has, but within the constraint |
| 1039 | * of the power state provided by its parent. The driver expresses its desire by |
| 1040 | * calling changePowerStateTo(), the policy-maker expresses its desire by calling |
| 1041 | * changePowerStateToPriv(), and the children express their desires by calling |
| 1042 | * requestPowerDomainState(). |
| 1043 | * |
| 1044 | * The Root Power Domain owns the policy for idle and demand sleep for the system. |
| 1045 | * It is a power-managed IOService just like the others in the system. |
| 1046 | * It implements several power states which map to what we see as Sleep and On. |
| 1047 | * |
| 1048 | * The sleep policy is as follows: |
| 1049 | * 1. Sleep is prevented if the case is open so that nobody will think the machine |
| 1050 | * is off and plug/unplug cards. |
| 1051 | * 2. Sleep is prevented if the sleep timeout slider in the prefs panel is zero. |
| 1052 | * 3. System cannot Sleep if some object in the tree is in a power state marked |
| 1053 | * kIOPMPreventSystemSleep. |
| 1054 | * |
| 1055 | * These three conditions are enforced using the "driver clamp" by calling |
| 1056 | * changePowerStateTo(). For example, if the case is opened, |
| 1057 | * changePowerStateTo(ON_STATE) is called to hold the system on regardless |
| 1058 | * of the desires of the children of the root or the state of the other clamp. |
| 1059 | * |
| 1060 | * Demand Sleep is initiated by pressing the front panel power button, closing |
| 1061 | * the clamshell, or selecting the menu item. In this case the root's parent |
| 1062 | * actually initiates the power state change so that the root domain has no |
| 1063 | * choice and does not give applications the opportunity to veto the change. |
| 1064 | * |
| 1065 | * Idle Sleep occurs if no objects in the tree are in a state marked |
| 1066 | * kIOPMPreventIdleSleep. When this is true, the root's children are not holding |
| 1067 | * the root on, so it sets the "policy-maker clamp" by calling |
| 1068 | * changePowerStateToPriv(ON_STATE) to hold itself on until the sleep timer expires. |
| 1069 | * This timer is set for the difference between the sleep timeout slider and the |
| 1070 | * display dim timeout slider. When the timer expires, it releases its clamp and |
| 1071 | * now nothing is holding it awake, so it falls asleep. |
| 1072 | * |
| 1073 | * Demand sleep is prevented when the system is booting. When preferences are |
| 1074 | * transmitted by the loginwindow at the end of boot, a flag is cleared, |
| 1075 | * and this allows subsequent Demand Sleep. |
| 1076 | */ |
| 1077 | |
| 1078 | //****************************************************************************** |
| 1079 | |
| 1080 | IOPMrootDomain * |
| 1081 | IOPMrootDomain::construct( void ) |
| 1082 | { |
| 1083 | IOPMrootDomain *root; |
| 1084 | |
| 1085 | root = new IOPMrootDomain; |
| 1086 | if (root) { |
| 1087 | root->init(); |
| 1088 | } |
| 1089 | |
| 1090 | return root; |
| 1091 | } |
| 1092 | |
| 1093 | //****************************************************************************** |
| 1094 | // updateConsoleUsersCallout |
| 1095 | // |
| 1096 | //****************************************************************************** |
| 1097 | |
| 1098 | static void |
| 1099 | updateConsoleUsersCallout(thread_call_param_t p0, thread_call_param_t p1) |
| 1100 | { |
| 1101 | IOPMrootDomain * rootDomain = (IOPMrootDomain *) p0; |
| 1102 | rootDomain->updateConsoleUsers(); |
| 1103 | } |
| 1104 | |
| 1105 | void |
| 1106 | IOPMrootDomain::updateConsoleUsers(void) |
| 1107 | { |
| 1108 | IOService::updateConsoleUsers(NULL, kIOMessageSystemHasPoweredOn); |
| 1109 | updateTasksSuspend(newTasksSuspended: kTasksSuspendUnsuspended, newAOTTasksSuspended: kTasksSuspendNoChange); |
| 1110 | } |
| 1111 | |
| 1112 | bool |
| 1113 | IOPMrootDomain::updateTasksSuspend(int newTasksSuspended, int newAOTTasksSuspended) |
| 1114 | { |
| 1115 | bool newSuspend; |
| 1116 | |
| 1117 | WAKEEVENT_LOCK(); |
| 1118 | if (newTasksSuspended != kTasksSuspendNoChange) { |
| 1119 | tasksSuspended = (newTasksSuspended != kTasksSuspendUnsuspended); |
| 1120 | } |
| 1121 | if (newAOTTasksSuspended != kTasksSuspendNoChange) { |
| 1122 | _aotTasksSuspended = (newAOTTasksSuspended != kTasksSuspendUnsuspended); |
| 1123 | } |
| 1124 | newSuspend = (tasksSuspended || _aotTasksSuspended); |
| 1125 | if (newSuspend == tasksSuspendState) { |
| 1126 | WAKEEVENT_UNLOCK(); |
| 1127 | return false; |
| 1128 | } |
| 1129 | tasksSuspendState = newSuspend; |
| 1130 | WAKEEVENT_UNLOCK(); |
| 1131 | tasks_system_suspend(suspend: newSuspend); |
| 1132 | return true; |
| 1133 | } |
| 1134 | |
| 1135 | //****************************************************************************** |
| 1136 | |
| 1137 | static void |
| 1138 | disk_sync_callout( thread_call_param_t p0, thread_call_param_t p1 ) |
| 1139 | { |
| 1140 | IOPMrootDomain * rootDomain = (IOPMrootDomain *) p0; |
| 1141 | uint32_t notifyRef = (uint32_t)(uintptr_t) p1; |
| 1142 | uint32_t powerState = rootDomain->getPowerState(); |
| 1143 | |
| 1144 | DLOG("disk_sync_callout ps=%u\n" , powerState); |
| 1145 | |
| 1146 | if (ON_STATE == powerState) { |
| 1147 | sync_internal(); |
| 1148 | |
| 1149 | #if HIBERNATION |
| 1150 | // Block sleep until trim issued on previous wake path is completed. |
| 1151 | IOHibernateSystemPostWake(true); |
| 1152 | #endif |
| 1153 | } |
| 1154 | #if HIBERNATION |
| 1155 | else { |
| 1156 | IOHibernateSystemPostWake(false); |
| 1157 | |
| 1158 | rootDomain->sleepWakeDebugSaveSpinDumpFile(); |
| 1159 | } |
| 1160 | #endif |
| 1161 | |
| 1162 | rootDomain->allowPowerChange(refcon: notifyRef); |
| 1163 | DLOG("disk_sync_callout finish\n" ); |
| 1164 | } |
| 1165 | |
| 1166 | //****************************************************************************** |
| 1167 | static UInt32 |
| 1168 | computeDeltaTimeMS( const AbsoluteTime * startTime, AbsoluteTime * elapsedTime ) |
| 1169 | { |
| 1170 | AbsoluteTime endTime; |
| 1171 | UInt64 nano = 0; |
| 1172 | |
| 1173 | clock_get_uptime(result: &endTime); |
| 1174 | if (CMP_ABSOLUTETIME(&endTime, startTime) <= 0) { |
| 1175 | *elapsedTime = 0; |
| 1176 | } else { |
| 1177 | SUB_ABSOLUTETIME(&endTime, startTime); |
| 1178 | absolutetime_to_nanoseconds(abstime: endTime, result: &nano); |
| 1179 | *elapsedTime = endTime; |
| 1180 | } |
| 1181 | |
| 1182 | return (UInt32)(nano / NSEC_PER_MSEC); |
| 1183 | } |
| 1184 | |
| 1185 | //****************************************************************************** |
| 1186 | |
| 1187 | static int |
| 1188 | sysctl_sleepwaketime SYSCTL_HANDLER_ARGS |
| 1189 | { |
| 1190 | struct timeval *swt = (struct timeval *)arg1; |
| 1191 | struct proc *p = req->p; |
| 1192 | |
| 1193 | if (p == kernproc) { |
| 1194 | return sysctl_io_opaque(req, pValue: swt, valueSize: sizeof(*swt), NULL); |
| 1195 | } else if (proc_is64bit(p)) { |
| 1196 | struct user64_timeval t = {}; |
| 1197 | t.tv_sec = swt->tv_sec; |
| 1198 | t.tv_usec = swt->tv_usec; |
| 1199 | return sysctl_io_opaque(req, pValue: &t, valueSize: sizeof(t), NULL); |
| 1200 | } else { |
| 1201 | struct user32_timeval t = {}; |
| 1202 | t.tv_sec = (typeof(t.tv_sec))swt->tv_sec; |
| 1203 | t.tv_usec = swt->tv_usec; |
| 1204 | return sysctl_io_opaque(req, pValue: &t, valueSize: sizeof(t), NULL); |
| 1205 | } |
| 1206 | } |
| 1207 | |
| 1208 | static SYSCTL_PROC(_kern, OID_AUTO, sleeptime, |
| 1209 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1210 | &gIOLastUserSleepTime, 0, sysctl_sleepwaketime, "S,timeval" , "" ); |
| 1211 | |
| 1212 | static SYSCTL_PROC(_kern, OID_AUTO, waketime, |
| 1213 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1214 | &gIOLastWakeTime, 0, sysctl_sleepwaketime, "S,timeval" , "" ); |
| 1215 | |
| 1216 | SYSCTL_QUAD(_kern, OID_AUTO, wake_abs_time, CTLFLAG_RD | CTLFLAG_LOCKED, &gIOLastWakeAbsTime, "" ); |
| 1217 | SYSCTL_QUAD(_kern, OID_AUTO, sleep_abs_time, CTLFLAG_RD | CTLFLAG_LOCKED, &gIOLastSleepAbsTime, "" ); |
| 1218 | SYSCTL_QUAD(_kern, OID_AUTO, useractive_abs_time, CTLFLAG_RD | CTLFLAG_LOCKED, &gUserActiveAbsTime, "" ); |
| 1219 | SYSCTL_QUAD(_kern, OID_AUTO, userinactive_abs_time, CTLFLAG_RD | CTLFLAG_LOCKED, &gUserInactiveAbsTime, "" ); |
| 1220 | |
| 1221 | static int |
| 1222 | sysctl_willshutdown SYSCTL_HANDLER_ARGS |
| 1223 | { |
| 1224 | int new_value, changed, error; |
| 1225 | |
| 1226 | if (!gWillShutdownSysctlRegistered) { |
| 1227 | return ENOENT; |
| 1228 | } |
| 1229 | |
| 1230 | error = sysctl_io_number(req, bigValue: gWillShutdown, valueSize: sizeof(int), pValue: &new_value, changed: &changed); |
| 1231 | if (changed) { |
| 1232 | if (!gWillShutdown && (new_value == 1)) { |
| 1233 | IOPMRootDomainWillShutdown(); |
| 1234 | } else { |
| 1235 | error = EINVAL; |
| 1236 | } |
| 1237 | } |
| 1238 | return error; |
| 1239 | } |
| 1240 | |
| 1241 | static SYSCTL_PROC(_kern, OID_AUTO, willshutdown, |
| 1242 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1243 | NULL, 0, sysctl_willshutdown, "I" , "" ); |
| 1244 | |
| 1245 | #if defined(XNU_TARGET_OS_OSX) |
| 1246 | |
| 1247 | static int |
| 1248 | sysctl_progressmeterenable |
| 1249 | (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) |
| 1250 | { |
| 1251 | int error; |
| 1252 | int new_value, changed; |
| 1253 | |
| 1254 | error = sysctl_io_number(req, bigValue: vc_progressmeter_enable, valueSize: sizeof(int), pValue: &new_value, changed: &changed); |
| 1255 | |
| 1256 | if (changed) { |
| 1257 | vc_enable_progressmeter(new_value); |
| 1258 | } |
| 1259 | |
| 1260 | return error; |
| 1261 | } |
| 1262 | |
| 1263 | static int |
| 1264 | sysctl_progressmeter |
| 1265 | (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) |
| 1266 | { |
| 1267 | int error; |
| 1268 | int new_value, changed; |
| 1269 | |
| 1270 | error = sysctl_io_number(req, bigValue: vc_progressmeter_value, valueSize: sizeof(int), pValue: &new_value, changed: &changed); |
| 1271 | |
| 1272 | if (changed) { |
| 1273 | vc_set_progressmeter(new_value); |
| 1274 | } |
| 1275 | |
| 1276 | return error; |
| 1277 | } |
| 1278 | |
| 1279 | static SYSCTL_PROC(_kern, OID_AUTO, progressmeterenable, |
| 1280 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1281 | NULL, 0, sysctl_progressmeterenable, "I" , "" ); |
| 1282 | |
| 1283 | static SYSCTL_PROC(_kern, OID_AUTO, progressmeter, |
| 1284 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1285 | NULL, 0, sysctl_progressmeter, "I" , "" ); |
| 1286 | |
| 1287 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
| 1288 | |
| 1289 | |
| 1290 | |
| 1291 | static int |
| 1292 | sysctl_consoleoptions |
| 1293 | (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) |
| 1294 | { |
| 1295 | int error, changed; |
| 1296 | uint32_t new_value; |
| 1297 | |
| 1298 | error = sysctl_io_number(req, bigValue: vc_user_options.options, valueSize: sizeof(uint32_t), pValue: &new_value, changed: &changed); |
| 1299 | |
| 1300 | if (changed) { |
| 1301 | vc_user_options.options = new_value; |
| 1302 | } |
| 1303 | |
| 1304 | return error; |
| 1305 | } |
| 1306 | |
| 1307 | static SYSCTL_PROC(_kern, OID_AUTO, consoleoptions, |
| 1308 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1309 | NULL, 0, sysctl_consoleoptions, "I" , "" ); |
| 1310 | |
| 1311 | |
| 1312 | static int |
| 1313 | sysctl_progressoptions SYSCTL_HANDLER_ARGS |
| 1314 | { |
| 1315 | return sysctl_io_opaque(req, pValue: &vc_user_options, valueSize: sizeof(vc_user_options), NULL); |
| 1316 | } |
| 1317 | |
| 1318 | static SYSCTL_PROC(_kern, OID_AUTO, progressoptions, |
| 1319 | CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, |
| 1320 | NULL, 0, sysctl_progressoptions, "S,vc_progress_user_options" , "" ); |
| 1321 | |
| 1322 | |
| 1323 | static int |
| 1324 | sysctl_wakereason SYSCTL_HANDLER_ARGS |
| 1325 | { |
| 1326 | char wr[sizeof(gWakeReasonString)]; |
| 1327 | |
| 1328 | wr[0] = '\0'; |
| 1329 | if (gRootDomain && gWakeReasonSysctlRegistered) { |
| 1330 | gRootDomain->copyWakeReasonString(outBuf: wr, bufSize: sizeof(wr)); |
| 1331 | } else { |
| 1332 | return ENOENT; |
| 1333 | } |
| 1334 | |
| 1335 | return sysctl_io_string(req, pValue: wr, valueSize: 0, trunc: 0, NULL); |
| 1336 | } |
| 1337 | |
| 1338 | SYSCTL_PROC(_kern, OID_AUTO, wakereason, |
| 1339 | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1340 | NULL, 0, sysctl_wakereason, "A" , "wakereason" ); |
| 1341 | |
| 1342 | static int |
| 1343 | sysctl_bootreason SYSCTL_HANDLER_ARGS |
| 1344 | { |
| 1345 | if (!os_atomic_load(&gBootReasonSysctlRegistered, acquire)) { |
| 1346 | return ENOENT; |
| 1347 | } |
| 1348 | |
| 1349 | return sysctl_io_string(req, pValue: gBootReasonString, valueSize: 0, trunc: 0, NULL); |
| 1350 | } |
| 1351 | |
| 1352 | SYSCTL_PROC(_kern, OID_AUTO, bootreason, |
| 1353 | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1354 | NULL, 0, sysctl_bootreason, "A" , "" ); |
| 1355 | |
| 1356 | static int |
| 1357 | sysctl_shutdownreason SYSCTL_HANDLER_ARGS |
| 1358 | { |
| 1359 | char sr[sizeof(gShutdownReasonString)]; |
| 1360 | |
| 1361 | sr[0] = '\0'; |
| 1362 | if (gRootDomain && gShutdownReasonSysctlRegistered) { |
| 1363 | gRootDomain->copyShutdownReasonString(outBuf: sr, bufSize: sizeof(sr)); |
| 1364 | } else { |
| 1365 | return ENOENT; |
| 1366 | } |
| 1367 | |
| 1368 | return sysctl_io_string(req, pValue: sr, valueSize: 0, trunc: 0, NULL); |
| 1369 | } |
| 1370 | |
| 1371 | SYSCTL_PROC(_kern, OID_AUTO, shutdownreason, |
| 1372 | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1373 | NULL, 0, sysctl_shutdownreason, "A" , "shutdownreason" ); |
| 1374 | |
| 1375 | static int |
| 1376 | sysctl_targettype SYSCTL_HANDLER_ARGS |
| 1377 | { |
| 1378 | IOService * root; |
| 1379 | OSSharedPtr<OSObject> obj; |
| 1380 | OSData * data; |
| 1381 | char tt[32]; |
| 1382 | |
| 1383 | tt[0] = '\0'; |
| 1384 | root = IOService::getServiceRoot(); |
| 1385 | if (root && (obj = root->copyProperty(aKey: gIODTTargetTypeKey))) { |
| 1386 | if ((data = OSDynamicCast(OSData, obj.get()))) { |
| 1387 | strlcpy(dst: tt, src: (const char *) data->getBytesNoCopy(), n: sizeof(tt)); |
| 1388 | } |
| 1389 | } |
| 1390 | return sysctl_io_string(req, pValue: tt, valueSize: 0, trunc: 0, NULL); |
| 1391 | } |
| 1392 | |
| 1393 | SYSCTL_PROC(_hw, OID_AUTO, targettype, |
| 1394 | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1395 | NULL, 0, sysctl_targettype, "A" , "targettype" ); |
| 1396 | |
| 1397 | static SYSCTL_INT(_debug, OID_AUTO, noidle, CTLFLAG_RW, &gNoIdleFlag, 0, "" ); |
| 1398 | static SYSCTL_INT(_debug, OID_AUTO, swd_sleep_timeout, CTLFLAG_RW, &gSwdSleepTimeout, 0, "" ); |
| 1399 | static SYSCTL_INT(_debug, OID_AUTO, swd_wake_timeout, CTLFLAG_RW, &gSwdWakeTimeout, 0, "" ); |
| 1400 | static SYSCTL_INT(_debug, OID_AUTO, swd_timeout, CTLFLAG_RW, &gSwdSleepWakeTimeout, 0, "" ); |
| 1401 | static SYSCTL_INT(_debug, OID_AUTO, swd_panic, CTLFLAG_RW, &gSwdPanic, 0, "" ); |
| 1402 | #if DEVELOPMENT || DEBUG |
| 1403 | static SYSCTL_INT(_debug, OID_AUTO, swd_panic_phase, CTLFLAG_RW, &swd_panic_phase, 0, "" ); |
| 1404 | #if defined(XNU_TARGET_OS_OSX) |
| 1405 | static SYSCTL_INT(_debug, OID_AUTO, clamshell, CTLFLAG_RW, &gClamshellFlags, 0, "" ); |
| 1406 | static SYSCTL_INT(_debug, OID_AUTO, darkwake, CTLFLAG_RW, &gDarkWakeFlags, 0, "" ); |
| 1407 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
| 1408 | #endif /* DEVELOPMENT || DEBUG */ |
| 1409 | |
| 1410 | //****************************************************************************** |
| 1411 | // AOT |
| 1412 | |
| 1413 | static int |
| 1414 | sysctl_aotmetrics SYSCTL_HANDLER_ARGS |
| 1415 | { |
| 1416 | if (NULL == gRootDomain) { |
| 1417 | return ENOENT; |
| 1418 | } |
| 1419 | if (NULL == gRootDomain->_aotMetrics) { |
| 1420 | IOPMAOTMetrics nullMetrics = {}; |
| 1421 | return sysctl_io_opaque(req, pValue: &nullMetrics, valueSize: sizeof(IOPMAOTMetrics), NULL); |
| 1422 | } |
| 1423 | return sysctl_io_opaque(req, pValue: gRootDomain->_aotMetrics, valueSize: sizeof(IOPMAOTMetrics), NULL); |
| 1424 | } |
| 1425 | |
| 1426 | static SYSCTL_PROC(_kern, OID_AUTO, aotmetrics, |
| 1427 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_KERN | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, |
| 1428 | NULL, 0, sysctl_aotmetrics, "S,IOPMAOTMetrics" , "" ); |
| 1429 | |
| 1430 | |
| 1431 | static int |
| 1432 | update_aotmode(uint32_t mode) |
| 1433 | { |
| 1434 | int result; |
| 1435 | |
| 1436 | if (!gIOPMWorkLoop) { |
| 1437 | return ENOENT; |
| 1438 | } |
| 1439 | result = gIOPMWorkLoop->runActionBlock(action: ^IOReturn (void) { |
| 1440 | unsigned int oldCount; |
| 1441 | |
| 1442 | if (mode && !gRootDomain->_aotMetrics) { |
| 1443 | gRootDomain->_aotMetrics = IOMallocType(IOPMAOTMetrics); |
| 1444 | } |
| 1445 | |
| 1446 | oldCount = gRootDomain->idleSleepPreventersCount(); |
| 1447 | gRootDomain->_aotMode = (mode & kIOPMAOTModeMask); |
| 1448 | gRootDomain->updatePreventIdleSleepListInternal(NULL, addNotRemove: false, oldCount); |
| 1449 | return 0; |
| 1450 | }); |
| 1451 | return result; |
| 1452 | } |
| 1453 | |
| 1454 | static int |
| 1455 | sysctl_aotmodebits |
| 1456 | (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) |
| 1457 | { |
| 1458 | int error, changed; |
| 1459 | uint32_t new_value; |
| 1460 | |
| 1461 | if (NULL == gRootDomain) { |
| 1462 | return ENOENT; |
| 1463 | } |
| 1464 | error = sysctl_io_number(req, bigValue: gRootDomain->_aotMode, valueSize: sizeof(uint32_t), pValue: &new_value, changed: &changed); |
| 1465 | if (changed && gIOPMWorkLoop) { |
| 1466 | error = update_aotmode(mode: new_value); |
| 1467 | } |
| 1468 | |
| 1469 | return error; |
| 1470 | } |
| 1471 | |
| 1472 | static SYSCTL_PROC(_kern, OID_AUTO, aotmodebits, |
| 1473 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED, |
| 1474 | NULL, 0, sysctl_aotmodebits, "I" , "" ); |
| 1475 | |
| 1476 | static int |
| 1477 | sysctl_aotmode |
| 1478 | (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) |
| 1479 | { |
| 1480 | int error, changed; |
| 1481 | uint32_t new_value; |
| 1482 | |
| 1483 | if (NULL == gRootDomain) { |
| 1484 | return ENOENT; |
| 1485 | } |
| 1486 | error = sysctl_io_number(req, bigValue: gRootDomain->_aotMode, valueSize: sizeof(uint32_t), pValue: &new_value, changed: &changed); |
| 1487 | if (changed && gIOPMWorkLoop) { |
| 1488 | if (new_value) { |
| 1489 | new_value = kIOPMAOTModeDefault; // & ~kIOPMAOTModeRespectTimers; |
| 1490 | } |
| 1491 | error = update_aotmode(mode: new_value); |
| 1492 | } |
| 1493 | |
| 1494 | return error; |
| 1495 | } |
| 1496 | |
| 1497 | static SYSCTL_PROC(_kern, OID_AUTO, aotmode, |
| 1498 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, |
| 1499 | NULL, 0, sysctl_aotmode, "I" , "" ); |
| 1500 | |
| 1501 | //****************************************************************************** |
| 1502 | |
| 1503 | static OSSharedPtr<const OSSymbol> gIOPMSettingAutoWakeCalendarKey; |
| 1504 | static OSSharedPtr<const OSSymbol> gIOPMSettingAutoWakeSecondsKey; |
| 1505 | static OSSharedPtr<const OSSymbol> gIOPMSettingAutoPowerCalendarKey; |
| 1506 | static OSSharedPtr<const OSSymbol> gIOPMSettingAutoPowerSecondsKey; |
| 1507 | static OSSharedPtr<const OSSymbol> gIOPMSettingDebugWakeRelativeKey; |
| 1508 | static OSSharedPtr<const OSSymbol> gIOPMSettingDebugPowerRelativeKey; |
| 1509 | static OSSharedPtr<const OSSymbol> gIOPMSettingMaintenanceWakeCalendarKey; |
| 1510 | static OSSharedPtr<const OSSymbol> gIOPMSettingSleepServiceWakeCalendarKey; |
| 1511 | static OSSharedPtr<const OSSymbol> gIOPMSettingSilentRunningKey; |
| 1512 | static OSSharedPtr<const OSSymbol> gIOPMUserTriggeredFullWakeKey; |
| 1513 | static OSSharedPtr<const OSSymbol> gIOPMUserIsActiveKey; |
| 1514 | static OSSharedPtr<const OSSymbol> gIOPMSettingLowLatencyAudioModeKey; |
| 1515 | |
| 1516 | //****************************************************************************** |
| 1517 | // start |
| 1518 | // |
| 1519 | //****************************************************************************** |
| 1520 | |
| 1521 | #define kRootDomainSettingsCount 20 |
| 1522 | #define kRootDomainNoPublishSettingsCount 4 |
| 1523 | |
| 1524 | bool |
| 1525 | IOPMrootDomain::start( IOService * nub ) |
| 1526 | { |
| 1527 | OSSharedPtr<OSIterator> psIterator; |
| 1528 | OSSharedPtr<OSDictionary> tmpDict; |
| 1529 | |
| 1530 | super::start(provider: nub); |
| 1531 | |
| 1532 | gRootDomain = this; |
| 1533 | gIOPMSettingAutoWakeCalendarKey = OSSymbol::withCString(kIOPMSettingAutoWakeCalendarKey); |
| 1534 | gIOPMSettingAutoWakeSecondsKey = OSSymbol::withCString(kIOPMSettingAutoWakeSecondsKey); |
| 1535 | gIOPMSettingAutoPowerCalendarKey = OSSymbol::withCString(kIOPMSettingAutoPowerCalendarKey); |
| 1536 | gIOPMSettingAutoPowerSecondsKey = OSSymbol::withCString(kIOPMSettingAutoPowerSecondsKey); |
| 1537 | gIOPMSettingDebugWakeRelativeKey = OSSymbol::withCString(kIOPMSettingDebugWakeRelativeKey); |
| 1538 | gIOPMSettingDebugPowerRelativeKey = OSSymbol::withCString(kIOPMSettingDebugPowerRelativeKey); |
| 1539 | gIOPMSettingMaintenanceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingMaintenanceWakeCalendarKey); |
| 1540 | gIOPMSettingSleepServiceWakeCalendarKey = OSSymbol::withCString(kIOPMSettingSleepServiceWakeCalendarKey); |
| 1541 | gIOPMSettingSilentRunningKey = OSSymbol::withCStringNoCopy(kIOPMSettingSilentRunningKey); |
| 1542 | gIOPMUserTriggeredFullWakeKey = OSSymbol::withCStringNoCopy(kIOPMUserTriggeredFullWakeKey); |
| 1543 | gIOPMUserIsActiveKey = OSSymbol::withCStringNoCopy(kIOPMUserIsActiveKey); |
| 1544 | gIOPMSettingLowLatencyAudioModeKey = OSSymbol::withCStringNoCopy(kIOPMSettingLowLatencyAudioModeKey); |
| 1545 | |
| 1546 | gIOPMStatsResponseTimedOut = OSSymbol::withCString(kIOPMStatsResponseTimedOut); |
| 1547 | gIOPMStatsResponseCancel = OSSymbol::withCString(kIOPMStatsResponseCancel); |
| 1548 | gIOPMStatsResponseSlow = OSSymbol::withCString(kIOPMStatsResponseSlow); |
| 1549 | gIOPMStatsResponsePrompt = OSSymbol::withCString(kIOPMStatsResponsePrompt); |
| 1550 | gIOPMStatsDriverPSChangeSlow = OSSymbol::withCString(kIOPMStatsDriverPSChangeSlow); |
| 1551 | |
| 1552 | sleepSupportedPEFunction = OSSymbol::withCString(cString: "IOPMSetSleepSupported" ); |
| 1553 | sleepMessagePEFunction = OSSymbol::withCString(cString: "IOPMSystemSleepMessage" ); |
| 1554 | gIOPMWakeTypeUserKey = OSSymbol::withCStringNoCopy(kIOPMRootDomainWakeTypeUser); |
| 1555 | |
| 1556 | OSSharedPtr<const OSSymbol> settingsArr[kRootDomainSettingsCount] = |
| 1557 | { |
| 1558 | OSSymbol::withCString(kIOPMSettingSleepOnPowerButtonKey), |
| 1559 | gIOPMSettingAutoWakeSecondsKey, |
| 1560 | gIOPMSettingAutoPowerSecondsKey, |
| 1561 | gIOPMSettingAutoWakeCalendarKey, |
| 1562 | gIOPMSettingAutoPowerCalendarKey, |
| 1563 | gIOPMSettingDebugWakeRelativeKey, |
| 1564 | gIOPMSettingDebugPowerRelativeKey, |
| 1565 | OSSymbol::withCString(kIOPMSettingWakeOnRingKey), |
| 1566 | OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey), |
| 1567 | OSSymbol::withCString(kIOPMSettingWakeOnClamshellKey), |
| 1568 | OSSymbol::withCString(kIOPMSettingWakeOnACChangeKey), |
| 1569 | OSSymbol::withCString(kIOPMSettingTimeZoneOffsetKey), |
| 1570 | OSSymbol::withCString(kIOPMSettingDisplaySleepUsesDimKey), |
| 1571 | OSSymbol::withCString(kIOPMSettingMobileMotionModuleKey), |
| 1572 | OSSymbol::withCString(kIOPMSettingGraphicsSwitchKey), |
| 1573 | OSSymbol::withCString(kIOPMStateConsoleShutdown), |
| 1574 | OSSymbol::withCString(kIOPMSettingProModeControl), |
| 1575 | OSSymbol::withCString(kIOPMSettingProModeDefer), |
| 1576 | gIOPMSettingSilentRunningKey, |
| 1577 | gIOPMSettingLowLatencyAudioModeKey, |
| 1578 | }; |
| 1579 | |
| 1580 | OSSharedPtr<const OSSymbol> noPublishSettingsArr[kRootDomainNoPublishSettingsCount] = |
| 1581 | { |
| 1582 | OSSymbol::withCString(kIOPMSettingProModeControl), |
| 1583 | OSSymbol::withCString(kIOPMSettingProModeDefer), |
| 1584 | gIOPMSettingSilentRunningKey, |
| 1585 | gIOPMSettingLowLatencyAudioModeKey, |
| 1586 | }; |
| 1587 | |
| 1588 | #if DEVELOPMENT || DEBUG |
| 1589 | #if defined(XNU_TARGET_OS_OSX) |
| 1590 | PE_parse_boot_argn("darkwake" , &gDarkWakeFlags, sizeof(gDarkWakeFlags)); |
| 1591 | PE_parse_boot_argn("clamshell" , &gClamshellFlags, sizeof(gClamshellFlags)); |
| 1592 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
| 1593 | #endif /* DEVELOPMENT || DEBUG */ |
| 1594 | |
| 1595 | PE_parse_boot_argn(arg_string: "noidle" , arg_ptr: &gNoIdleFlag, max_arg: sizeof(gNoIdleFlag)); |
| 1596 | PE_parse_boot_argn(arg_string: "swd_sleeptimeout" , arg_ptr: &gSwdSleepTimeout, max_arg: sizeof(gSwdSleepTimeout)); |
| 1597 | PE_parse_boot_argn(arg_string: "swd_waketimeout" , arg_ptr: &gSwdWakeTimeout, max_arg: sizeof(gSwdWakeTimeout)); |
| 1598 | PE_parse_boot_argn(arg_string: "swd_timeout" , arg_ptr: &gSwdSleepWakeTimeout, max_arg: sizeof(gSwdSleepWakeTimeout)); |
| 1599 | PE_parse_boot_argn(arg_string: "haltmspanic" , arg_ptr: &gHaltTimeMaxPanic, max_arg: sizeof(gHaltTimeMaxPanic)); |
| 1600 | PE_parse_boot_argn(arg_string: "haltmslog" , arg_ptr: &gHaltTimeMaxLog, max_arg: sizeof(gHaltTimeMaxLog)); |
| 1601 | |
| 1602 | // read noidle setting from Device Tree |
| 1603 | if (PE_get_default(property_name: "no-idle" , property_ptr: &gNoIdleFlag, max_property: sizeof(gNoIdleFlag))) { |
| 1604 | DLOG("Setting gNoIdleFlag to %u from device tree\n" , gNoIdleFlag); |
| 1605 | } |
| 1606 | |
| 1607 | queue_init(&aggressivesQueue); |
| 1608 | aggressivesThreadCall = thread_call_allocate(func: handleAggressivesFunction, param0: this); |
| 1609 | aggressivesData = OSData::withCapacity( |
| 1610 | capacity: sizeof(AggressivesRecord) * (kPMLastAggressivenessType + 4)); |
| 1611 | |
| 1612 | featuresDictLock = IOLockAlloc(); |
| 1613 | settingsCtrlLock = IOLockAlloc(); |
| 1614 | wakeEventLock = IOLockAlloc(); |
| 1615 | gHaltLogLock = IOLockAlloc(); |
| 1616 | setPMRootDomain(this); |
| 1617 | |
| 1618 | extraSleepTimer = thread_call_allocate( |
| 1619 | func: idleSleepTimerExpired, |
| 1620 | param0: (thread_call_param_t) this); |
| 1621 | |
| 1622 | powerButtonDown = thread_call_allocate( |
| 1623 | func: powerButtonDownCallout, |
| 1624 | param0: (thread_call_param_t) this); |
| 1625 | |
| 1626 | powerButtonUp = thread_call_allocate( |
| 1627 | func: powerButtonUpCallout, |
| 1628 | param0: (thread_call_param_t) this); |
| 1629 | |
| 1630 | diskSyncCalloutEntry = thread_call_allocate( |
| 1631 | func: &disk_sync_callout, |
| 1632 | param0: (thread_call_param_t) this); |
| 1633 | updateConsoleUsersEntry = thread_call_allocate( |
| 1634 | func: &updateConsoleUsersCallout, |
| 1635 | param0: (thread_call_param_t) this); |
| 1636 | |
| 1637 | #if DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY |
| 1638 | fullWakeThreadCall = thread_call_allocate_with_options( |
| 1639 | OSMemberFunctionCast(thread_call_func_t, this, |
| 1640 | &IOPMrootDomain::fullWakeDelayedWork), |
| 1641 | (thread_call_param_t) this, THREAD_CALL_PRIORITY_KERNEL, |
| 1642 | THREAD_CALL_OPTIONS_ONCE); |
| 1643 | #endif |
| 1644 | |
| 1645 | setProperty(kIOSleepSupportedKey, aBoolean: true); |
| 1646 | |
| 1647 | bzero(s: &gPMStats, n: sizeof(gPMStats)); |
| 1648 | |
| 1649 | pmTracer = PMTraceWorker::tracer(this); |
| 1650 | |
| 1651 | pmAssertions = PMAssertionsTracker::pmAssertionsTracker(this); |
| 1652 | |
| 1653 | userDisabledAllSleep = false; |
| 1654 | systemBooting = true; |
| 1655 | idleSleepEnabled = false; |
| 1656 | sleepSlider = 0; |
| 1657 | idleSleepTimerPending = false; |
| 1658 | wrangler = NULL; |
| 1659 | clamshellClosed = false; |
| 1660 | clamshellExists = false; |
| 1661 | #if DISPLAY_WRANGLER_PRESENT |
| 1662 | clamshellDisabled = true; |
| 1663 | #else |
| 1664 | clamshellDisabled = false; |
| 1665 | #endif |
| 1666 | clamshellIgnoreClose = false; |
| 1667 | acAdaptorConnected = true; |
| 1668 | clamshellSleepDisableMask = 0; |
| 1669 | gWakeReasonString[0] = '\0'; |
| 1670 | |
| 1671 | // Initialize to user active. |
| 1672 | // Will never transition to user inactive w/o wrangler. |
| 1673 | fullWakeReason = kFullWakeReasonLocalUser; |
| 1674 | userIsActive = userWasActive = true; |
| 1675 | clock_get_uptime(result: &gUserActiveAbsTime); |
| 1676 | setProperty(aKey: gIOPMUserIsActiveKey.get(), anObject: kOSBooleanTrue); |
| 1677 | |
| 1678 | // Set the default system capabilities at boot. |
| 1679 | _currentCapability = kIOPMSystemCapabilityCPU | |
| 1680 | kIOPMSystemCapabilityGraphics | |
| 1681 | kIOPMSystemCapabilityAudio | |
| 1682 | kIOPMSystemCapabilityNetwork; |
| 1683 | |
| 1684 | _pendingCapability = _currentCapability; |
| 1685 | _desiredCapability = _currentCapability; |
| 1686 | _highestCapability = _currentCapability; |
| 1687 | setProperty(kIOPMSystemCapabilitiesKey, aValue: _currentCapability, aNumberOfBits: 64); |
| 1688 | |
| 1689 | queuedSleepWakeUUIDString = NULL; |
| 1690 | initializeBootSessionUUID(); |
| 1691 | pmStatsAppResponses = OSArray::withCapacity(capacity: 5); |
| 1692 | _statsNameKey = OSSymbol::withCString(kIOPMStatsNameKey); |
| 1693 | _statsPIDKey = OSSymbol::withCString(kIOPMStatsPIDKey); |
| 1694 | _statsTimeMSKey = OSSymbol::withCString(kIOPMStatsTimeMSKey); |
| 1695 | _statsResponseTypeKey = OSSymbol::withCString(kIOPMStatsApplicationResponseTypeKey); |
| 1696 | _statsMessageTypeKey = OSSymbol::withCString(kIOPMStatsMessageTypeKey); |
| 1697 | _statsPowerCapsKey = OSSymbol::withCString(kIOPMStatsPowerCapabilityKey); |
| 1698 | assertOnWakeSecs = -1;// Invalid value to prevent updates |
| 1699 | |
| 1700 | pmStatsLock = IOLockAlloc(); |
| 1701 | idxPMCPUClamshell = kCPUUnknownIndex; |
| 1702 | idxPMCPULimitedPower = kCPUUnknownIndex; |
| 1703 | |
| 1704 | tmpDict = OSDictionary::withCapacity(capacity: 1); |
| 1705 | setProperty(kRootDomainSupportedFeatures, anObject: tmpDict.get()); |
| 1706 | |
| 1707 | // Set a default "SystemPowerProfileOverrideDict" for platform |
| 1708 | // drivers without any overrides. |
| 1709 | if (!propertyExists(kIOPMSystemDefaultOverrideKey)) { |
| 1710 | tmpDict = OSDictionary::withCapacity(capacity: 1); |
| 1711 | setProperty(kIOPMSystemDefaultOverrideKey, anObject: tmpDict.get()); |
| 1712 | } |
| 1713 | |
| 1714 | settingsCallbacks = OSDictionary::withCapacity(capacity: 1); |
| 1715 | |
| 1716 | // Create a list of the valid PM settings that we'll relay to |
| 1717 | // interested clients in setProperties() => setPMSetting() |
| 1718 | allowedPMSettings = OSArray::withObjects( |
| 1719 | objects: (const OSObject **)settingsArr, |
| 1720 | kRootDomainSettingsCount, |
| 1721 | capacity: 0); |
| 1722 | |
| 1723 | // List of PM settings that should not automatically publish itself |
| 1724 | // as a feature when registered by a listener. |
| 1725 | noPublishPMSettings = OSArray::withObjects( |
| 1726 | objects: (const OSObject **)noPublishSettingsArr, |
| 1727 | kRootDomainNoPublishSettingsCount, |
| 1728 | capacity: 0); |
| 1729 | |
| 1730 | fPMSettingsDict = OSDictionary::withCapacity(capacity: 5); |
| 1731 | preventIdleSleepList = OSSet::withCapacity(capacity: 8); |
| 1732 | preventSystemSleepList = OSSet::withCapacity(capacity: 2); |
| 1733 | |
| 1734 | PMinit(); // creates gIOPMWorkLoop |
| 1735 | gIOPMWorkLoop = getIOPMWorkloop(); |
| 1736 | |
| 1737 | // Create IOPMPowerStateQueue used to queue external power |
| 1738 | // events, and to handle those events on the PM work loop. |
| 1739 | pmPowerStateQueue = IOPMPowerStateQueue::PMPowerStateQueue( |
| 1740 | owner: this, OSMemberFunctionCast(IOEventSource::Action, this, |
| 1741 | &IOPMrootDomain::dispatchPowerEvent)); |
| 1742 | gIOPMWorkLoop->addEventSource(newEvent: pmPowerStateQueue); |
| 1743 | |
| 1744 | _aotMode = 0; |
| 1745 | _aotTimerES = IOTimerEventSource::timerEventSource(owner: this, |
| 1746 | OSMemberFunctionCast(IOTimerEventSource::Action, |
| 1747 | this, &IOPMrootDomain::aotEvaluate)); |
| 1748 | gIOPMWorkLoop->addEventSource(newEvent: _aotTimerES.get()); |
| 1749 | |
| 1750 | // Avoid publishing service early so gIOPMWorkLoop is |
| 1751 | // guaranteed to be initialized by rootDomain. |
| 1752 | publishPMRootDomain(); |
| 1753 | |
| 1754 | // create our power parent |
| 1755 | gPatriarch = new IORootParent; |
| 1756 | gPatriarch->init(); |
| 1757 | gPatriarch->attach(provider: this); |
| 1758 | gPatriarch->start(nub: this); |
| 1759 | gPatriarch->addPowerChild(theChild: this); |
| 1760 | |
| 1761 | registerPowerDriver(controllingDriver: this, powerStates: ourPowerStates, numberOfStates: NUM_POWER_STATES); |
| 1762 | changePowerStateWithTagToPriv(ordinal: ON_STATE, tag: kCPSReasonInit); |
| 1763 | |
| 1764 | // install power change handler |
| 1765 | gSysPowerDownNotifier = registerPrioritySleepWakeInterest( handler: &sysPowerDownHandler, self: this, NULL); |
| 1766 | |
| 1767 | #if DISPLAY_WRANGLER_PRESENT |
| 1768 | wranglerIdleSettings = OSDictionary::withCapacity(1); |
| 1769 | OSSharedPtr<OSNumber> wranglerIdlePeriod = OSNumber::withNumber(kDefaultWranglerIdlePeriod, 32); |
| 1770 | |
| 1771 | if (wranglerIdleSettings && wranglerIdlePeriod) { |
| 1772 | wranglerIdleSettings->setObject(kIORequestWranglerIdleKey, |
| 1773 | wranglerIdlePeriod.get()); |
| 1774 | } |
| 1775 | |
| 1776 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 1777 | |
| 1778 | lowLatencyAudioNotifierDict = OSDictionary::withCapacity(capacity: 2); |
| 1779 | lowLatencyAudioNotifyStateSym = OSSymbol::withCString(cString: "LowLatencyAudioNotifyState" ); |
| 1780 | lowLatencyAudioNotifyTimestampSym = OSSymbol::withCString(cString: "LowLatencyAudioNotifyTimestamp" ); |
| 1781 | lowLatencyAudioNotifyStateVal = OSNumber::withNumber(value: 0ull, numberOfBits: 32); |
| 1782 | lowLatencyAudioNotifyTimestampVal = OSNumber::withNumber(value: 0ull, numberOfBits: 64); |
| 1783 | |
| 1784 | if (lowLatencyAudioNotifierDict && lowLatencyAudioNotifyStateSym && lowLatencyAudioNotifyTimestampSym && |
| 1785 | lowLatencyAudioNotifyStateVal && lowLatencyAudioNotifyTimestampVal) { |
| 1786 | lowLatencyAudioNotifierDict->setObject(aKey: lowLatencyAudioNotifyStateSym.get(), anObject: lowLatencyAudioNotifyStateVal.get()); |
| 1787 | lowLatencyAudioNotifierDict->setObject(aKey: lowLatencyAudioNotifyTimestampSym.get(), anObject: lowLatencyAudioNotifyTimestampVal.get()); |
| 1788 | } |
| 1789 | |
| 1790 | OSSharedPtr<const OSSymbol> ucClassName = OSSymbol::withCStringNoCopy(cString: "RootDomainUserClient" ); |
| 1791 | setProperty(aKey: gIOUserClientClassKey, anObject: const_cast<OSObject *>(static_cast<const OSObject *>(ucClassName.get()))); |
| 1792 | |
| 1793 | // IOBacklightDisplay can take a long time to load at boot, or it may |
| 1794 | // not load at all if you're booting with clamshell closed. We publish |
| 1795 | // 'DisplayDims' here redundantly to get it published early and at all. |
| 1796 | OSSharedPtr<OSDictionary> matching; |
| 1797 | matching = serviceMatching(className: "IOPMPowerSource" ); |
| 1798 | psIterator = getMatchingServices(matching: matching.get()); |
| 1799 | |
| 1800 | if (psIterator && psIterator->getNextObject()) { |
| 1801 | // There's at least one battery on the system, so we publish |
| 1802 | // 'DisplayDims' support for the LCD. |
| 1803 | publishFeature(feature: "DisplayDims" ); |
| 1804 | } |
| 1805 | |
| 1806 | // read swd_panic boot-arg |
| 1807 | PE_parse_boot_argn(arg_string: "swd_panic" , arg_ptr: &gSwdPanic, max_arg: sizeof(gSwdPanic)); |
| 1808 | gWillShutdownSysctlRegistered = true; |
| 1809 | |
| 1810 | #if HIBERNATION |
| 1811 | #if defined(__arm64__) |
| 1812 | #endif /* defined(__arm64__) */ |
| 1813 | IOHibernateSystemInit(this); |
| 1814 | #endif |
| 1815 | |
| 1816 | registerService(); // let clients find us |
| 1817 | |
| 1818 | return true; |
| 1819 | } |
| 1820 | |
| 1821 | //****************************************************************************** |
| 1822 | // setProperties |
| 1823 | // |
| 1824 | // Receive a setProperty call |
| 1825 | // The "System Boot" property means the system is completely booted. |
| 1826 | //****************************************************************************** |
| 1827 | |
| 1828 | IOReturn |
| 1829 | IOPMrootDomain::setProperties( OSObject * props_obj ) |
| 1830 | { |
| 1831 | IOReturn return_value = kIOReturnSuccess; |
| 1832 | OSDictionary *dict = OSDynamicCast(OSDictionary, props_obj); |
| 1833 | OSBoolean *b = NULL; |
| 1834 | OSNumber *n = NULL; |
| 1835 | const OSSymbol *key = NULL; |
| 1836 | OSObject *obj = NULL; |
| 1837 | OSSharedPtr<OSCollectionIterator> iter; |
| 1838 | |
| 1839 | if (!dict) { |
| 1840 | return kIOReturnBadArgument; |
| 1841 | } |
| 1842 | |
| 1843 | bool clientEntitled = false; |
| 1844 | { |
| 1845 | OSSharedPtr<OSObject> obj = IOUserClient::copyClientEntitlement(task: current_task(), kRootDomainEntitlementSetProperty); |
| 1846 | clientEntitled = (obj == kOSBooleanTrue); |
| 1847 | } |
| 1848 | |
| 1849 | if (!clientEntitled) { |
| 1850 | const char * errorSuffix = NULL; |
| 1851 | |
| 1852 | // IOPMSchedulePowerEvent() clients may not be entitled, but must be root. |
| 1853 | // That API can set 6 possible keys that are checked below. |
| 1854 | if ((dict->getCount() == 1) && |
| 1855 | (dict->getObject(aKey: gIOPMSettingAutoWakeSecondsKey.get()) || |
| 1856 | dict->getObject(aKey: gIOPMSettingAutoPowerSecondsKey.get()) || |
| 1857 | dict->getObject(aKey: gIOPMSettingAutoWakeCalendarKey.get()) || |
| 1858 | dict->getObject(aKey: gIOPMSettingAutoPowerCalendarKey.get()) || |
| 1859 | dict->getObject(aKey: gIOPMSettingDebugWakeRelativeKey.get()) || |
| 1860 | dict->getObject(aKey: gIOPMSettingDebugPowerRelativeKey.get()))) { |
| 1861 | return_value = IOUserClient::clientHasPrivilege(securityToken: current_task(), kIOClientPrivilegeAdministrator); |
| 1862 | if (return_value != kIOReturnSuccess) { |
| 1863 | errorSuffix = "privileged" ; |
| 1864 | } |
| 1865 | } else { |
| 1866 | return_value = kIOReturnNotPermitted; |
| 1867 | errorSuffix = "entitled" ; |
| 1868 | } |
| 1869 | |
| 1870 | if (return_value != kIOReturnSuccess) { |
| 1871 | OSSharedPtr<OSString> procName(IOCopyLogNameForPID(pid: proc_selfpid()), OSNoRetain); |
| 1872 | DLOG("%s failed, process %s is not %s\n" , __func__, |
| 1873 | procName ? procName->getCStringNoCopy() : "" , errorSuffix); |
| 1874 | return return_value; |
| 1875 | } |
| 1876 | } |
| 1877 | |
| 1878 | OSSharedPtr<const OSSymbol> publish_simulated_battery_string = OSSymbol::withCString(cString: "SoftwareSimulatedBatteries" ); |
| 1879 | OSSharedPtr<const OSSymbol> boot_complete_string = OSSymbol::withCString(cString: "System Boot Complete" ); |
| 1880 | OSSharedPtr<const OSSymbol> sys_shutdown_string = OSSymbol::withCString(cString: "System Shutdown" ); |
| 1881 | OSSharedPtr<const OSSymbol> stall_halt_string = OSSymbol::withCString(cString: "StallSystemAtHalt" ); |
| 1882 | OSSharedPtr<const OSSymbol> battery_warning_disabled_string = OSSymbol::withCString(cString: "BatteryWarningsDisabled" ); |
| 1883 | OSSharedPtr<const OSSymbol> idle_seconds_string = OSSymbol::withCString(cString: "System Idle Seconds" ); |
| 1884 | OSSharedPtr<const OSSymbol> idle_milliseconds_string = OSSymbol::withCString(cString: "System Idle Milliseconds" ); |
| 1885 | OSSharedPtr<const OSSymbol> sleepdisabled_string = OSSymbol::withCString(cString: "SleepDisabled" ); |
| 1886 | OSSharedPtr<const OSSymbol> ondeck_sleepwake_uuid_string = OSSymbol::withCString(kIOPMSleepWakeUUIDKey); |
| 1887 | OSSharedPtr<const OSSymbol> loginwindow_progress_string = OSSymbol::withCString(kIOPMLoginWindowProgressKey); |
| 1888 | OSSharedPtr<const OSSymbol> coredisplay_progress_string = OSSymbol::withCString(kIOPMCoreDisplayProgressKey); |
| 1889 | OSSharedPtr<const OSSymbol> coregraphics_progress_string = OSSymbol::withCString(kIOPMCoreGraphicsProgressKey); |
| 1890 | #if DEBUG || DEVELOPMENT |
| 1891 | OSSharedPtr<const OSSymbol> clamshell_close_string = OSSymbol::withCString("IOPMTestClamshellClose" ); |
| 1892 | OSSharedPtr<const OSSymbol> clamshell_open_string = OSSymbol::withCString("IOPMTestClamshellOpen" ); |
| 1893 | OSSharedPtr<const OSSymbol> ac_detach_string = OSSymbol::withCString("IOPMTestACDetach" ); |
| 1894 | OSSharedPtr<const OSSymbol> ac_attach_string = OSSymbol::withCString("IOPMTestACAttach" ); |
| 1895 | OSSharedPtr<const OSSymbol> desktopmode_set_string = OSSymbol::withCString("IOPMTestDesktopModeSet" ); |
| 1896 | OSSharedPtr<const OSSymbol> desktopmode_remove_string = OSSymbol::withCString("IOPMTestDesktopModeRemove" ); |
| 1897 | #endif |
| 1898 | |
| 1899 | #if HIBERNATION |
| 1900 | OSSharedPtr<const OSSymbol> hibernatemode_string = OSSymbol::withCString(kIOHibernateModeKey); |
| 1901 | OSSharedPtr<const OSSymbol> hibernatefile_string = OSSymbol::withCString(kIOHibernateFileKey); |
| 1902 | OSSharedPtr<const OSSymbol> hibernatefilemin_string = OSSymbol::withCString(kIOHibernateFileMinSizeKey); |
| 1903 | OSSharedPtr<const OSSymbol> hibernatefilemax_string = OSSymbol::withCString(kIOHibernateFileMaxSizeKey); |
| 1904 | OSSharedPtr<const OSSymbol> hibernatefreeratio_string = OSSymbol::withCString(kIOHibernateFreeRatioKey); |
| 1905 | OSSharedPtr<const OSSymbol> hibernatefreetime_string = OSSymbol::withCString(kIOHibernateFreeTimeKey); |
| 1906 | #endif |
| 1907 | |
| 1908 | iter = OSCollectionIterator::withCollection(inColl: dict); |
| 1909 | if (!iter) { |
| 1910 | return_value = kIOReturnNoMemory; |
| 1911 | goto exit; |
| 1912 | } |
| 1913 | |
| 1914 | while ((key = (const OSSymbol *) iter->getNextObject()) && |
| 1915 | (obj = dict->getObject(aKey: key))) { |
| 1916 | if (key->isEqualTo(aSymbol: publish_simulated_battery_string.get())) { |
| 1917 | if (OSDynamicCast(OSBoolean, obj)) { |
| 1918 | publishResource(key, value: kOSBooleanTrue); |
| 1919 | } |
| 1920 | } else if (key->isEqualTo(aSymbol: idle_seconds_string.get())) { |
| 1921 | if ((n = OSDynamicCast(OSNumber, obj))) { |
| 1922 | setProperty(aKey: key, anObject: n); |
| 1923 | idleMilliSeconds = n->unsigned32BitValue() * 1000; |
| 1924 | } |
| 1925 | } else if (key->isEqualTo(aSymbol: idle_milliseconds_string.get())) { |
| 1926 | if ((n = OSDynamicCast(OSNumber, obj))) { |
| 1927 | setProperty(aKey: key, anObject: n); |
| 1928 | idleMilliSeconds = n->unsigned32BitValue(); |
| 1929 | } |
| 1930 | } else if (key->isEqualTo(aSymbol: boot_complete_string.get())) { |
| 1931 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventSystemBootCompleted); |
| 1932 | } else if (key->isEqualTo(aSymbol: sys_shutdown_string.get())) { |
| 1933 | if ((b = OSDynamicCast(OSBoolean, obj))) { |
| 1934 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventSystemShutdown, arg0: (void *) b); |
| 1935 | } |
| 1936 | } else if (key->isEqualTo(aSymbol: battery_warning_disabled_string.get())) { |
| 1937 | setProperty(aKey: key, anObject: obj); |
| 1938 | } |
| 1939 | #if HIBERNATION |
| 1940 | else if (key->isEqualTo(hibernatemode_string.get()) || |
| 1941 | key->isEqualTo(hibernatefilemin_string.get()) || |
| 1942 | key->isEqualTo(hibernatefilemax_string.get()) || |
| 1943 | key->isEqualTo(hibernatefreeratio_string.get()) || |
| 1944 | key->isEqualTo(hibernatefreetime_string.get())) { |
| 1945 | if ((n = OSDynamicCast(OSNumber, obj))) { |
| 1946 | setProperty(key, n); |
| 1947 | } |
| 1948 | } else if (key->isEqualTo(hibernatefile_string.get())) { |
| 1949 | OSString * str = OSDynamicCast(OSString, obj); |
| 1950 | if (str) { |
| 1951 | setProperty(key, str); |
| 1952 | } |
| 1953 | } |
| 1954 | #endif |
| 1955 | else if (key->isEqualTo(aSymbol: sleepdisabled_string.get())) { |
| 1956 | if ((b = OSDynamicCast(OSBoolean, obj))) { |
| 1957 | setProperty(aKey: key, anObject: b); |
| 1958 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventUserDisabledSleep, arg0: (void *) b); |
| 1959 | } |
| 1960 | } else if (key->isEqualTo(aSymbol: ondeck_sleepwake_uuid_string.get())) { |
| 1961 | obj->retain(); |
| 1962 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventQueueSleepWakeUUID, arg0: (void *)obj); |
| 1963 | } else if (key->isEqualTo(aSymbol: loginwindow_progress_string.get())) { |
| 1964 | if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { |
| 1965 | uint32_t data = n->unsigned32BitValue(); |
| 1966 | pmTracer->traceComponentWakeProgress(component: kIOPMLoginWindowProgress, data); |
| 1967 | kdebugTrace(event: kPMLogComponentWakeProgress, regId: 0, param1: kIOPMLoginWindowProgress, param2: data); |
| 1968 | } |
| 1969 | } else if (key->isEqualTo(aSymbol: coredisplay_progress_string.get())) { |
| 1970 | if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { |
| 1971 | uint32_t data = n->unsigned32BitValue(); |
| 1972 | pmTracer->traceComponentWakeProgress(component: kIOPMCoreDisplayProgress, data); |
| 1973 | kdebugTrace(event: kPMLogComponentWakeProgress, regId: 0, param1: kIOPMCoreDisplayProgress, param2: data); |
| 1974 | } |
| 1975 | } else if (key->isEqualTo(aSymbol: coregraphics_progress_string.get())) { |
| 1976 | if (pmTracer && (n = OSDynamicCast(OSNumber, obj))) { |
| 1977 | uint32_t data = n->unsigned32BitValue(); |
| 1978 | pmTracer->traceComponentWakeProgress(component: kIOPMCoreGraphicsProgress, data); |
| 1979 | kdebugTrace(event: kPMLogComponentWakeProgress, regId: 0, param1: kIOPMCoreGraphicsProgress, param2: data); |
| 1980 | } |
| 1981 | } else if (key->isEqualTo(kIOPMDeepSleepEnabledKey) || |
| 1982 | key->isEqualTo(kIOPMDestroyFVKeyOnStandbyKey) || |
| 1983 | key->isEqualTo(kIOPMAutoPowerOffEnabledKey) || |
| 1984 | key->isEqualTo(aSymbol: stall_halt_string.get())) { |
| 1985 | if ((b = OSDynamicCast(OSBoolean, obj))) { |
| 1986 | setProperty(aKey: key, anObject: b); |
| 1987 | } |
| 1988 | } else if (key->isEqualTo(kIOPMDeepSleepDelayKey) || |
| 1989 | key->isEqualTo(kIOPMDeepSleepTimerKey) || |
| 1990 | key->isEqualTo(kIOPMAutoPowerOffDelayKey) || |
| 1991 | key->isEqualTo(kIOPMAutoPowerOffTimerKey)) { |
| 1992 | if ((n = OSDynamicCast(OSNumber, obj))) { |
| 1993 | setProperty(aKey: key, anObject: n); |
| 1994 | } |
| 1995 | } else if (key->isEqualTo(kIOPMUserWakeAlarmScheduledKey)) { |
| 1996 | if (kOSBooleanTrue == obj) { |
| 1997 | OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_userScheduledAlarmMask); |
| 1998 | } else { |
| 1999 | OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_userScheduledAlarmMask); |
| 2000 | } |
| 2001 | DLOG("_userScheduledAlarmMask 0x%x\n" , (uint32_t) _userScheduledAlarmMask); |
| 2002 | } |
| 2003 | #if DEBUG || DEVELOPMENT |
| 2004 | else if (key->isEqualTo(clamshell_close_string.get())) { |
| 2005 | DLOG("SetProperties: setting clamshell close\n" ); |
| 2006 | UInt32 msg = kIOPMClamshellClosed; |
| 2007 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2008 | } else if (key->isEqualTo(clamshell_open_string.get())) { |
| 2009 | DLOG("SetProperties: setting clamshell open\n" ); |
| 2010 | UInt32 msg = kIOPMClamshellOpened; |
| 2011 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2012 | } else if (key->isEqualTo(ac_detach_string.get())) { |
| 2013 | DLOG("SetProperties: setting ac detach\n" ); |
| 2014 | UInt32 msg = kIOPMSetACAdaptorConnected; |
| 2015 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2016 | } else if (key->isEqualTo(ac_attach_string.get())) { |
| 2017 | DLOG("SetProperties: setting ac attach\n" ); |
| 2018 | UInt32 msg = kIOPMSetACAdaptorConnected | kIOPMSetValue; |
| 2019 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2020 | } else if (key->isEqualTo(desktopmode_set_string.get())) { |
| 2021 | DLOG("SetProperties: setting desktopmode" ); |
| 2022 | UInt32 msg = kIOPMSetDesktopMode | kIOPMSetValue; |
| 2023 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2024 | } else if (key->isEqualTo(desktopmode_remove_string.get())) { |
| 2025 | DLOG("SetProperties: removing desktopmode\n" ); |
| 2026 | UInt32 msg = kIOPMSetDesktopMode; |
| 2027 | pmPowerStateQueue->submitPowerEvent(kPowerEventReceivedPowerNotification, (void *)(uintptr_t)msg); |
| 2028 | } |
| 2029 | #endif |
| 2030 | // Relay our allowed PM settings onto our registered PM clients |
| 2031 | else if ((allowedPMSettings->getNextIndexOfObject(anObject: key, index: 0) != (unsigned int) -1)) { |
| 2032 | return_value = setPMSetting(key, obj); |
| 2033 | if (kIOReturnSuccess != return_value) { |
| 2034 | break; |
| 2035 | } |
| 2036 | } else { |
| 2037 | DLOG("setProperties(%s) not handled\n" , key->getCStringNoCopy()); |
| 2038 | } |
| 2039 | } |
| 2040 | |
| 2041 | exit: |
| 2042 | return return_value; |
| 2043 | } |
| 2044 | |
| 2045 | // MARK: - |
| 2046 | // MARK: Aggressiveness |
| 2047 | |
| 2048 | //****************************************************************************** |
| 2049 | // setAggressiveness |
| 2050 | // |
| 2051 | // Override IOService::setAggressiveness() |
| 2052 | //****************************************************************************** |
| 2053 | |
| 2054 | IOReturn |
| 2055 | IOPMrootDomain::setAggressiveness( |
| 2056 | unsigned long type, |
| 2057 | unsigned long value ) |
| 2058 | { |
| 2059 | return setAggressiveness( type, value, options: 0 ); |
| 2060 | } |
| 2061 | |
| 2062 | /* |
| 2063 | * Private setAggressiveness() with an internal options argument. |
| 2064 | */ |
| 2065 | IOReturn |
| 2066 | IOPMrootDomain::setAggressiveness( |
| 2067 | unsigned long type, |
| 2068 | unsigned long value, |
| 2069 | IOOptionBits options ) |
| 2070 | { |
| 2071 | AggressivesRequest * entry; |
| 2072 | AggressivesRequest * request; |
| 2073 | bool found = false; |
| 2074 | |
| 2075 | if ((type > UINT_MAX) || (value > UINT_MAX)) { |
| 2076 | return kIOReturnBadArgument; |
| 2077 | } |
| 2078 | |
| 2079 | if (type == kPMMinutesToDim || type == kPMMinutesToSleep) { |
| 2080 | DLOG("setAggressiveness(%x) %s = %u\n" , |
| 2081 | (uint32_t) options, getAggressivenessTypeString((uint32_t) type), (uint32_t) value); |
| 2082 | } else { |
| 2083 | DEBUG_LOG("setAggressiveness(%x) %s = %u\n" , |
| 2084 | (uint32_t) options, getAggressivenessTypeString((uint32_t) type), (uint32_t) value); |
| 2085 | } |
| 2086 | |
| 2087 | request = IOMallocType(AggressivesRequest); |
| 2088 | request->options = options; |
| 2089 | request->dataType = kAggressivesRequestTypeRecord; |
| 2090 | request->data.record.type = (uint32_t) type; |
| 2091 | request->data.record.value = (uint32_t) value; |
| 2092 | |
| 2093 | AGGRESSIVES_LOCK(); |
| 2094 | |
| 2095 | // Update disk quick spindown flag used by getAggressiveness(). |
| 2096 | // Never merge requests with quick spindown flags set. |
| 2097 | |
| 2098 | if (options & kAggressivesOptionQuickSpindownEnable) { |
| 2099 | gAggressivesState |= kAggressivesStateQuickSpindown; |
| 2100 | } else if (options & kAggressivesOptionQuickSpindownDisable) { |
| 2101 | gAggressivesState &= ~kAggressivesStateQuickSpindown; |
| 2102 | } else { |
| 2103 | // Coalesce requests with identical aggressives types. |
| 2104 | // Deal with callers that calls us too "aggressively". |
| 2105 | |
| 2106 | queue_iterate(&aggressivesQueue, entry, AggressivesRequest *, chain) |
| 2107 | { |
| 2108 | if ((entry->dataType == kAggressivesRequestTypeRecord) && |
| 2109 | (entry->data.record.type == type) && |
| 2110 | ((entry->options & kAggressivesOptionQuickSpindownMask) == 0)) { |
| 2111 | entry->data.record.value = (uint32_t) value; |
| 2112 | found = true; |
| 2113 | break; |
| 2114 | } |
| 2115 | } |
| 2116 | } |
| 2117 | |
| 2118 | if (!found) { |
| 2119 | queue_enter(&aggressivesQueue, request, AggressivesRequest *, chain); |
| 2120 | } |
| 2121 | |
| 2122 | AGGRESSIVES_UNLOCK(); |
| 2123 | |
| 2124 | if (found) { |
| 2125 | IOFreeType(request, AggressivesRequest); |
| 2126 | } |
| 2127 | |
| 2128 | if (options & kAggressivesOptionSynchronous) { |
| 2129 | handleAggressivesRequests(); // not truly synchronous |
| 2130 | } else { |
| 2131 | thread_call_enter(call: aggressivesThreadCall); |
| 2132 | } |
| 2133 | |
| 2134 | return kIOReturnSuccess; |
| 2135 | } |
| 2136 | |
| 2137 | //****************************************************************************** |
| 2138 | // getAggressiveness |
| 2139 | // |
| 2140 | // Override IOService::setAggressiveness() |
| 2141 | // Fetch the aggressiveness factor with the given type. |
| 2142 | //****************************************************************************** |
| 2143 | |
| 2144 | IOReturn |
| 2145 | IOPMrootDomain::getAggressiveness( |
| 2146 | unsigned long type, |
| 2147 | unsigned long * outLevel ) |
| 2148 | { |
| 2149 | uint32_t value = 0; |
| 2150 | int source = 0; |
| 2151 | |
| 2152 | if (!outLevel || (type > UINT_MAX)) { |
| 2153 | return kIOReturnBadArgument; |
| 2154 | } |
| 2155 | |
| 2156 | AGGRESSIVES_LOCK(); |
| 2157 | |
| 2158 | // Disk quick spindown in effect, report value = 1 |
| 2159 | |
| 2160 | if ((gAggressivesState & kAggressivesStateQuickSpindown) && |
| 2161 | (type == kPMMinutesToSpinDown)) { |
| 2162 | value = kAggressivesMinValue; |
| 2163 | source = 1; |
| 2164 | } |
| 2165 | |
| 2166 | // Consult the pending request queue. |
| 2167 | |
| 2168 | if (!source) { |
| 2169 | AggressivesRequest * entry; |
| 2170 | |
| 2171 | queue_iterate(&aggressivesQueue, entry, AggressivesRequest *, chain) |
| 2172 | { |
| 2173 | if ((entry->dataType == kAggressivesRequestTypeRecord) && |
| 2174 | (entry->data.record.type == type) && |
| 2175 | ((entry->options & kAggressivesOptionQuickSpindownMask) == 0)) { |
| 2176 | value = entry->data.record.value; |
| 2177 | source = 2; |
| 2178 | break; |
| 2179 | } |
| 2180 | } |
| 2181 | } |
| 2182 | |
| 2183 | // Consult the backend records. |
| 2184 | |
| 2185 | if (!source && aggressivesData) { |
| 2186 | AggressivesRecord * record; |
| 2187 | int i, count; |
| 2188 | |
| 2189 | count = aggressivesData->getLength() / sizeof(AggressivesRecord); |
| 2190 | record = (AggressivesRecord *) aggressivesData->getBytesNoCopy(); |
| 2191 | |
| 2192 | for (i = 0; i < count; i++, record++) { |
| 2193 | if (record->type == type) { |
| 2194 | value = record->value; |
| 2195 | source = 3; |
| 2196 | break; |
| 2197 | } |
| 2198 | } |
| 2199 | } |
| 2200 | |
| 2201 | AGGRESSIVES_UNLOCK(); |
| 2202 | |
| 2203 | if (source) { |
| 2204 | *outLevel = (unsigned long) value; |
| 2205 | return kIOReturnSuccess; |
| 2206 | } else { |
| 2207 | DLOG("getAggressiveness type 0x%x not found\n" , (uint32_t) type); |
| 2208 | *outLevel = 0; // default return = 0, driver may not check for error |
| 2209 | return kIOReturnInvalid; |
| 2210 | } |
| 2211 | } |
| 2212 | |
| 2213 | //****************************************************************************** |
| 2214 | // joinAggressiveness |
| 2215 | // |
| 2216 | // Request from IOService to join future aggressiveness broadcasts. |
| 2217 | //****************************************************************************** |
| 2218 | |
| 2219 | IOReturn |
| 2220 | IOPMrootDomain::joinAggressiveness( |
| 2221 | IOService * service ) |
| 2222 | { |
| 2223 | AggressivesRequest * request; |
| 2224 | |
| 2225 | if (!service || (service == this)) { |
| 2226 | return kIOReturnBadArgument; |
| 2227 | } |
| 2228 | |
| 2229 | DEBUG_LOG("joinAggressiveness %s %p\n" , service->getName(), OBFUSCATE(service)); |
| 2230 | |
| 2231 | request = IOMallocType(AggressivesRequest); |
| 2232 | request->dataType = kAggressivesRequestTypeService; |
| 2233 | request->data.service.reset(p: service, OSRetain); // released by synchronizeAggressives() |
| 2234 | |
| 2235 | AGGRESSIVES_LOCK(); |
| 2236 | queue_enter(&aggressivesQueue, request, AggressivesRequest *, chain); |
| 2237 | AGGRESSIVES_UNLOCK(); |
| 2238 | |
| 2239 | thread_call_enter(call: aggressivesThreadCall); |
| 2240 | |
| 2241 | return kIOReturnSuccess; |
| 2242 | } |
| 2243 | |
| 2244 | //****************************************************************************** |
| 2245 | // handleAggressivesRequests |
| 2246 | // |
| 2247 | // Backend thread processes all incoming aggressiveness requests in the queue. |
| 2248 | //****************************************************************************** |
| 2249 | |
| 2250 | static void |
| 2251 | handleAggressivesFunction( |
| 2252 | thread_call_param_t param1, |
| 2253 | thread_call_param_t param2 ) |
| 2254 | { |
| 2255 | if (param1) { |
| 2256 | ((IOPMrootDomain *) param1)->handleAggressivesRequests(); |
| 2257 | } |
| 2258 | } |
| 2259 | |
| 2260 | void |
| 2261 | IOPMrootDomain::handleAggressivesRequests( void ) |
| 2262 | { |
| 2263 | AggressivesRecord * start; |
| 2264 | AggressivesRecord * record; |
| 2265 | AggressivesRequest * request; |
| 2266 | queue_head_t joinedQueue; |
| 2267 | int i, count; |
| 2268 | bool broadcast; |
| 2269 | bool found; |
| 2270 | bool pingSelf = false; |
| 2271 | |
| 2272 | AGGRESSIVES_LOCK(); |
| 2273 | |
| 2274 | if ((gAggressivesState & kAggressivesStateBusy) || !aggressivesData || |
| 2275 | queue_empty(&aggressivesQueue)) { |
| 2276 | goto unlock_done; |
| 2277 | } |
| 2278 | |
| 2279 | gAggressivesState |= kAggressivesStateBusy; |
| 2280 | count = aggressivesData->getLength() / sizeof(AggressivesRecord); |
| 2281 | start = (AggressivesRecord *) aggressivesData->getBytesNoCopy(); |
| 2282 | |
| 2283 | do{ |
| 2284 | broadcast = false; |
| 2285 | queue_init(&joinedQueue); |
| 2286 | |
| 2287 | do{ |
| 2288 | // Remove request from the incoming queue in FIFO order. |
| 2289 | queue_remove_first(&aggressivesQueue, request, AggressivesRequest *, chain); |
| 2290 | switch (request->dataType) { |
| 2291 | case kAggressivesRequestTypeRecord: |
| 2292 | // Update existing record if found. |
| 2293 | found = false; |
| 2294 | for (i = 0, record = start; i < count; i++, record++) { |
| 2295 | if (record->type == request->data.record.type) { |
| 2296 | found = true; |
| 2297 | |
| 2298 | if (request->options & kAggressivesOptionQuickSpindownEnable) { |
| 2299 | if ((record->flags & kAggressivesRecordFlagMinValue) == 0) { |
| 2300 | broadcast = true; |
| 2301 | record->flags |= (kAggressivesRecordFlagMinValue | |
| 2302 | kAggressivesRecordFlagModified); |
| 2303 | DLOG("disk spindown accelerated, was %u min\n" , |
| 2304 | record->value); |
| 2305 | } |
| 2306 | } else if (request->options & kAggressivesOptionQuickSpindownDisable) { |
| 2307 | if (record->flags & kAggressivesRecordFlagMinValue) { |
| 2308 | broadcast = true; |
| 2309 | record->flags |= kAggressivesRecordFlagModified; |
| 2310 | record->flags &= ~kAggressivesRecordFlagMinValue; |
| 2311 | DLOG("disk spindown restored to %u min\n" , |
| 2312 | record->value); |
| 2313 | } |
| 2314 | } else if (record->value != request->data.record.value) { |
| 2315 | record->value = request->data.record.value; |
| 2316 | if ((record->flags & kAggressivesRecordFlagMinValue) == 0) { |
| 2317 | broadcast = true; |
| 2318 | record->flags |= kAggressivesRecordFlagModified; |
| 2319 | } |
| 2320 | } |
| 2321 | break; |
| 2322 | } |
| 2323 | } |
| 2324 | |
| 2325 | // No matching record, append a new record. |
| 2326 | if (!found && |
| 2327 | ((request->options & kAggressivesOptionQuickSpindownDisable) == 0)) { |
| 2328 | AggressivesRecord newRecord; |
| 2329 | |
| 2330 | newRecord.flags = kAggressivesRecordFlagModified; |
| 2331 | newRecord.type = request->data.record.type; |
| 2332 | newRecord.value = request->data.record.value; |
| 2333 | if (request->options & kAggressivesOptionQuickSpindownEnable) { |
| 2334 | newRecord.flags |= kAggressivesRecordFlagMinValue; |
| 2335 | DLOG("disk spindown accelerated\n" ); |
| 2336 | } |
| 2337 | |
| 2338 | aggressivesData->appendValue(value: newRecord); |
| 2339 | |
| 2340 | // OSData may have switched to another (larger) buffer. |
| 2341 | count = aggressivesData->getLength() / sizeof(AggressivesRecord); |
| 2342 | start = (AggressivesRecord *) aggressivesData->getBytesNoCopy(); |
| 2343 | broadcast = true; |
| 2344 | } |
| 2345 | |
| 2346 | // Finished processing the request, release it. |
| 2347 | IOFreeType(request, AggressivesRequest); |
| 2348 | break; |
| 2349 | |
| 2350 | case kAggressivesRequestTypeService: |
| 2351 | // synchronizeAggressives() will free request. |
| 2352 | queue_enter(&joinedQueue, request, AggressivesRequest *, chain); |
| 2353 | break; |
| 2354 | |
| 2355 | default: |
| 2356 | panic("bad aggressives request type %x" , request->dataType); |
| 2357 | break; |
| 2358 | } |
| 2359 | } while (!queue_empty(&aggressivesQueue)); |
| 2360 | |
| 2361 | // Release the lock to perform work, with busy flag set. |
| 2362 | if (!queue_empty(&joinedQueue) || broadcast) { |
| 2363 | AGGRESSIVES_UNLOCK(); |
| 2364 | if (!queue_empty(&joinedQueue)) { |
| 2365 | synchronizeAggressives(services: &joinedQueue, array: start, count); |
| 2366 | } |
| 2367 | if (broadcast) { |
| 2368 | broadcastAggressives(array: start, count); |
| 2369 | } |
| 2370 | AGGRESSIVES_LOCK(); |
| 2371 | } |
| 2372 | |
| 2373 | // Remove the modified flag from all records. |
| 2374 | for (i = 0, record = start; i < count; i++, record++) { |
| 2375 | if ((record->flags & kAggressivesRecordFlagModified) && |
| 2376 | ((record->type == kPMMinutesToDim) || |
| 2377 | (record->type == kPMMinutesToSleep))) { |
| 2378 | pingSelf = true; |
| 2379 | } |
| 2380 | |
| 2381 | record->flags &= ~kAggressivesRecordFlagModified; |
| 2382 | } |
| 2383 | |
| 2384 | // Check the incoming queue again since new entries may have been |
| 2385 | // added while lock was released above. |
| 2386 | } while (!queue_empty(&aggressivesQueue)); |
| 2387 | |
| 2388 | gAggressivesState &= ~kAggressivesStateBusy; |
| 2389 | |
| 2390 | unlock_done: |
| 2391 | AGGRESSIVES_UNLOCK(); |
| 2392 | |
| 2393 | // Root domain is interested in system and display sleep slider changes. |
| 2394 | // Submit a power event to handle those changes on the PM work loop. |
| 2395 | |
| 2396 | if (pingSelf && pmPowerStateQueue) { |
| 2397 | pmPowerStateQueue->submitPowerEvent( |
| 2398 | eventType: kPowerEventPolicyStimulus, |
| 2399 | arg0: (void *) kStimulusAggressivenessChanged ); |
| 2400 | } |
| 2401 | } |
| 2402 | |
| 2403 | //****************************************************************************** |
| 2404 | // synchronizeAggressives |
| 2405 | // |
| 2406 | // Push all known aggressiveness records to one or more IOService. |
| 2407 | //****************************************************************************** |
| 2408 | |
| 2409 | void |
| 2410 | IOPMrootDomain::synchronizeAggressives( |
| 2411 | queue_head_t * joinedQueue, |
| 2412 | const AggressivesRecord * array, |
| 2413 | int count ) |
| 2414 | { |
| 2415 | OSSharedPtr<IOService> service; |
| 2416 | AggressivesRequest * request; |
| 2417 | const AggressivesRecord * record; |
| 2418 | IOPMDriverCallEntry callEntry; |
| 2419 | uint32_t value; |
| 2420 | int i; |
| 2421 | |
| 2422 | while (!queue_empty(joinedQueue)) { |
| 2423 | queue_remove_first(joinedQueue, request, AggressivesRequest *, chain); |
| 2424 | if (request->dataType == kAggressivesRequestTypeService) { |
| 2425 | // retained by joinAggressiveness(), so take ownership |
| 2426 | service = os::move(t&: request->data.service); |
| 2427 | } else { |
| 2428 | service.reset(); |
| 2429 | } |
| 2430 | |
| 2431 | IOFreeType(request, AggressivesRequest); |
| 2432 | request = NULL; |
| 2433 | |
| 2434 | if (service) { |
| 2435 | if (service->assertPMDriverCall(callEntry: &callEntry, method: kIOPMDriverCallMethodSetAggressive)) { |
| 2436 | for (i = 0, record = array; i < count; i++, record++) { |
| 2437 | value = record->value; |
| 2438 | if (record->flags & kAggressivesRecordFlagMinValue) { |
| 2439 | value = kAggressivesMinValue; |
| 2440 | } |
| 2441 | |
| 2442 | _LOG("synchronizeAggressives 0x%x = %u to %s\n" , |
| 2443 | record->type, value, service->getName()); |
| 2444 | service->setAggressiveness(type: record->type, newLevel: value); |
| 2445 | } |
| 2446 | service->deassertPMDriverCall(callEntry: &callEntry); |
| 2447 | } |
| 2448 | } |
| 2449 | } |
| 2450 | } |
| 2451 | |
| 2452 | //****************************************************************************** |
| 2453 | // broadcastAggressives |
| 2454 | // |
| 2455 | // Traverse PM tree and call setAggressiveness() for records that have changed. |
| 2456 | //****************************************************************************** |
| 2457 | |
| 2458 | void |
| 2459 | IOPMrootDomain::broadcastAggressives( |
| 2460 | const AggressivesRecord * array, |
| 2461 | int count ) |
| 2462 | { |
| 2463 | OSSharedPtr<IORegistryIterator> iter; |
| 2464 | IORegistryEntry *entry; |
| 2465 | OSSharedPtr<IORegistryEntry> child; |
| 2466 | IOPowerConnection *connect; |
| 2467 | IOService *service; |
| 2468 | const AggressivesRecord *record; |
| 2469 | IOPMDriverCallEntry callEntry; |
| 2470 | uint32_t value; |
| 2471 | int i; |
| 2472 | |
| 2473 | iter = IORegistryIterator::iterateOver( |
| 2474 | start: this, plane: gIOPowerPlane, options: kIORegistryIterateRecursively); |
| 2475 | if (iter) { |
| 2476 | do{ |
| 2477 | // !! reset the iterator |
| 2478 | iter->reset(); |
| 2479 | while ((entry = iter->getNextObject())) { |
| 2480 | connect = OSDynamicCast(IOPowerConnection, entry); |
| 2481 | if (!connect || !connect->getReadyFlag()) { |
| 2482 | continue; |
| 2483 | } |
| 2484 | |
| 2485 | child = connect->copyChildEntry(plane: gIOPowerPlane); |
| 2486 | if (child) { |
| 2487 | if ((service = OSDynamicCast(IOService, child.get()))) { |
| 2488 | if (service->assertPMDriverCall(callEntry: &callEntry, method: kIOPMDriverCallMethodSetAggressive)) { |
| 2489 | for (i = 0, record = array; i < count; i++, record++) { |
| 2490 | if (record->flags & kAggressivesRecordFlagModified) { |
| 2491 | value = record->value; |
| 2492 | if (record->flags & kAggressivesRecordFlagMinValue) { |
| 2493 | value = kAggressivesMinValue; |
| 2494 | } |
| 2495 | _LOG("broadcastAggressives %x = %u to %s\n" , |
| 2496 | record->type, value, service->getName()); |
| 2497 | service->setAggressiveness(type: record->type, newLevel: value); |
| 2498 | } |
| 2499 | } |
| 2500 | service->deassertPMDriverCall(callEntry: &callEntry); |
| 2501 | } |
| 2502 | } |
| 2503 | } |
| 2504 | } |
| 2505 | }while (!entry && !iter->isValid()); |
| 2506 | } |
| 2507 | } |
| 2508 | |
| 2509 | //***************************************** |
| 2510 | // stackshot on power button press |
| 2511 | // *************************************** |
| 2512 | static void |
| 2513 | powerButtonDownCallout(thread_call_param_t us, thread_call_param_t ) |
| 2514 | { |
| 2515 | /* Power button pressed during wake |
| 2516 | * Take a stackshot |
| 2517 | */ |
| 2518 | DEBUG_LOG("Powerbutton: down. Taking stackshot\n" ); |
| 2519 | ((IOPMrootDomain *)us)->takeStackshot(restart: false); |
| 2520 | } |
| 2521 | |
| 2522 | static void |
| 2523 | powerButtonUpCallout(thread_call_param_t us, thread_call_param_t) |
| 2524 | { |
| 2525 | /* Power button released. |
| 2526 | * Delete any stackshot data |
| 2527 | */ |
| 2528 | DEBUG_LOG("PowerButton: up callout. Delete stackshot\n" ); |
| 2529 | ((IOPMrootDomain *)us)->deleteStackshot(); |
| 2530 | } |
| 2531 | //************************************************************************* |
| 2532 | // |
| 2533 | |
| 2534 | // MARK: - |
| 2535 | // MARK: System Sleep |
| 2536 | |
| 2537 | //****************************************************************************** |
| 2538 | // startIdleSleepTimer |
| 2539 | // |
| 2540 | //****************************************************************************** |
| 2541 | |
| 2542 | void |
| 2543 | IOPMrootDomain::startIdleSleepTimer( uint32_t inMilliSeconds ) |
| 2544 | { |
| 2545 | AbsoluteTime deadline; |
| 2546 | |
| 2547 | ASSERT_GATED(); |
| 2548 | if (gNoIdleFlag) { |
| 2549 | DLOG("idle timer not set (noidle=%d)\n" , gNoIdleFlag); |
| 2550 | return; |
| 2551 | } |
| 2552 | if (inMilliSeconds) { |
| 2553 | if (inMilliSeconds < kMinimumTimeBeforeIdleSleep) { |
| 2554 | AbsoluteTime now; |
| 2555 | uint64_t nsec_since_wake; |
| 2556 | uint64_t msec_since_wake; |
| 2557 | |
| 2558 | // Adjust idle timer so it will not expire until atleast kMinimumTimeBeforeIdleSleep milliseconds |
| 2559 | // after the most recent AP wake. |
| 2560 | clock_get_uptime(result: &now); |
| 2561 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 2562 | absolutetime_to_nanoseconds(abstime: now, result: &nsec_since_wake); |
| 2563 | msec_since_wake = nsec_since_wake / NSEC_PER_MSEC; |
| 2564 | |
| 2565 | if (msec_since_wake < kMinimumTimeBeforeIdleSleep) { |
| 2566 | uint32_t newIdleTimer = kMinimumTimeBeforeIdleSleep - (uint32_t)msec_since_wake; |
| 2567 | |
| 2568 | // Ensure that our new idle timer is not less than inMilliSeconds, |
| 2569 | // as we should only be increasing the timer duration, not decreasing it |
| 2570 | if (newIdleTimer > inMilliSeconds) { |
| 2571 | DLOG("startIdleSleepTimer increasing timeout from %u to %u\n" , inMilliSeconds, newIdleTimer); |
| 2572 | inMilliSeconds = newIdleTimer; |
| 2573 | } |
| 2574 | } |
| 2575 | } |
| 2576 | clock_interval_to_deadline(interval: inMilliSeconds, scale_factor: kMillisecondScale, result: &deadline); |
| 2577 | thread_call_enter_delayed(call: extraSleepTimer, deadline); |
| 2578 | idleSleepTimerPending = true; |
| 2579 | } else { |
| 2580 | thread_call_enter(call: extraSleepTimer); |
| 2581 | } |
| 2582 | DLOG("idle timer set for %u milliseconds\n" , inMilliSeconds); |
| 2583 | } |
| 2584 | |
| 2585 | //****************************************************************************** |
| 2586 | // cancelIdleSleepTimer |
| 2587 | // |
| 2588 | //****************************************************************************** |
| 2589 | |
| 2590 | void |
| 2591 | IOPMrootDomain::cancelIdleSleepTimer( void ) |
| 2592 | { |
| 2593 | ASSERT_GATED(); |
| 2594 | if (idleSleepTimerPending) { |
| 2595 | DLOG("idle timer cancelled\n" ); |
| 2596 | thread_call_cancel(call: extraSleepTimer); |
| 2597 | idleSleepTimerPending = false; |
| 2598 | |
| 2599 | if (!assertOnWakeSecs && gIOLastWakeAbsTime) { |
| 2600 | AbsoluteTime now; |
| 2601 | clock_usec_t microsecs; |
| 2602 | clock_get_uptime(result: &now); |
| 2603 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 2604 | absolutetime_to_microtime(abstime: now, secs: &assertOnWakeSecs, microsecs: µsecs); |
| 2605 | if (assertOnWakeReport) { |
| 2606 | HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); |
| 2607 | DLOG("Updated assertOnWake %lu\n" , (unsigned long)assertOnWakeSecs); |
| 2608 | } |
| 2609 | } |
| 2610 | } |
| 2611 | } |
| 2612 | |
| 2613 | //****************************************************************************** |
| 2614 | // idleSleepTimerExpired |
| 2615 | // |
| 2616 | //****************************************************************************** |
| 2617 | |
| 2618 | static void |
| 2619 | idleSleepTimerExpired( |
| 2620 | thread_call_param_t us, thread_call_param_t ) |
| 2621 | { |
| 2622 | ((IOPMrootDomain *)us)->handleSleepTimerExpiration(); |
| 2623 | } |
| 2624 | |
| 2625 | //****************************************************************************** |
| 2626 | // handleSleepTimerExpiration |
| 2627 | // |
| 2628 | // The time between the sleep idle timeout and the next longest one has elapsed. |
| 2629 | // It's time to sleep. Start that by removing the clamp that's holding us awake. |
| 2630 | //****************************************************************************** |
| 2631 | |
| 2632 | void |
| 2633 | IOPMrootDomain::handleSleepTimerExpiration( void ) |
| 2634 | { |
| 2635 | if (!gIOPMWorkLoop->inGate()) { |
| 2636 | gIOPMWorkLoop->runAction( |
| 2637 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 2638 | &IOPMrootDomain::handleSleepTimerExpiration), |
| 2639 | target: this); |
| 2640 | return; |
| 2641 | } |
| 2642 | |
| 2643 | DLOG("sleep timer expired\n" ); |
| 2644 | ASSERT_GATED(); |
| 2645 | |
| 2646 | idleSleepTimerPending = false; |
| 2647 | setQuickSpinDownTimeout(); |
| 2648 | adjustPowerState(sleepASAP: true); |
| 2649 | } |
| 2650 | |
| 2651 | //****************************************************************************** |
| 2652 | // getTimeToIdleSleep |
| 2653 | // |
| 2654 | // Returns number of milliseconds left before going into idle sleep. |
| 2655 | // Caller has to make sure that idle sleep is allowed at the time of calling |
| 2656 | // this function |
| 2657 | //****************************************************************************** |
| 2658 | |
| 2659 | uint32_t |
| 2660 | IOPMrootDomain::getTimeToIdleSleep( void ) |
| 2661 | { |
| 2662 | AbsoluteTime now, lastActivityTime; |
| 2663 | uint64_t nanos; |
| 2664 | uint32_t minutesSinceUserInactive = 0; |
| 2665 | uint32_t sleepDelay = 0; |
| 2666 | |
| 2667 | if (!idleSleepEnabled) { |
| 2668 | return 0xffffffff; |
| 2669 | } |
| 2670 | |
| 2671 | if (userActivityTime) { |
| 2672 | lastActivityTime = userActivityTime; |
| 2673 | } else { |
| 2674 | lastActivityTime = userBecameInactiveTime; |
| 2675 | } |
| 2676 | |
| 2677 | // Ignore any lastActivityTime that predates the last system wake. |
| 2678 | // The goal is to avoid a sudden idle sleep right after a dark wake |
| 2679 | // due to sleepDelay=0 computed below. The alternative 60s minimum |
| 2680 | // timeout should be large enough to allow dark wake to complete, |
| 2681 | // at which point the idle timer will be promptly cancelled. |
| 2682 | clock_get_uptime(result: &now); |
| 2683 | if ((CMP_ABSOLUTETIME(&lastActivityTime, &gIOLastWakeAbsTime) >= 0) && |
| 2684 | (CMP_ABSOLUTETIME(&now, &lastActivityTime) > 0)) { |
| 2685 | SUB_ABSOLUTETIME(&now, &lastActivityTime); |
| 2686 | absolutetime_to_nanoseconds(abstime: now, result: &nanos); |
| 2687 | minutesSinceUserInactive = nanos / (60000000000ULL); |
| 2688 | |
| 2689 | if (minutesSinceUserInactive >= sleepSlider) { |
| 2690 | sleepDelay = 0; |
| 2691 | } else { |
| 2692 | sleepDelay = sleepSlider - minutesSinceUserInactive; |
| 2693 | } |
| 2694 | } else { |
| 2695 | DLOG("ignoring lastActivityTime 0x%qx, now 0x%qx, wake 0x%qx\n" , |
| 2696 | lastActivityTime, now, gIOLastWakeAbsTime); |
| 2697 | sleepDelay = sleepSlider; |
| 2698 | } |
| 2699 | |
| 2700 | DLOG("user inactive %u min, time to idle sleep %u min\n" , |
| 2701 | minutesSinceUserInactive, sleepDelay); |
| 2702 | |
| 2703 | return sleepDelay * 60 * 1000; |
| 2704 | } |
| 2705 | |
| 2706 | //****************************************************************************** |
| 2707 | // setQuickSpinDownTimeout |
| 2708 | // |
| 2709 | //****************************************************************************** |
| 2710 | |
| 2711 | void |
| 2712 | IOPMrootDomain::setQuickSpinDownTimeout( void ) |
| 2713 | { |
| 2714 | ASSERT_GATED(); |
| 2715 | setAggressiveness( |
| 2716 | type: kPMMinutesToSpinDown, value: 0, options: kAggressivesOptionQuickSpindownEnable ); |
| 2717 | } |
| 2718 | |
| 2719 | //****************************************************************************** |
| 2720 | // restoreUserSpinDownTimeout |
| 2721 | // |
| 2722 | //****************************************************************************** |
| 2723 | |
| 2724 | void |
| 2725 | IOPMrootDomain::restoreUserSpinDownTimeout( void ) |
| 2726 | { |
| 2727 | ASSERT_GATED(); |
| 2728 | setAggressiveness( |
| 2729 | type: kPMMinutesToSpinDown, value: 0, options: kAggressivesOptionQuickSpindownDisable ); |
| 2730 | } |
| 2731 | |
| 2732 | //****************************************************************************** |
| 2733 | // sleepSystem |
| 2734 | // |
| 2735 | //****************************************************************************** |
| 2736 | |
| 2737 | /* public */ |
| 2738 | IOReturn |
| 2739 | IOPMrootDomain::sleepSystem( void ) |
| 2740 | { |
| 2741 | return sleepSystemOptions(NULL); |
| 2742 | } |
| 2743 | |
| 2744 | /* private */ |
| 2745 | IOReturn |
| 2746 | IOPMrootDomain::sleepSystemOptions( OSDictionary *options ) |
| 2747 | { |
| 2748 | OSObject *obj = NULL; |
| 2749 | OSString *reason = NULL; |
| 2750 | /* sleepSystem is a public function, and may be called by any kernel driver. |
| 2751 | * And that's bad - drivers should sleep the system by calling |
| 2752 | * receivePowerNotification() instead. Drivers should not use sleepSystem. |
| 2753 | * |
| 2754 | * Note that user space app calls to IOPMSleepSystem() will also travel |
| 2755 | * this code path and thus be correctly identified as software sleeps. |
| 2756 | */ |
| 2757 | |
| 2758 | if (options && options->getObject(aKey: "OSSwitch" )) { |
| 2759 | // Log specific sleep cause for OS Switch hibernation |
| 2760 | return privateSleepSystem( sleepReason: kIOPMSleepReasonOSSwitchHibernate); |
| 2761 | } |
| 2762 | |
| 2763 | if (options && (obj = options->getObject(aKey: "Sleep Reason" ))) { |
| 2764 | reason = OSDynamicCast(OSString, obj); |
| 2765 | if (reason && reason->isEqualTo(kIOPMDarkWakeThermalEmergencyKey)) { |
| 2766 | return privateSleepSystem(sleepReason: kIOPMSleepReasonDarkWakeThermalEmergency); |
| 2767 | } |
| 2768 | if (reason && reason->isEqualTo(kIOPMNotificationWakeExitKey)) { |
| 2769 | return privateSleepSystem(sleepReason: kIOPMSleepReasonNotificationWakeExit); |
| 2770 | } |
| 2771 | } |
| 2772 | |
| 2773 | return privateSleepSystem( sleepReason: kIOPMSleepReasonSoftware); |
| 2774 | } |
| 2775 | |
| 2776 | /* private */ |
| 2777 | IOReturn |
| 2778 | IOPMrootDomain::privateSleepSystem( uint32_t sleepReason ) |
| 2779 | { |
| 2780 | /* Called from both gated and non-gated context */ |
| 2781 | |
| 2782 | if (!checkSystemSleepEnabled() || !pmPowerStateQueue) { |
| 2783 | return kIOReturnNotPermitted; |
| 2784 | } |
| 2785 | |
| 2786 | pmPowerStateQueue->submitPowerEvent( |
| 2787 | eventType: kPowerEventPolicyStimulus, |
| 2788 | arg0: (void *) kStimulusDemandSystemSleep, |
| 2789 | arg1: sleepReason); |
| 2790 | |
| 2791 | return kIOReturnSuccess; |
| 2792 | } |
| 2793 | |
| 2794 | //****************************************************************************** |
| 2795 | // powerChangeDone |
| 2796 | // |
| 2797 | // This overrides powerChangeDone in IOService. |
| 2798 | //****************************************************************************** |
| 2799 | void |
| 2800 | IOPMrootDomain::powerChangeDone( unsigned long previousPowerState ) |
| 2801 | { |
| 2802 | #if !__i386__ && !__x86_64__ |
| 2803 | uint64_t timeSinceReset = 0; |
| 2804 | #endif |
| 2805 | uint64_t now; |
| 2806 | unsigned long newState; |
| 2807 | clock_sec_t secs; |
| 2808 | clock_usec_t microsecs; |
| 2809 | uint32_t lastDebugWakeSeconds; |
| 2810 | clock_sec_t adjWakeTime; |
| 2811 | IOPMCalendarStruct nowCalendar; |
| 2812 | |
| 2813 | ASSERT_GATED(); |
| 2814 | newState = getPowerState(); |
| 2815 | DLOG("PowerChangeDone: %s->%s\n" , |
| 2816 | getPowerStateString((uint32_t) previousPowerState), getPowerStateString((uint32_t) getPowerState())); |
| 2817 | |
| 2818 | if (previousPowerState == newState) { |
| 2819 | return; |
| 2820 | } |
| 2821 | |
| 2822 | notifierThread = current_thread(); |
| 2823 | switch (getPowerState()) { |
| 2824 | case SLEEP_STATE: { |
| 2825 | if (kPMCalendarTypeInvalid != _aotWakeTimeCalendar.selector) { |
| 2826 | secs = 0; |
| 2827 | microsecs = 0; |
| 2828 | PEGetUTCTimeOfDay(secs: &secs, usecs: µsecs); |
| 2829 | |
| 2830 | adjWakeTime = 0; |
| 2831 | if ((kIOPMAOTModeRespectTimers & _aotMode) && (_calendarWakeAlarmUTC < _aotWakeTimeUTC)) { |
| 2832 | IOLog(format: "use _calendarWakeAlarmUTC\n" ); |
| 2833 | adjWakeTime = _calendarWakeAlarmUTC; |
| 2834 | } else if (kIOPMWakeEventAOTExitFlags & _aotPendingFlags) { |
| 2835 | IOLog(format: "accelerate _aotWakeTime for exit\n" ); |
| 2836 | adjWakeTime = secs; |
| 2837 | } else if (kIOPMDriverAssertionLevelOn == getPMAssertionLevel(whichAssertionBits: kIOPMDriverAssertionCPUBit)) { |
| 2838 | IOLog(format: "accelerate _aotWakeTime for assertion\n" ); |
| 2839 | adjWakeTime = secs; |
| 2840 | } |
| 2841 | if (adjWakeTime) { |
| 2842 | IOPMConvertSecondsToCalendar(secs: adjWakeTime, dt: &_aotWakeTimeCalendar); |
| 2843 | } |
| 2844 | |
| 2845 | IOPMConvertSecondsToCalendar(secs, dt: &nowCalendar); |
| 2846 | IOLog(format: "aotSleep at " YMDTF " sched: " YMDTF "\n" , YMDT(&nowCalendar), YMDT(&_aotWakeTimeCalendar)); |
| 2847 | |
| 2848 | IOReturn __unused ret = setMaintenanceWakeCalendar(&_aotWakeTimeCalendar); |
| 2849 | assert(kIOReturnSuccess == ret); |
| 2850 | } |
| 2851 | if (_aotLastWakeTime) { |
| 2852 | _aotMetrics->totalTime += mach_absolute_time() - _aotLastWakeTime; |
| 2853 | if (_aotMetrics->sleepCount && (_aotMetrics->sleepCount <= kIOPMAOTMetricsKernelWakeCountMax)) { |
| 2854 | strlcpy(dst: &_aotMetrics->kernelWakeReason[_aotMetrics->sleepCount - 1][0], |
| 2855 | src: gWakeReasonString, |
| 2856 | n: sizeof(_aotMetrics->kernelWakeReason[_aotMetrics->sleepCount])); |
| 2857 | } |
| 2858 | } |
| 2859 | _aotPendingFlags &= ~kIOPMWakeEventAOTPerCycleFlags; |
| 2860 | if (_aotTimerScheduled) { |
| 2861 | _aotTimerES->cancelTimeout(); |
| 2862 | _aotTimerScheduled = false; |
| 2863 | } |
| 2864 | acceptSystemWakeEvents(control: kAcceptSystemWakeEvents_Enable); |
| 2865 | |
| 2866 | // re-enable this timer for next sleep |
| 2867 | cancelIdleSleepTimer(); |
| 2868 | |
| 2869 | if (clamshellExists) { |
| 2870 | #if DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY |
| 2871 | if (gClamshellFlags & kClamshell_WAR_58009435) { |
| 2872 | // Disable clamshell sleep until system has completed full wake. |
| 2873 | // This prevents a system sleep request (due to a clamshell close) |
| 2874 | // from being queued until the end of system full wake - even if |
| 2875 | // other clamshell disable bits outside of our control is wrong. |
| 2876 | setClamShellSleepDisable(true, kClamshellSleepDisableInternal); |
| 2877 | } |
| 2878 | #endif |
| 2879 | |
| 2880 | // Log the last known clamshell state before system sleep |
| 2881 | DLOG("clamshell closed %d, disabled %d/%x, desktopMode %d, ac %d\n" , |
| 2882 | clamshellClosed, clamshellDisabled, clamshellSleepDisableMask, |
| 2883 | desktopMode, acAdaptorConnected); |
| 2884 | } |
| 2885 | |
| 2886 | clock_get_calendar_absolute_and_microtime(secs: &secs, microsecs: µsecs, abstime: &now); |
| 2887 | logtime(secs); |
| 2888 | gIOLastSleepTime.tv_sec = secs; |
| 2889 | gIOLastSleepTime.tv_usec = microsecs; |
| 2890 | if (!_aotLastWakeTime) { |
| 2891 | gIOLastUserSleepTime = gIOLastSleepTime; |
| 2892 | } |
| 2893 | |
| 2894 | gIOLastWakeTime.tv_sec = 0; |
| 2895 | gIOLastWakeTime.tv_usec = 0; |
| 2896 | gIOLastSleepAbsTime = now; |
| 2897 | |
| 2898 | if (wake2DarkwakeDelay && sleepDelaysReport) { |
| 2899 | clock_sec_t wake2DarkwakeSecs, darkwake2SleepSecs; |
| 2900 | // Update 'wake2DarkwakeDelay' histogram if this is a fullwake->sleep transition |
| 2901 | |
| 2902 | SUB_ABSOLUTETIME(&now, &ts_sleepStart); |
| 2903 | absolutetime_to_microtime(abstime: now, secs: &darkwake2SleepSecs, microsecs: µsecs); |
| 2904 | absolutetime_to_microtime(abstime: wake2DarkwakeDelay, secs: &wake2DarkwakeSecs, microsecs: µsecs); |
| 2905 | HISTREPORT_TALLYVALUE(sleepDelaysReport, |
| 2906 | (int64_t)(wake2DarkwakeSecs + darkwake2SleepSecs)); |
| 2907 | |
| 2908 | DLOG("Updated sleepDelaysReport %lu %lu\n" , (unsigned long)wake2DarkwakeSecs, (unsigned long)darkwake2SleepSecs); |
| 2909 | wake2DarkwakeDelay = 0; |
| 2910 | } |
| 2911 | #if HIBERNATION |
| 2912 | LOG("System %sSleep\n" , gIOHibernateState ? "Safe" : "" ); |
| 2913 | #if (DEVELOPMENT || DEBUG) |
| 2914 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 2915 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 2916 | "System State" , |
| 2917 | gIOHibernateState ? "Enter Hibernate" : "Enter Sleep" |
| 2918 | ); |
| 2919 | #endif /* DEVELOPMENT || DEBUG */ |
| 2920 | IOHibernateSystemHasSlept(); |
| 2921 | |
| 2922 | evaluateSystemSleepPolicyFinal(); |
| 2923 | #else |
| 2924 | LOG("System Sleep\n" ); |
| 2925 | #if (DEVELOPMENT || DEBUG) |
| 2926 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 2927 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 2928 | "System State" , "Enter Sleep" ); |
| 2929 | #endif /* DEVELOPMENT || DEBUG */ |
| 2930 | #endif |
| 2931 | if (thermalWarningState) { |
| 2932 | OSSharedPtr<const OSSymbol> event = OSSymbol::withCString(kIOPMThermalLevelWarningKey); |
| 2933 | if (event) { |
| 2934 | systemPowerEventOccurred(event: event.get(), intValue: kIOPMThermalLevelUnknown); |
| 2935 | } |
| 2936 | } |
| 2937 | assertOnWakeSecs = 0; |
| 2938 | lowBatteryCondition = false; |
| 2939 | thermalEmergencyState = false; |
| 2940 | |
| 2941 | #if DEVELOPMENT || DEBUG |
| 2942 | extern int g_should_log_clock_adjustments; |
| 2943 | if (g_should_log_clock_adjustments) { |
| 2944 | clock_sec_t secs = 0; |
| 2945 | clock_usec_t microsecs = 0; |
| 2946 | uint64_t now_b = mach_absolute_time(); |
| 2947 | |
| 2948 | secs = 0; |
| 2949 | microsecs = 0; |
| 2950 | PEGetUTCTimeOfDay(&secs, µsecs); |
| 2951 | |
| 2952 | uint64_t now_a = mach_absolute_time(); |
| 2953 | os_log(OS_LOG_DEFAULT, "%s PMU before going to sleep %lu s %d u %llu abs_b_PEG %llu abs_a_PEG \n" , |
| 2954 | __func__, (unsigned long)secs, microsecs, now_b, now_a); |
| 2955 | } |
| 2956 | #endif |
| 2957 | |
| 2958 | getPlatform()->sleepKernel(); |
| 2959 | |
| 2960 | // The CPU(s) are off at this point, |
| 2961 | // Code will resume execution here upon wake. |
| 2962 | |
| 2963 | clock_get_uptime(result: &gIOLastWakeAbsTime); |
| 2964 | IOLog(format: "gIOLastWakeAbsTime: %lld\n" , gIOLastWakeAbsTime); |
| 2965 | #if DEVELOPMENT || DEBUG |
| 2966 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 2967 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 2968 | "System State" , "Waking Up" |
| 2969 | ); |
| 2970 | #endif /* DEVELOPMENT || DEBUG */ |
| 2971 | _highestCapability = 0; |
| 2972 | |
| 2973 | #if HIBERNATION |
| 2974 | IOHibernateSystemWake(); |
| 2975 | #endif |
| 2976 | |
| 2977 | // sleep transition complete |
| 2978 | gSleepOrShutdownPending = 0; |
| 2979 | |
| 2980 | // trip the reset of the calendar clock |
| 2981 | clock_wakeup_calendar(); |
| 2982 | clock_get_calendar_microtime(secs: &secs, microsecs: µsecs); |
| 2983 | gIOLastWakeTime.tv_sec = secs; |
| 2984 | gIOLastWakeTime.tv_usec = microsecs; |
| 2985 | |
| 2986 | // aot |
| 2987 | if (_aotWakeTimeCalendar.selector != kPMCalendarTypeInvalid) { |
| 2988 | _aotWakeTimeCalendar.selector = kPMCalendarTypeInvalid; |
| 2989 | secs = 0; |
| 2990 | microsecs = 0; |
| 2991 | PEGetUTCTimeOfDay(secs: &secs, usecs: µsecs); |
| 2992 | IOPMConvertSecondsToCalendar(secs, dt: &nowCalendar); |
| 2993 | IOLog(format: "aotWake at " YMDTF " sched: " YMDTF "\n" , YMDT(&nowCalendar), YMDT(&_aotWakeTimeCalendar)); |
| 2994 | _aotMetrics->sleepCount++; |
| 2995 | _aotLastWakeTime = gIOLastWakeAbsTime; |
| 2996 | if (_aotMetrics->sleepCount <= kIOPMAOTMetricsKernelWakeCountMax) { |
| 2997 | _aotMetrics->kernelSleepTime[_aotMetrics->sleepCount - 1] |
| 2998 | = (((uint64_t) gIOLastSleepTime.tv_sec) << 10) + (gIOLastSleepTime.tv_usec / 1000); |
| 2999 | _aotMetrics->kernelWakeTime[_aotMetrics->sleepCount - 1] |
| 3000 | = (((uint64_t) gIOLastWakeTime.tv_sec) << 10) + (gIOLastWakeTime.tv_usec / 1000); |
| 3001 | } |
| 3002 | |
| 3003 | if (_aotTestTime) { |
| 3004 | if (_aotWakeTimeUTC <= secs) { |
| 3005 | _aotTestTime = _aotTestTime + _aotTestInterval; |
| 3006 | } |
| 3007 | setWakeTime(_aotTestTime); |
| 3008 | } |
| 3009 | } |
| 3010 | |
| 3011 | #if HIBERNATION |
| 3012 | LOG("System %sWake\n" , gIOHibernateState ? "SafeSleep " : "" ); |
| 3013 | #endif |
| 3014 | |
| 3015 | lastSleepReason = 0; |
| 3016 | |
| 3017 | lastDebugWakeSeconds = _debugWakeSeconds; |
| 3018 | _debugWakeSeconds = 0; |
| 3019 | _scheduledAlarmMask = 0; |
| 3020 | _nextScheduledAlarmType = NULL; |
| 3021 | |
| 3022 | darkWakeExit = false; |
| 3023 | darkWakePowerClamped = false; |
| 3024 | darkWakePostTickle = false; |
| 3025 | darkWakeHibernateError = false; |
| 3026 | darkWakeToSleepASAP = true; |
| 3027 | darkWakeLogClamp = true; |
| 3028 | sleepTimerMaintenance = false; |
| 3029 | sleepToStandby = false; |
| 3030 | wranglerTickled = false; |
| 3031 | userWasActive = false; |
| 3032 | isRTCAlarmWake = false; |
| 3033 | clamshellIgnoreClose = false; |
| 3034 | fullWakeReason = kFullWakeReasonNone; |
| 3035 | |
| 3036 | #if defined(__i386__) || defined(__x86_64__) |
| 3037 | kdebugTrace(kPMLogSystemWake, 0, 0, 0); |
| 3038 | |
| 3039 | OSSharedPtr<OSObject> wakeTypeProp = copyProperty(kIOPMRootDomainWakeTypeKey); |
| 3040 | OSSharedPtr<OSObject> wakeReasonProp = copyProperty(kIOPMRootDomainWakeReasonKey); |
| 3041 | OSString * wakeType = OSDynamicCast(OSString, wakeTypeProp.get()); |
| 3042 | OSString * wakeReason = OSDynamicCast(OSString, wakeReasonProp.get()); |
| 3043 | |
| 3044 | if (wakeReason && (wakeReason->getLength() >= 2) && |
| 3045 | gWakeReasonString[0] == '\0') { |
| 3046 | WAKEEVENT_LOCK(); |
| 3047 | // Until the platform driver can claim its wake reasons |
| 3048 | strlcat(gWakeReasonString, wakeReason->getCStringNoCopy(), |
| 3049 | sizeof(gWakeReasonString)); |
| 3050 | if (!gWakeReasonSysctlRegistered) { |
| 3051 | gWakeReasonSysctlRegistered = true; |
| 3052 | } |
| 3053 | WAKEEVENT_UNLOCK(); |
| 3054 | } |
| 3055 | |
| 3056 | if (wakeType && wakeType->isEqualTo(kIOPMrootDomainWakeTypeLowBattery)) { |
| 3057 | lowBatteryCondition = true; |
| 3058 | darkWakeMaintenance = true; |
| 3059 | } else { |
| 3060 | #if HIBERNATION |
| 3061 | OSSharedPtr<OSObject> hibOptionsProp = copyProperty(kIOHibernateOptionsKey); |
| 3062 | OSNumber * hibOptions = OSDynamicCast( OSNumber, hibOptionsProp.get()); |
| 3063 | if (hibernateAborted || ((hibOptions && |
| 3064 | !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake)))) { |
| 3065 | // Hibernate aborted, or EFI brought up graphics |
| 3066 | darkWakeExit = true; |
| 3067 | if (hibernateAborted) { |
| 3068 | DLOG("Hibernation aborted\n" ); |
| 3069 | } else { |
| 3070 | DLOG("EFI brought up graphics. Going to full wake. HibOptions: 0x%x\n" , hibOptions->unsigned32BitValue()); |
| 3071 | } |
| 3072 | } else |
| 3073 | #endif |
| 3074 | if (wakeType && ( |
| 3075 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeUser) || |
| 3076 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm))) { |
| 3077 | // User wake or RTC alarm |
| 3078 | darkWakeExit = true; |
| 3079 | if (wakeType->isEqualTo(kIOPMRootDomainWakeTypeAlarm)) { |
| 3080 | isRTCAlarmWake = true; |
| 3081 | } |
| 3082 | } else if (wakeType && |
| 3083 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepTimer)) { |
| 3084 | // SMC standby timer trumps SleepX |
| 3085 | darkWakeMaintenance = true; |
| 3086 | sleepTimerMaintenance = true; |
| 3087 | } else if ((lastDebugWakeSeconds != 0) && |
| 3088 | ((gDarkWakeFlags & kDarkWakeFlagAlarmIsDark) == 0)) { |
| 3089 | // SleepX before maintenance |
| 3090 | darkWakeExit = true; |
| 3091 | } else if (wakeType && |
| 3092 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeMaintenance)) { |
| 3093 | darkWakeMaintenance = true; |
| 3094 | } else if (wakeType && |
| 3095 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeSleepService)) { |
| 3096 | darkWakeMaintenance = true; |
| 3097 | darkWakeSleepService = true; |
| 3098 | #if HIBERNATION |
| 3099 | if (kIOHibernateStateWakingFromHibernate == gIOHibernateState) { |
| 3100 | sleepToStandby = true; |
| 3101 | } |
| 3102 | #endif |
| 3103 | } else if (wakeType && |
| 3104 | wakeType->isEqualTo(kIOPMRootDomainWakeTypeHibernateError)) { |
| 3105 | darkWakeMaintenance = true; |
| 3106 | darkWakeHibernateError = true; |
| 3107 | } else { |
| 3108 | // Unidentified wake source, resume to full wake if debug |
| 3109 | // alarm is pending. |
| 3110 | |
| 3111 | if (lastDebugWakeSeconds && |
| 3112 | (!wakeReason || wakeReason->isEqualTo("" ))) { |
| 3113 | darkWakeExit = true; |
| 3114 | } |
| 3115 | } |
| 3116 | } |
| 3117 | |
| 3118 | if (darkWakeExit) { |
| 3119 | darkWakeToSleepASAP = false; |
| 3120 | fullWakeReason = kFullWakeReasonLocalUser; |
| 3121 | reportUserInput(); |
| 3122 | } else if (displayPowerOnRequested && checkSystemCanSustainFullWake()) { |
| 3123 | handleSetDisplayPowerOn(true); |
| 3124 | } else if (!darkWakeMaintenance) { |
| 3125 | // Early/late tickle for non-maintenance wake. |
| 3126 | if ((gDarkWakeFlags & kDarkWakeFlagPromotionMask) != kDarkWakeFlagPromotionNone) { |
| 3127 | darkWakePostTickle = true; |
| 3128 | } |
| 3129 | } |
| 3130 | #else /* !__i386__ && !__x86_64__ */ |
| 3131 | timeSinceReset = ml_get_time_since_reset(); |
| 3132 | kdebugTrace(event: kPMLogSystemWake, regId: 0, param1: (uintptr_t)(timeSinceReset >> 32), param2: (uintptr_t) timeSinceReset); |
| 3133 | |
| 3134 | if ((gDarkWakeFlags & kDarkWakeFlagPromotionMask) == kDarkWakeFlagPromotionEarly) { |
| 3135 | wranglerTickled = true; |
| 3136 | fullWakeReason = kFullWakeReasonLocalUser; |
| 3137 | requestUserActive(driver: this, reason: "Full wake on dark wake promotion boot-arg" ); |
| 3138 | } else if ((lastDebugWakeSeconds != 0) && !(gDarkWakeFlags & kDarkWakeFlagAlarmIsDark)) { |
| 3139 | isRTCAlarmWake = true; |
| 3140 | fullWakeReason = kFullWakeReasonLocalUser; |
| 3141 | requestUserActive(driver: this, reason: "RTC debug alarm" ); |
| 3142 | } else { |
| 3143 | #if HIBERNATION |
| 3144 | OSSharedPtr<OSObject> hibOptionsProp = copyProperty(kIOHibernateOptionsKey); |
| 3145 | OSNumber * hibOptions = OSDynamicCast(OSNumber, hibOptionsProp.get()); |
| 3146 | if (hibOptions && !(hibOptions->unsigned32BitValue() & kIOHibernateOptionDarkWake)) { |
| 3147 | fullWakeReason = kFullWakeReasonLocalUser; |
| 3148 | requestUserActive(this, "hibernate user wake" ); |
| 3149 | } |
| 3150 | #endif |
| 3151 | } |
| 3152 | |
| 3153 | // stay awake for at least 30 seconds |
| 3154 | startIdleSleepTimer(inMilliSeconds: 30 * 1000); |
| 3155 | #endif |
| 3156 | sleepCnt++; |
| 3157 | |
| 3158 | thread_call_enter(call: updateConsoleUsersEntry); |
| 3159 | |
| 3160 | // Skip AOT_STATE if we are waking up from an RTC timer. |
| 3161 | // This check needs to be done after the epoch change is processed |
| 3162 | // and before the changePowerStateWithTagToPriv() call below. |
| 3163 | WAKEEVENT_LOCK(); |
| 3164 | aotShouldExit(checkTimeSet: false, software: false); |
| 3165 | WAKEEVENT_UNLOCK(); |
| 3166 | |
| 3167 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), tag: kCPSReasonWake); |
| 3168 | break; |
| 3169 | } |
| 3170 | #if !__i386__ && !__x86_64__ |
| 3171 | case ON_STATE: |
| 3172 | case AOT_STATE: |
| 3173 | { |
| 3174 | DLOG("Force re-evaluating aggressiveness\n" ); |
| 3175 | /* Force re-evaluate the aggressiveness values to set appropriate idle sleep timer */ |
| 3176 | pmPowerStateQueue->submitPowerEvent( |
| 3177 | eventType: kPowerEventPolicyStimulus, |
| 3178 | arg0: (void *) kStimulusNoIdleSleepPreventers ); |
| 3179 | |
| 3180 | // After changing to ON_STATE, invalidate any previously queued |
| 3181 | // request to change to a state less than ON_STATE. This isn't |
| 3182 | // necessary for AOT_STATE or if the device has only one running |
| 3183 | // state since the changePowerStateToPriv() issued at the tail |
| 3184 | // end of SLEEP_STATE case should take care of that. |
| 3185 | if (getPowerState() == ON_STATE) { |
| 3186 | changePowerStateWithTagToPriv(ordinal: ON_STATE, tag: kCPSReasonWake); |
| 3187 | } |
| 3188 | break; |
| 3189 | } |
| 3190 | #endif /* !__i386__ && !__x86_64__ */ |
| 3191 | } |
| 3192 | notifierThread = NULL; |
| 3193 | } |
| 3194 | |
| 3195 | //****************************************************************************** |
| 3196 | // requestPowerDomainState |
| 3197 | // |
| 3198 | // Extend implementation in IOService. Running on PM work loop thread. |
| 3199 | //****************************************************************************** |
| 3200 | |
| 3201 | IOReturn |
| 3202 | IOPMrootDomain::requestPowerDomainState( |
| 3203 | IOPMPowerFlags childDesire, |
| 3204 | IOPowerConnection * childConnection, |
| 3205 | unsigned long specification ) |
| 3206 | { |
| 3207 | // Idle and system sleep prevention flags affects driver desire. |
| 3208 | // Children desire are irrelevant so they are cleared. |
| 3209 | |
| 3210 | return super::requestPowerDomainState(desiredState: 0, whichChild: childConnection, specificationFlags: specification); |
| 3211 | } |
| 3212 | |
| 3213 | |
| 3214 | static void |
| 3215 | makeSleepPreventersListLog(const OSSharedPtr<OSSet> &preventers, char *buf, size_t buf_size) |
| 3216 | { |
| 3217 | if (!preventers->getCount()) { |
| 3218 | return; |
| 3219 | } |
| 3220 | |
| 3221 | char *buf_iter = buf + strlen(s: buf); |
| 3222 | char *buf_end = buf + buf_size; |
| 3223 | |
| 3224 | OSSharedPtr<OSCollectionIterator> iterator = OSCollectionIterator::withCollection(inColl: preventers.get()); |
| 3225 | OSObject *obj = NULL; |
| 3226 | |
| 3227 | while ((obj = iterator->getNextObject())) { |
| 3228 | IOService *srv = OSDynamicCast(IOService, obj); |
| 3229 | if (buf_iter < buf_end) { |
| 3230 | buf_iter += snprintf(buf_iter, count: buf_end - buf_iter, " %s" , srv->getName()); |
| 3231 | } else { |
| 3232 | DLOG("Print buffer exhausted for sleep preventers list\n" ); |
| 3233 | break; |
| 3234 | } |
| 3235 | } |
| 3236 | } |
| 3237 | |
| 3238 | //****************************************************************************** |
| 3239 | // updatePreventIdleSleepList |
| 3240 | // |
| 3241 | // Called by IOService on PM work loop. |
| 3242 | // Returns true if PM policy recognized the driver's desire to prevent idle |
| 3243 | // sleep and updated the list of idle sleep preventers. Returns false otherwise |
| 3244 | //****************************************************************************** |
| 3245 | |
| 3246 | bool |
| 3247 | IOPMrootDomain::updatePreventIdleSleepList( |
| 3248 | IOService * service, bool addNotRemove) |
| 3249 | { |
| 3250 | unsigned int oldCount; |
| 3251 | |
| 3252 | oldCount = idleSleepPreventersCount(); |
| 3253 | return updatePreventIdleSleepListInternal(service, addNotRemove, oldCount); |
| 3254 | } |
| 3255 | |
| 3256 | bool |
| 3257 | IOPMrootDomain::updatePreventIdleSleepListInternal( |
| 3258 | IOService * service, bool addNotRemove, unsigned int oldCount) |
| 3259 | { |
| 3260 | unsigned int newCount; |
| 3261 | |
| 3262 | ASSERT_GATED(); |
| 3263 | |
| 3264 | #if defined(XNU_TARGET_OS_OSX) |
| 3265 | // Only the display wrangler and no-idle-sleep kernel assertions |
| 3266 | // can prevent idle sleep. The kIOPMPreventIdleSleep capability flag |
| 3267 | // reported by drivers in their power state table is ignored. |
| 3268 | if (service && (service != wrangler) && (service != this)) { |
| 3269 | return false; |
| 3270 | } |
| 3271 | #endif |
| 3272 | |
| 3273 | if (service) { |
| 3274 | if (addNotRemove) { |
| 3275 | preventIdleSleepList->setObject(service); |
| 3276 | DLOG("Added %s to idle sleep preventers list (Total %u)\n" , |
| 3277 | service->getName(), preventIdleSleepList->getCount()); |
| 3278 | } else if (preventIdleSleepList->member(anObject: service)) { |
| 3279 | preventIdleSleepList->removeObject(anObject: service); |
| 3280 | DLOG("Removed %s from idle sleep preventers list (Total %u)\n" , |
| 3281 | service->getName(), preventIdleSleepList->getCount()); |
| 3282 | } |
| 3283 | |
| 3284 | if (preventIdleSleepList->getCount()) { |
| 3285 | char buf[256] = "Idle Sleep Preventers:" ; |
| 3286 | makeSleepPreventersListLog(preventers: preventIdleSleepList, buf, buf_size: sizeof(buf)); |
| 3287 | DLOG("%s\n" , buf); |
| 3288 | } |
| 3289 | } |
| 3290 | |
| 3291 | newCount = idleSleepPreventersCount(); |
| 3292 | |
| 3293 | if ((oldCount == 0) && (newCount != 0)) { |
| 3294 | // Driver added to empty prevent list. |
| 3295 | // Update the driver desire to prevent idle sleep. |
| 3296 | // Driver desire does not prevent demand sleep. |
| 3297 | |
| 3298 | changePowerStateWithTagTo(ordinal: getRUN_STATE(), tag: kCPSReasonIdleSleepPrevent); |
| 3299 | } else if ((oldCount != 0) && (newCount == 0)) { |
| 3300 | // Last driver removed from prevent list. |
| 3301 | // Drop the driver clamp to allow idle sleep. |
| 3302 | |
| 3303 | changePowerStateWithTagTo(ordinal: SLEEP_STATE, tag: kCPSReasonIdleSleepAllow); |
| 3304 | evaluatePolicy( stimulus: kStimulusNoIdleSleepPreventers ); |
| 3305 | } |
| 3306 | messageClient(kIOPMMessageIdleSleepPreventers, client: systemCapabilityNotifier.get(), |
| 3307 | messageArgument: &newCount, argSize: sizeof(newCount)); |
| 3308 | |
| 3309 | #if defined(XNU_TARGET_OS_OSX) |
| 3310 | if (addNotRemove && (service == wrangler) && !checkSystemCanSustainFullWake()) { |
| 3311 | DLOG("Cannot cancel idle sleep\n" ); |
| 3312 | return false; // do not idle-cancel |
| 3313 | } |
| 3314 | #endif |
| 3315 | |
| 3316 | return true; |
| 3317 | } |
| 3318 | |
| 3319 | //****************************************************************************** |
| 3320 | // startSpinDump |
| 3321 | //****************************************************************************** |
| 3322 | |
| 3323 | void |
| 3324 | IOPMrootDomain::startSpinDump(uint32_t spindumpKind) |
| 3325 | { |
| 3326 | messageClients(kIOPMMessageLaunchBootSpinDump, argument: (void *)(uintptr_t)spindumpKind); |
| 3327 | } |
| 3328 | |
| 3329 | //****************************************************************************** |
| 3330 | // preventSystemSleepListUpdate |
| 3331 | // |
| 3332 | // Called by IOService on PM work loop. |
| 3333 | //****************************************************************************** |
| 3334 | |
| 3335 | void |
| 3336 | IOPMrootDomain::updatePreventSystemSleepList( |
| 3337 | IOService * service, bool addNotRemove ) |
| 3338 | { |
| 3339 | unsigned int oldCount, newCount; |
| 3340 | |
| 3341 | ASSERT_GATED(); |
| 3342 | if (this == service) { |
| 3343 | return; |
| 3344 | } |
| 3345 | |
| 3346 | oldCount = preventSystemSleepList->getCount(); |
| 3347 | if (addNotRemove) { |
| 3348 | preventSystemSleepList->setObject(service); |
| 3349 | DLOG("Added %s to system sleep preventers list (Total %u)\n" , |
| 3350 | service->getName(), preventSystemSleepList->getCount()); |
| 3351 | if (!assertOnWakeSecs && gIOLastWakeAbsTime) { |
| 3352 | AbsoluteTime now; |
| 3353 | clock_usec_t microsecs; |
| 3354 | clock_get_uptime(result: &now); |
| 3355 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 3356 | absolutetime_to_microtime(abstime: now, secs: &assertOnWakeSecs, microsecs: µsecs); |
| 3357 | if (assertOnWakeReport) { |
| 3358 | HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); |
| 3359 | DLOG("Updated assertOnWake %lu\n" , (unsigned long)assertOnWakeSecs); |
| 3360 | } |
| 3361 | } |
| 3362 | } else if (preventSystemSleepList->member(anObject: service)) { |
| 3363 | preventSystemSleepList->removeObject(anObject: service); |
| 3364 | DLOG("Removed %s from system sleep preventers list (Total %u)\n" , |
| 3365 | service->getName(), preventSystemSleepList->getCount()); |
| 3366 | |
| 3367 | if ((oldCount != 0) && (preventSystemSleepList->getCount() == 0)) { |
| 3368 | // Lost all system sleep preventers. |
| 3369 | // Send stimulus if system sleep was blocked, and is in dark wake. |
| 3370 | evaluatePolicy( stimulus: kStimulusDarkWakeEvaluate ); |
| 3371 | } |
| 3372 | } |
| 3373 | |
| 3374 | newCount = preventSystemSleepList->getCount(); |
| 3375 | if (newCount) { |
| 3376 | char buf[256] = "System Sleep Preventers:" ; |
| 3377 | makeSleepPreventersListLog(preventers: preventSystemSleepList, buf, buf_size: sizeof(buf)); |
| 3378 | DLOG("%s\n" , buf); |
| 3379 | } |
| 3380 | |
| 3381 | messageClient(kIOPMMessageSystemSleepPreventers, client: systemCapabilityNotifier.get(), |
| 3382 | messageArgument: &newCount, argSize: sizeof(newCount)); |
| 3383 | } |
| 3384 | |
| 3385 | void |
| 3386 | IOPMrootDomain::copySleepPreventersList(OSArray **idleSleepList, OSArray **systemSleepList) |
| 3387 | { |
| 3388 | OSSharedPtr<OSCollectionIterator> iterator; |
| 3389 | OSObject *object = NULL; |
| 3390 | OSSharedPtr<OSArray> array; |
| 3391 | |
| 3392 | if (!gIOPMWorkLoop->inGate()) { |
| 3393 | gIOPMWorkLoop->runAction( |
| 3394 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 3395 | &IOPMrootDomain::IOPMrootDomain::copySleepPreventersList), |
| 3396 | target: this, arg0: (void *)idleSleepList, arg1: (void *)systemSleepList); |
| 3397 | return; |
| 3398 | } |
| 3399 | |
| 3400 | if (idleSleepList && preventIdleSleepList && (preventIdleSleepList->getCount() != 0)) { |
| 3401 | iterator = OSCollectionIterator::withCollection(inColl: preventIdleSleepList.get()); |
| 3402 | array = OSArray::withCapacity(capacity: 5); |
| 3403 | |
| 3404 | if (iterator && array) { |
| 3405 | while ((object = iterator->getNextObject())) { |
| 3406 | IOService *service = OSDynamicCast(IOService, object); |
| 3407 | if (service) { |
| 3408 | OSSharedPtr<const OSSymbol> name = service->copyName(); |
| 3409 | if (name) { |
| 3410 | array->setObject(name.get()); |
| 3411 | } |
| 3412 | } |
| 3413 | } |
| 3414 | } |
| 3415 | *idleSleepList = array.detach(); |
| 3416 | } |
| 3417 | |
| 3418 | if (systemSleepList && preventSystemSleepList && (preventSystemSleepList->getCount() != 0)) { |
| 3419 | iterator = OSCollectionIterator::withCollection(inColl: preventSystemSleepList.get()); |
| 3420 | array = OSArray::withCapacity(capacity: 5); |
| 3421 | |
| 3422 | if (iterator && array) { |
| 3423 | while ((object = iterator->getNextObject())) { |
| 3424 | IOService *service = OSDynamicCast(IOService, object); |
| 3425 | if (service) { |
| 3426 | OSSharedPtr<const OSSymbol> name = service->copyName(); |
| 3427 | if (name) { |
| 3428 | array->setObject(name.get()); |
| 3429 | } |
| 3430 | } |
| 3431 | } |
| 3432 | } |
| 3433 | *systemSleepList = array.detach(); |
| 3434 | } |
| 3435 | } |
| 3436 | |
| 3437 | void |
| 3438 | IOPMrootDomain::copySleepPreventersListWithID(OSArray **idleSleepList, OSArray **systemSleepList) |
| 3439 | { |
| 3440 | OSSharedPtr<OSCollectionIterator> iterator; |
| 3441 | OSObject *object = NULL; |
| 3442 | OSSharedPtr<OSArray> array; |
| 3443 | |
| 3444 | if (!gIOPMWorkLoop->inGate()) { |
| 3445 | gIOPMWorkLoop->runAction( |
| 3446 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 3447 | &IOPMrootDomain::IOPMrootDomain::copySleepPreventersListWithID), |
| 3448 | target: this, arg0: (void *)idleSleepList, arg1: (void *)systemSleepList); |
| 3449 | return; |
| 3450 | } |
| 3451 | |
| 3452 | if (idleSleepList && preventIdleSleepList && (preventIdleSleepList->getCount() != 0)) { |
| 3453 | iterator = OSCollectionIterator::withCollection(inColl: preventIdleSleepList.get()); |
| 3454 | array = OSArray::withCapacity(capacity: 5); |
| 3455 | |
| 3456 | if (iterator && array) { |
| 3457 | while ((object = iterator->getNextObject())) { |
| 3458 | IOService *service = OSDynamicCast(IOService, object); |
| 3459 | if (service) { |
| 3460 | OSSharedPtr<OSDictionary> dict = OSDictionary::withCapacity(capacity: 2); |
| 3461 | OSSharedPtr<const OSSymbol> name = service->copyName(); |
| 3462 | OSSharedPtr<OSNumber> id = OSNumber::withNumber(value: service->getRegistryEntryID(), numberOfBits: 64); |
| 3463 | if (dict && name && id) { |
| 3464 | dict->setObject(kIOPMDriverAssertionRegistryEntryIDKey, anObject: id.get()); |
| 3465 | dict->setObject(kIOPMDriverAssertionOwnerStringKey, anObject: name.get()); |
| 3466 | array->setObject(dict.get()); |
| 3467 | } |
| 3468 | } |
| 3469 | } |
| 3470 | } |
| 3471 | *idleSleepList = array.detach(); |
| 3472 | } |
| 3473 | |
| 3474 | if (systemSleepList && preventSystemSleepList && (preventSystemSleepList->getCount() != 0)) { |
| 3475 | iterator = OSCollectionIterator::withCollection(inColl: preventSystemSleepList.get()); |
| 3476 | array = OSArray::withCapacity(capacity: 5); |
| 3477 | |
| 3478 | if (iterator && array) { |
| 3479 | while ((object = iterator->getNextObject())) { |
| 3480 | IOService *service = OSDynamicCast(IOService, object); |
| 3481 | if (service) { |
| 3482 | OSSharedPtr<OSDictionary> dict = OSDictionary::withCapacity(capacity: 2); |
| 3483 | OSSharedPtr<const OSSymbol> name = service->copyName(); |
| 3484 | OSSharedPtr<OSNumber> id = OSNumber::withNumber(value: service->getRegistryEntryID(), numberOfBits: 64); |
| 3485 | if (dict && name && id) { |
| 3486 | dict->setObject(kIOPMDriverAssertionRegistryEntryIDKey, anObject: id.get()); |
| 3487 | dict->setObject(kIOPMDriverAssertionOwnerStringKey, anObject: name.get()); |
| 3488 | array->setObject(dict.get()); |
| 3489 | } |
| 3490 | } |
| 3491 | } |
| 3492 | } |
| 3493 | *systemSleepList = array.detach(); |
| 3494 | } |
| 3495 | } |
| 3496 | |
| 3497 | //****************************************************************************** |
| 3498 | // tellChangeDown |
| 3499 | // |
| 3500 | // Override the superclass implementation to send a different message type. |
| 3501 | //****************************************************************************** |
| 3502 | |
| 3503 | bool |
| 3504 | IOPMrootDomain::tellChangeDown( unsigned long stateNum ) |
| 3505 | { |
| 3506 | DLOG("tellChangeDown %s->%s\n" , |
| 3507 | getPowerStateString((uint32_t) getPowerState()), getPowerStateString((uint32_t) stateNum)); |
| 3508 | |
| 3509 | if (SLEEP_STATE == stateNum) { |
| 3510 | // Legacy apps were already told in the full->dark transition |
| 3511 | if (!ignoreTellChangeDown) { |
| 3512 | tracePoint( point: kIOPMTracePointSleepApplications ); |
| 3513 | } else { |
| 3514 | tracePoint( point: kIOPMTracePointSleepPriorityClients ); |
| 3515 | } |
| 3516 | } |
| 3517 | |
| 3518 | if (!ignoreTellChangeDown) { |
| 3519 | userActivityAtSleep = userActivityCount; |
| 3520 | DLOG("tellChangeDown::userActivityAtSleep %d\n" , userActivityAtSleep); |
| 3521 | |
| 3522 | if (SLEEP_STATE == stateNum) { |
| 3523 | hibernateAborted = false; |
| 3524 | |
| 3525 | // Direct callout into OSKext so it can disable kext unloads |
| 3526 | // during sleep/wake to prevent deadlocks. |
| 3527 | OSKextSystemSleepOrWake( kIOMessageSystemWillSleep ); |
| 3528 | |
| 3529 | IOService::updateConsoleUsers(NULL, kIOMessageSystemWillSleep); |
| 3530 | |
| 3531 | // Two change downs are sent by IOServicePM. Ignore the 2nd. |
| 3532 | // But tellClientsWithResponse() must be called for both. |
| 3533 | ignoreTellChangeDown = true; |
| 3534 | } |
| 3535 | } |
| 3536 | |
| 3537 | return super::tellClientsWithResponse( kIOMessageSystemWillSleep ); |
| 3538 | } |
| 3539 | |
| 3540 | //****************************************************************************** |
| 3541 | // askChangeDown |
| 3542 | // |
| 3543 | // Override the superclass implementation to send a different message type. |
| 3544 | // This must be idle sleep since we don't ask during any other power change. |
| 3545 | //****************************************************************************** |
| 3546 | |
| 3547 | bool |
| 3548 | IOPMrootDomain::askChangeDown( unsigned long stateNum ) |
| 3549 | { |
| 3550 | DLOG("askChangeDown %s->%s\n" , |
| 3551 | getPowerStateString((uint32_t) getPowerState()), getPowerStateString((uint32_t) stateNum)); |
| 3552 | |
| 3553 | // Don't log for dark wake entry |
| 3554 | if (kSystemTransitionSleep == _systemTransitionType) { |
| 3555 | tracePoint( point: kIOPMTracePointSleepApplications ); |
| 3556 | } |
| 3557 | |
| 3558 | return super::tellClientsWithResponse( kIOMessageCanSystemSleep ); |
| 3559 | } |
| 3560 | |
| 3561 | //****************************************************************************** |
| 3562 | // askChangeDownDone |
| 3563 | // |
| 3564 | // An opportunity for root domain to cancel the power transition, |
| 3565 | // possibily due to an assertion created by powerd in response to |
| 3566 | // kIOMessageCanSystemSleep. |
| 3567 | // |
| 3568 | // Idle sleep: |
| 3569 | // full -> dark wake transition |
| 3570 | // 1. Notify apps and powerd with kIOMessageCanSystemSleep |
| 3571 | // 2. askChangeDownDone() |
| 3572 | // dark -> sleep transition |
| 3573 | // 1. Notify powerd with kIOMessageCanSystemSleep |
| 3574 | // 2. askChangeDownDone() |
| 3575 | // |
| 3576 | // Demand sleep: |
| 3577 | // full -> dark wake transition |
| 3578 | // 1. Notify powerd with kIOMessageCanSystemSleep |
| 3579 | // 2. askChangeDownDone() |
| 3580 | // dark -> sleep transition |
| 3581 | // 1. Notify powerd with kIOMessageCanSystemSleep |
| 3582 | // 2. askChangeDownDone() |
| 3583 | //****************************************************************************** |
| 3584 | |
| 3585 | void |
| 3586 | IOPMrootDomain::askChangeDownDone( |
| 3587 | IOPMPowerChangeFlags * inOutChangeFlags, bool * cancel ) |
| 3588 | { |
| 3589 | DLOG("askChangeDownDone(0x%x, %u) type %x, cap %x->%x\n" , |
| 3590 | *inOutChangeFlags, *cancel, |
| 3591 | _systemTransitionType, |
| 3592 | _currentCapability, _pendingCapability); |
| 3593 | |
| 3594 | if ((false == *cancel) && (kSystemTransitionSleep == _systemTransitionType)) { |
| 3595 | // Dark->Sleep transition. |
| 3596 | // Check if there are any deny sleep assertions. |
| 3597 | // lastSleepReason already set by handleOurPowerChangeStart() |
| 3598 | |
| 3599 | if (!checkSystemCanSleep(sleepReason: lastSleepReason)) { |
| 3600 | // Cancel dark wake to sleep transition. |
| 3601 | // Must re-scan assertions upon entering dark wake. |
| 3602 | |
| 3603 | *cancel = true; |
| 3604 | DLOG("cancel dark->sleep\n" ); |
| 3605 | } |
| 3606 | if (_aotMode && (kPMCalendarTypeInvalid != _aotWakeTimeCalendar.selector)) { |
| 3607 | uint64_t now = mach_continuous_time(); |
| 3608 | if (((now + _aotWakePreWindow) >= _aotWakeTimeContinuous) |
| 3609 | && (now < (_aotWakeTimeContinuous + _aotWakePostWindow))) { |
| 3610 | *cancel = true; |
| 3611 | IOLog(format: "AOT wake window cancel: %qd, %qd\n" , now, _aotWakeTimeContinuous); |
| 3612 | } |
| 3613 | } |
| 3614 | } |
| 3615 | } |
| 3616 | |
| 3617 | //****************************************************************************** |
| 3618 | // systemDidNotSleep |
| 3619 | // |
| 3620 | // Work common to both canceled or aborted sleep. |
| 3621 | //****************************************************************************** |
| 3622 | |
| 3623 | void |
| 3624 | IOPMrootDomain::systemDidNotSleep( void ) |
| 3625 | { |
| 3626 | // reset console lock state |
| 3627 | thread_call_enter(call: updateConsoleUsersEntry); |
| 3628 | |
| 3629 | if (idleSleepEnabled) { |
| 3630 | if (!wrangler) { |
| 3631 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 3632 | startIdleSleepTimer(kIdleSleepRetryInterval); |
| 3633 | #else |
| 3634 | startIdleSleepTimer(idleMilliSeconds); |
| 3635 | #endif |
| 3636 | } else if (!userIsActive) { |
| 3637 | // Manually start the idle sleep timer besides waiting for |
| 3638 | // the user to become inactive. |
| 3639 | startIdleSleepTimer(kIdleSleepRetryInterval); |
| 3640 | } |
| 3641 | } |
| 3642 | |
| 3643 | preventTransitionToUserActive(prevent: false); |
| 3644 | IOService::setAdvisoryTickleEnable( true ); |
| 3645 | |
| 3646 | // After idle revert and cancel, send a did-change message to powerd |
| 3647 | // to balance the previous will-change message. Kernel clients do not |
| 3648 | // need this since sleep cannot be canceled once they are notified. |
| 3649 | |
| 3650 | if (toldPowerdCapWillChange && systemCapabilityNotifier && |
| 3651 | (_pendingCapability != _currentCapability) && |
| 3652 | ((_systemMessageClientMask & kSystemMessageClientPowerd) != 0)) { |
| 3653 | // Differs from a real capability gain change where notifyRef != 0, |
| 3654 | // but it is zero here since no response is expected. |
| 3655 | |
| 3656 | IOPMSystemCapabilityChangeParameters params; |
| 3657 | |
| 3658 | bzero(s: ¶ms, n: sizeof(params)); |
| 3659 | params.fromCapabilities = _pendingCapability; |
| 3660 | params.toCapabilities = _currentCapability; |
| 3661 | params.changeFlags = kIOPMSystemCapabilityDidChange; |
| 3662 | |
| 3663 | DLOG("MESG cap %x->%x did change\n" , |
| 3664 | params.fromCapabilities, params.toCapabilities); |
| 3665 | messageClient(kIOMessageSystemCapabilityChange, client: systemCapabilityNotifier.get(), |
| 3666 | messageArgument: ¶ms, argSize: sizeof(params)); |
| 3667 | } |
| 3668 | } |
| 3669 | |
| 3670 | //****************************************************************************** |
| 3671 | // tellNoChangeDown |
| 3672 | // |
| 3673 | // Notify registered applications and kernel clients that we are not dropping |
| 3674 | // power. |
| 3675 | // |
| 3676 | // We override the superclass implementation so we can send a different message |
| 3677 | // type to the client or application being notified. |
| 3678 | // |
| 3679 | // This must be a vetoed idle sleep, since no other power change can be vetoed. |
| 3680 | //****************************************************************************** |
| 3681 | |
| 3682 | void |
| 3683 | IOPMrootDomain::tellNoChangeDown( unsigned long stateNum ) |
| 3684 | { |
| 3685 | DLOG("tellNoChangeDown %s->%s\n" , |
| 3686 | getPowerStateString((uint32_t) getPowerState()), getPowerStateString((uint32_t) stateNum)); |
| 3687 | |
| 3688 | // Sleep canceled, clear the sleep trace point. |
| 3689 | tracePoint(point: kIOPMTracePointSystemUp); |
| 3690 | |
| 3691 | systemDidNotSleep(); |
| 3692 | return tellClients( kIOMessageSystemWillNotSleep ); |
| 3693 | } |
| 3694 | |
| 3695 | //****************************************************************************** |
| 3696 | // tellChangeUp |
| 3697 | // |
| 3698 | // Notify registered applications and kernel clients that we are raising power. |
| 3699 | // |
| 3700 | // We override the superclass implementation so we can send a different message |
| 3701 | // type to the client or application being notified. |
| 3702 | //****************************************************************************** |
| 3703 | |
| 3704 | void |
| 3705 | IOPMrootDomain::tellChangeUp( unsigned long stateNum ) |
| 3706 | { |
| 3707 | DLOG("tellChangeUp %s->%s\n" , |
| 3708 | getPowerStateString((uint32_t) getPowerState()), getPowerStateString((uint32_t) stateNum)); |
| 3709 | |
| 3710 | ignoreTellChangeDown = false; |
| 3711 | |
| 3712 | if (stateNum == ON_STATE) { |
| 3713 | // Direct callout into OSKext so it can disable kext unloads |
| 3714 | // during sleep/wake to prevent deadlocks. |
| 3715 | OSKextSystemSleepOrWake( kIOMessageSystemHasPoweredOn ); |
| 3716 | |
| 3717 | // Notify platform that sleep was cancelled or resumed. |
| 3718 | getPlatform()->callPlatformFunction( |
| 3719 | functionName: sleepMessagePEFunction.get(), waitForFunction: false, |
| 3720 | param1: (void *)(uintptr_t) kIOMessageSystemHasPoweredOn, |
| 3721 | NULL, NULL, NULL); |
| 3722 | |
| 3723 | if (getPowerState() == ON_STATE) { |
| 3724 | // Sleep was cancelled by idle cancel or revert |
| 3725 | if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics)) { |
| 3726 | // rdar://problem/50363791 |
| 3727 | // If system is in dark wake and sleep is cancelled, do not |
| 3728 | // send SystemWillPowerOn/HasPoweredOn messages to kernel |
| 3729 | // priority clients. They haven't yet seen a SystemWillSleep |
| 3730 | // message before the cancellation. So make sure the kernel |
| 3731 | // client bit is cleared in _systemMessageClientMask before |
| 3732 | // invoking the tellClients() below. This bit may have been |
| 3733 | // set by handleOurPowerChangeStart() anticipating a successful |
| 3734 | // sleep and setting the filter mask ahead of time allows the |
| 3735 | // SystemWillSleep message to go through. |
| 3736 | _systemMessageClientMask &= ~kSystemMessageClientKernel; |
| 3737 | } |
| 3738 | |
| 3739 | systemDidNotSleep(); |
| 3740 | tellClients( kIOMessageSystemWillPowerOn ); |
| 3741 | } |
| 3742 | |
| 3743 | tracePoint( point: kIOPMTracePointWakeApplications ); |
| 3744 | tellClients( kIOMessageSystemHasPoweredOn ); |
| 3745 | } else if (stateNum == AOT_STATE) { |
| 3746 | if (getPowerState() == AOT_STATE) { |
| 3747 | // Sleep was cancelled by idle cancel or revert |
| 3748 | startIdleSleepTimer(inMilliSeconds: idleMilliSeconds); |
| 3749 | } |
| 3750 | } |
| 3751 | } |
| 3752 | |
| 3753 | #define CAP_WILL_CHANGE_TO_OFF(params, flag) \ |
| 3754 | (((params)->changeFlags & kIOPMSystemCapabilityWillChange) && \ |
| 3755 | ((params)->fromCapabilities & (flag)) && \ |
| 3756 | (((params)->toCapabilities & (flag)) == 0)) |
| 3757 | |
| 3758 | #define CAP_DID_CHANGE_TO_ON(params, flag) \ |
| 3759 | (((params)->changeFlags & kIOPMSystemCapabilityDidChange) && \ |
| 3760 | ((params)->toCapabilities & (flag)) && \ |
| 3761 | (((params)->fromCapabilities & (flag)) == 0)) |
| 3762 | |
| 3763 | #define CAP_DID_CHANGE_TO_OFF(params, flag) \ |
| 3764 | (((params)->changeFlags & kIOPMSystemCapabilityDidChange) && \ |
| 3765 | ((params)->fromCapabilities & (flag)) && \ |
| 3766 | (((params)->toCapabilities & (flag)) == 0)) |
| 3767 | |
| 3768 | #define CAP_WILL_CHANGE_TO_ON(params, flag) \ |
| 3769 | (((params)->changeFlags & kIOPMSystemCapabilityWillChange) && \ |
| 3770 | ((params)->toCapabilities & (flag)) && \ |
| 3771 | (((params)->fromCapabilities & (flag)) == 0)) |
| 3772 | |
| 3773 | //****************************************************************************** |
| 3774 | // sysPowerDownHandler |
| 3775 | // |
| 3776 | // Perform a vfs sync before system sleep. |
| 3777 | //****************************************************************************** |
| 3778 | |
| 3779 | IOReturn |
| 3780 | IOPMrootDomain::sysPowerDownHandler( |
| 3781 | void * target, void * refCon, |
| 3782 | UInt32 messageType, IOService * service, |
| 3783 | void * messageArgs, vm_size_t argSize ) |
| 3784 | { |
| 3785 | static UInt32 lastSystemMessageType = 0; |
| 3786 | IOReturn ret = 0; |
| 3787 | |
| 3788 | DLOG("sysPowerDownHandler message %s\n" , getIOMessageString(messageType)); |
| 3789 | |
| 3790 | // rdar://problem/50363791 |
| 3791 | // Sanity check to make sure the SystemWill/Has message types are |
| 3792 | // received in the expected order for all kernel priority clients. |
| 3793 | if (messageType == kIOMessageSystemWillSleep || |
| 3794 | messageType == kIOMessageSystemWillPowerOn || |
| 3795 | messageType == kIOMessageSystemHasPoweredOn) { |
| 3796 | switch (messageType) { |
| 3797 | case kIOMessageSystemWillPowerOn: |
| 3798 | assert(lastSystemMessageType == kIOMessageSystemWillSleep); |
| 3799 | break; |
| 3800 | case kIOMessageSystemHasPoweredOn: |
| 3801 | assert(lastSystemMessageType == kIOMessageSystemWillPowerOn); |
| 3802 | break; |
| 3803 | } |
| 3804 | |
| 3805 | lastSystemMessageType = messageType; |
| 3806 | } |
| 3807 | |
| 3808 | if (!gRootDomain) { |
| 3809 | return kIOReturnUnsupported; |
| 3810 | } |
| 3811 | |
| 3812 | if (messageType == kIOMessageSystemCapabilityChange) { |
| 3813 | IOPMSystemCapabilityChangeParameters * params = |
| 3814 | (IOPMSystemCapabilityChangeParameters *) messageArgs; |
| 3815 | |
| 3816 | // Interested applications have been notified of an impending power |
| 3817 | // change and have acked (when applicable). |
| 3818 | // This is our chance to save whatever state we can before powering |
| 3819 | // down. |
| 3820 | // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c, |
| 3821 | // via callout |
| 3822 | |
| 3823 | DLOG("sysPowerDownHandler cap %x -> %x (flags %x)\n" , |
| 3824 | params->fromCapabilities, params->toCapabilities, |
| 3825 | params->changeFlags); |
| 3826 | |
| 3827 | if (CAP_WILL_CHANGE_TO_OFF(params, kIOPMSystemCapabilityCPU)) { |
| 3828 | // We will ack within 20 seconds |
| 3829 | params->maxWaitForReply = 20 * 1000 * 1000; |
| 3830 | |
| 3831 | #if HIBERNATION |
| 3832 | gRootDomain->evaluateSystemSleepPolicyEarly(); |
| 3833 | |
| 3834 | // add in time we could spend freeing pages |
| 3835 | if (gRootDomain->hibernateMode && !gRootDomain->hibernateDisabled) { |
| 3836 | params->maxWaitForReply = kCapabilityClientMaxWait; |
| 3837 | } |
| 3838 | DLOG("sysPowerDownHandler max wait %d s\n" , |
| 3839 | (int) (params->maxWaitForReply / 1000 / 1000)); |
| 3840 | #endif |
| 3841 | |
| 3842 | // Notify platform that sleep has begun, after the early |
| 3843 | // sleep policy evaluation. |
| 3844 | getPlatform()->callPlatformFunction( |
| 3845 | functionName: sleepMessagePEFunction.get(), waitForFunction: false, |
| 3846 | param1: (void *)(uintptr_t) kIOMessageSystemWillSleep, |
| 3847 | NULL, NULL, NULL); |
| 3848 | |
| 3849 | if (!OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending )) { |
| 3850 | // Purposely delay the ack and hope that shutdown occurs quickly. |
| 3851 | // Another option is not to schedule the thread and wait for |
| 3852 | // ack timeout... |
| 3853 | AbsoluteTime deadline; |
| 3854 | clock_interval_to_deadline( interval: 30, scale_factor: kSecondScale, result: &deadline ); |
| 3855 | thread_call_enter1_delayed( |
| 3856 | call: gRootDomain->diskSyncCalloutEntry, |
| 3857 | param1: (thread_call_param_t)(uintptr_t) params->notifyRef, |
| 3858 | deadline ); |
| 3859 | } else { |
| 3860 | thread_call_enter1( |
| 3861 | call: gRootDomain->diskSyncCalloutEntry, |
| 3862 | param1: (thread_call_param_t)(uintptr_t) params->notifyRef); |
| 3863 | } |
| 3864 | } |
| 3865 | #if HIBERNATION |
| 3866 | else if (CAP_DID_CHANGE_TO_ON(params, kIOPMSystemCapabilityCPU)) { |
| 3867 | // We will ack within 110 seconds |
| 3868 | params->maxWaitForReply = 110 * 1000 * 1000; |
| 3869 | |
| 3870 | thread_call_enter1( |
| 3871 | gRootDomain->diskSyncCalloutEntry, |
| 3872 | (thread_call_param_t)(uintptr_t) params->notifyRef); |
| 3873 | } |
| 3874 | #endif |
| 3875 | ret = kIOReturnSuccess; |
| 3876 | } |
| 3877 | |
| 3878 | return ret; |
| 3879 | } |
| 3880 | |
| 3881 | //****************************************************************************** |
| 3882 | // handleQueueSleepWakeUUID |
| 3883 | // |
| 3884 | // Called from IOPMrootDomain when we're initiating a sleep, |
| 3885 | // or indirectly from PM configd when PM decides to clear the UUID. |
| 3886 | // PM clears the UUID several minutes after successful wake from sleep, |
| 3887 | // so that we might associate App spindumps with the immediately previous |
| 3888 | // sleep/wake. |
| 3889 | // |
| 3890 | // @param obj has a retain on it. We're responsible for releasing that retain. |
| 3891 | //****************************************************************************** |
| 3892 | |
| 3893 | void |
| 3894 | IOPMrootDomain::handleQueueSleepWakeUUID(OSObject *obj) |
| 3895 | { |
| 3896 | OSSharedPtr<OSString> str; |
| 3897 | |
| 3898 | if (kOSBooleanFalse == obj) { |
| 3899 | handlePublishSleepWakeUUID(shouldPublish: false); |
| 3900 | } else { |
| 3901 | str.reset(OSDynamicCast(OSString, obj), OSNoRetain); |
| 3902 | if (str) { |
| 3903 | // This branch caches the UUID for an upcoming sleep/wake |
| 3904 | queuedSleepWakeUUIDString = str; |
| 3905 | DLOG("SleepWake UUID queued: %s\n" , queuedSleepWakeUUIDString->getCStringNoCopy()); |
| 3906 | } |
| 3907 | } |
| 3908 | } |
| 3909 | //****************************************************************************** |
| 3910 | // handlePublishSleepWakeUUID |
| 3911 | // |
| 3912 | // Called from IOPMrootDomain when we're initiating a sleep, |
| 3913 | // or indirectly from PM configd when PM decides to clear the UUID. |
| 3914 | // PM clears the UUID several minutes after successful wake from sleep, |
| 3915 | // so that we might associate App spindumps with the immediately previous |
| 3916 | // sleep/wake. |
| 3917 | //****************************************************************************** |
| 3918 | |
| 3919 | void |
| 3920 | IOPMrootDomain::handlePublishSleepWakeUUID( bool shouldPublish ) |
| 3921 | { |
| 3922 | ASSERT_GATED(); |
| 3923 | |
| 3924 | /* |
| 3925 | * Clear the current UUID |
| 3926 | */ |
| 3927 | if (gSleepWakeUUIDIsSet) { |
| 3928 | DLOG("SleepWake UUID cleared\n" ); |
| 3929 | |
| 3930 | gSleepWakeUUIDIsSet = false; |
| 3931 | |
| 3932 | removeProperty(kIOPMSleepWakeUUIDKey); |
| 3933 | messageClients(kIOPMMessageSleepWakeUUIDChange, kIOPMMessageSleepWakeUUIDCleared); |
| 3934 | } |
| 3935 | |
| 3936 | /* |
| 3937 | * Optionally, publish a new UUID |
| 3938 | */ |
| 3939 | if (queuedSleepWakeUUIDString && shouldPublish) { |
| 3940 | OSSharedPtr<OSString> publishThisUUID; |
| 3941 | |
| 3942 | publishThisUUID = queuedSleepWakeUUIDString; |
| 3943 | |
| 3944 | if (publishThisUUID) { |
| 3945 | setProperty(kIOPMSleepWakeUUIDKey, anObject: publishThisUUID.get()); |
| 3946 | } |
| 3947 | |
| 3948 | gSleepWakeUUIDIsSet = true; |
| 3949 | messageClients(kIOPMMessageSleepWakeUUIDChange, kIOPMMessageSleepWakeUUIDSet); |
| 3950 | |
| 3951 | queuedSleepWakeUUIDString.reset(); |
| 3952 | } |
| 3953 | } |
| 3954 | |
| 3955 | //****************************************************************************** |
| 3956 | // IOPMGetSleepWakeUUIDKey |
| 3957 | // |
| 3958 | // Return the truth value of gSleepWakeUUIDIsSet and optionally copy the key. |
| 3959 | // To get the full key -- a C string -- the buffer must large enough for |
| 3960 | // the end-of-string character. |
| 3961 | // The key is expected to be an UUID string |
| 3962 | //****************************************************************************** |
| 3963 | |
| 3964 | extern "C" bool |
| 3965 | IOPMCopySleepWakeUUIDKey(char *buffer, size_t buf_len) |
| 3966 | { |
| 3967 | if (!gSleepWakeUUIDIsSet) { |
| 3968 | return false; |
| 3969 | } |
| 3970 | |
| 3971 | if (buffer != NULL) { |
| 3972 | OSSharedPtr<OSString> string = |
| 3973 | OSDynamicPtrCast<OSString>(source: gRootDomain->copyProperty(kIOPMSleepWakeUUIDKey)); |
| 3974 | |
| 3975 | if (!string) { |
| 3976 | *buffer = '\0'; |
| 3977 | } else { |
| 3978 | strlcpy(dst: buffer, src: string->getCStringNoCopy(), n: buf_len); |
| 3979 | } |
| 3980 | } |
| 3981 | |
| 3982 | return true; |
| 3983 | } |
| 3984 | |
| 3985 | //****************************************************************************** |
| 3986 | // lowLatencyAudioNotify |
| 3987 | // |
| 3988 | // Used to send an update about low latency audio activity to interested |
| 3989 | // clients. To keep the overhead minimal the OSDictionary used here |
| 3990 | // is initialized at boot. |
| 3991 | //****************************************************************************** |
| 3992 | |
| 3993 | void |
| 3994 | IOPMrootDomain::lowLatencyAudioNotify(uint64_t time, boolean_t state) |
| 3995 | { |
| 3996 | if (lowLatencyAudioNotifierDict && lowLatencyAudioNotifyStateSym && lowLatencyAudioNotifyTimestampSym && |
| 3997 | lowLatencyAudioNotifyStateVal && lowLatencyAudioNotifyTimestampVal) { |
| 3998 | lowLatencyAudioNotifyTimestampVal->setValue(time); |
| 3999 | lowLatencyAudioNotifyStateVal->setValue(state); |
| 4000 | setPMSetting(gIOPMSettingLowLatencyAudioModeKey.get(), lowLatencyAudioNotifierDict.get()); |
| 4001 | } else { |
| 4002 | DLOG("LowLatencyAudioNotify error\n" ); |
| 4003 | } |
| 4004 | return; |
| 4005 | } |
| 4006 | |
| 4007 | //****************************************************************************** |
| 4008 | // IOPMrootDomainRTNotifier |
| 4009 | // |
| 4010 | // Used by performance controller to update the timestamp and state associated |
| 4011 | // with low latency audio activity in the system. |
| 4012 | //****************************************************************************** |
| 4013 | |
| 4014 | extern "C" void |
| 4015 | IOPMrootDomainRTNotifier(uint64_t time, boolean_t state) |
| 4016 | { |
| 4017 | gRootDomain->lowLatencyAudioNotify(time, state); |
| 4018 | return; |
| 4019 | } |
| 4020 | |
| 4021 | //****************************************************************************** |
| 4022 | // initializeBootSessionUUID |
| 4023 | // |
| 4024 | // Initialize the boot session uuid at boot up and sets it into registry. |
| 4025 | //****************************************************************************** |
| 4026 | |
| 4027 | void |
| 4028 | IOPMrootDomain::initializeBootSessionUUID(void) |
| 4029 | { |
| 4030 | uuid_t new_uuid; |
| 4031 | uuid_string_t new_uuid_string; |
| 4032 | |
| 4033 | uuid_generate(out: new_uuid); |
| 4034 | uuid_unparse_upper(uu: new_uuid, out: new_uuid_string); |
| 4035 | memcpy(dst: bootsessionuuid_string, src: new_uuid_string, n: sizeof(uuid_string_t)); |
| 4036 | |
| 4037 | setProperty(kIOPMBootSessionUUIDKey, aString: new_uuid_string); |
| 4038 | } |
| 4039 | |
| 4040 | //****************************************************************************** |
| 4041 | // Root domain uses the private and tagged changePowerState methods for |
| 4042 | // tracking and logging purposes. |
| 4043 | //****************************************************************************** |
| 4044 | |
| 4045 | #define REQUEST_TAG_TO_REASON(x) ((uint16_t)x) |
| 4046 | |
| 4047 | static uint32_t |
| 4048 | nextRequestTag( IOPMRequestTag tag ) |
| 4049 | { |
| 4050 | static SInt16 msb16 = 1; |
| 4051 | uint16_t id = OSAddAtomic16(amount: 1, address: &msb16); |
| 4052 | return ((uint32_t)id << 16) | REQUEST_TAG_TO_REASON(tag); |
| 4053 | } |
| 4054 | |
| 4055 | // TODO: remove this shim function and exported symbol |
| 4056 | IOReturn |
| 4057 | IOPMrootDomain::changePowerStateTo( unsigned long ordinal ) |
| 4058 | { |
| 4059 | return changePowerStateWithTagTo(ordinal, tag: kCPSReasonNone); |
| 4060 | } |
| 4061 | |
| 4062 | // TODO: remove this shim function and exported symbol |
| 4063 | IOReturn |
| 4064 | IOPMrootDomain::changePowerStateToPriv( unsigned long ordinal ) |
| 4065 | { |
| 4066 | return changePowerStateWithTagToPriv(ordinal, tag: kCPSReasonNone); |
| 4067 | } |
| 4068 | |
| 4069 | IOReturn |
| 4070 | IOPMrootDomain::changePowerStateWithOverrideTo( |
| 4071 | IOPMPowerStateIndex ordinal, IOPMRequestTag reason ) |
| 4072 | { |
| 4073 | uint32_t tag = nextRequestTag(tag: reason); |
| 4074 | DLOG("%s(%s, %x)\n" , __FUNCTION__, getPowerStateString((uint32_t) ordinal), tag); |
| 4075 | |
| 4076 | if ((ordinal != ON_STATE) && (ordinal != AOT_STATE) && (ordinal != SLEEP_STATE)) { |
| 4077 | return kIOReturnUnsupported; |
| 4078 | } |
| 4079 | |
| 4080 | return super::changePowerStateWithOverrideTo(ordinal, tag); |
| 4081 | } |
| 4082 | |
| 4083 | IOReturn |
| 4084 | IOPMrootDomain::changePowerStateWithTagTo( |
| 4085 | IOPMPowerStateIndex ordinal, IOPMRequestTag reason ) |
| 4086 | { |
| 4087 | uint32_t tag = nextRequestTag(tag: reason); |
| 4088 | DLOG("%s(%s, %x)\n" , __FUNCTION__, getPowerStateString((uint32_t) ordinal), tag); |
| 4089 | |
| 4090 | if ((ordinal != ON_STATE) && (ordinal != AOT_STATE) && (ordinal != SLEEP_STATE)) { |
| 4091 | return kIOReturnUnsupported; |
| 4092 | } |
| 4093 | |
| 4094 | return super::changePowerStateWithTagTo(ordinal, tag); |
| 4095 | } |
| 4096 | |
| 4097 | IOReturn |
| 4098 | IOPMrootDomain::changePowerStateWithTagToPriv( |
| 4099 | IOPMPowerStateIndex ordinal, IOPMRequestTag reason ) |
| 4100 | { |
| 4101 | uint32_t tag = nextRequestTag(tag: reason); |
| 4102 | DLOG("%s(%s, %x)\n" , __FUNCTION__, getPowerStateString((uint32_t) ordinal), tag); |
| 4103 | |
| 4104 | if ((ordinal != ON_STATE) && (ordinal != AOT_STATE) && (ordinal != SLEEP_STATE)) { |
| 4105 | return kIOReturnUnsupported; |
| 4106 | } |
| 4107 | |
| 4108 | return super::changePowerStateWithTagToPriv(ordinal, tag); |
| 4109 | } |
| 4110 | |
| 4111 | //****************************************************************************** |
| 4112 | // activity detect |
| 4113 | // |
| 4114 | //****************************************************************************** |
| 4115 | |
| 4116 | bool |
| 4117 | IOPMrootDomain::activitySinceSleep(void) |
| 4118 | { |
| 4119 | return userActivityCount != userActivityAtSleep; |
| 4120 | } |
| 4121 | |
| 4122 | bool |
| 4123 | IOPMrootDomain::abortHibernation(void) |
| 4124 | { |
| 4125 | #if __arm64__ |
| 4126 | // don't allow hibernation to be aborted on ARM due to user activity |
| 4127 | // since once ApplePMGR decides we're hibernating, we can't turn back |
| 4128 | // see: <rdar://problem/63848862> Tonga ApplePMGR diff quiesce path support |
| 4129 | return false; |
| 4130 | #else |
| 4131 | bool ret = activitySinceSleep(); |
| 4132 | |
| 4133 | if (ret && !hibernateAborted && checkSystemCanSustainFullWake()) { |
| 4134 | DLOG("activitySinceSleep ABORT [%d, %d]\n" , userActivityCount, userActivityAtSleep); |
| 4135 | hibernateAborted = true; |
| 4136 | } |
| 4137 | return ret; |
| 4138 | #endif |
| 4139 | } |
| 4140 | |
| 4141 | extern "C" int |
| 4142 | hibernate_should_abort(void) |
| 4143 | { |
| 4144 | if (gRootDomain) { |
| 4145 | return gRootDomain->abortHibernation(); |
| 4146 | } else { |
| 4147 | return 0; |
| 4148 | } |
| 4149 | } |
| 4150 | |
| 4151 | //****************************************************************************** |
| 4152 | // willNotifyPowerChildren |
| 4153 | // |
| 4154 | // Called after all interested drivers have all acknowledged the power change, |
| 4155 | // but before any power children is informed. Dispatched though a thread call, |
| 4156 | // so it is safe to perform work that might block on a sleeping disk. PM state |
| 4157 | // machine (not thread) will block w/o timeout until this function returns. |
| 4158 | //****************************************************************************** |
| 4159 | |
| 4160 | void |
| 4161 | IOPMrootDomain::willNotifyPowerChildren( IOPMPowerStateIndex newPowerState ) |
| 4162 | { |
| 4163 | OSSharedPtr<OSDictionary> dict; |
| 4164 | OSSharedPtr<OSNumber> secs; |
| 4165 | |
| 4166 | if (SLEEP_STATE == newPowerState) { |
| 4167 | notifierThread = current_thread(); |
| 4168 | if (updateTasksSuspend(newTasksSuspended: kTasksSuspendSuspended, newAOTTasksSuspended: kTasksSuspendNoChange)) { |
| 4169 | AbsoluteTime deadline; |
| 4170 | |
| 4171 | clock_interval_to_deadline(interval: 10, scale_factor: kSecondScale, result: &deadline); |
| 4172 | #if defined(XNU_TARGET_OS_OSX) |
| 4173 | vm_pageout_wait(AbsoluteTime_to_scalar(&deadline)); |
| 4174 | #endif /* defined(XNU_TARGET_OS_OSX) */ |
| 4175 | } |
| 4176 | |
| 4177 | _aotReadyToFullWake = false; |
| 4178 | #if 0 |
| 4179 | if (_aotLingerTime) { |
| 4180 | uint64_t deadline; |
| 4181 | IOLog("aot linger no return\n" ); |
| 4182 | clock_absolutetime_interval_to_deadline(_aotLingerTime, &deadline); |
| 4183 | clock_delay_until(deadline); |
| 4184 | } |
| 4185 | #endif |
| 4186 | if (!_aotMode) { |
| 4187 | _aotTestTime = 0; |
| 4188 | _aotWakeTimeCalendar.selector = kPMCalendarTypeInvalid; |
| 4189 | _aotLastWakeTime = 0; |
| 4190 | if (_aotMetrics) { |
| 4191 | bzero(s: _aotMetrics, n: sizeof(IOPMAOTMetrics)); |
| 4192 | } |
| 4193 | } else if (!_aotNow && !_debugWakeSeconds) { |
| 4194 | _aotNow = true; |
| 4195 | _aotPendingFlags = 0; |
| 4196 | _aotTasksSuspended = true; |
| 4197 | _aotLastWakeTime = 0; |
| 4198 | bzero(s: _aotMetrics, n: sizeof(IOPMAOTMetrics)); |
| 4199 | if (kIOPMAOTModeCycle & _aotMode) { |
| 4200 | clock_interval_to_absolutetime_interval(interval: 60, scale_factor: kSecondScale, result: &_aotTestInterval); |
| 4201 | _aotTestTime = mach_continuous_time() + _aotTestInterval; |
| 4202 | setWakeTime(_aotTestTime); |
| 4203 | } |
| 4204 | uint32_t lingerSecs; |
| 4205 | if (!PE_parse_boot_argn(arg_string: "aotlinger" , arg_ptr: &lingerSecs, max_arg: sizeof(lingerSecs))) { |
| 4206 | lingerSecs = 0; |
| 4207 | } |
| 4208 | clock_interval_to_absolutetime_interval(interval: lingerSecs, scale_factor: kSecondScale, result: &_aotLingerTime); |
| 4209 | clock_interval_to_absolutetime_interval(interval: 2000, scale_factor: kMillisecondScale, result: &_aotWakePreWindow); |
| 4210 | clock_interval_to_absolutetime_interval(interval: 1100, scale_factor: kMillisecondScale, result: &_aotWakePostWindow); |
| 4211 | } |
| 4212 | |
| 4213 | #if HIBERNATION |
| 4214 | IOHibernateSystemSleep(); |
| 4215 | IOHibernateIOKitSleep(); |
| 4216 | #endif |
| 4217 | #if defined(__arm64__) && HIBERNATION |
| 4218 | // On AS, hibernation cannot be aborted. Resetting RTC to 1s during hibernation upon detecting |
| 4219 | // user activity is pointless (we are likely to spend >1s hibernating). It also clears existing |
| 4220 | // alarms, which can mess with cycler tools. |
| 4221 | if (gRootDomain->activitySinceSleep() && gIOHibernateState == kIOHibernateStateInactive) { |
| 4222 | #else /* defined(__arm64__) && HIBERNATION */ |
| 4223 | // On non-AS, hibernation can be aborted if user activity is detected. So continue to reset the |
| 4224 | // RTC alarm (even during hibernation) so we can immediately wake from regular S2R if needed. |
| 4225 | if (gRootDomain->activitySinceSleep()) { |
| 4226 | #endif /* defined(__arm64__) && HIBERNATION */ |
| 4227 | dict = OSDictionary::withCapacity(capacity: 1); |
| 4228 | secs = OSNumber::withNumber(value: 1, numberOfBits: 32); |
| 4229 | |
| 4230 | if (dict && secs) { |
| 4231 | dict->setObject(aKey: gIOPMSettingDebugWakeRelativeKey.get(), anObject: secs.get()); |
| 4232 | gRootDomain->setProperties(dict.get()); |
| 4233 | MSG("Reverting sleep with relative wake\n" ); |
| 4234 | } |
| 4235 | } |
| 4236 | |
| 4237 | notifierThread = NULL; |
| 4238 | } |
| 4239 | } |
| 4240 | |
| 4241 | //****************************************************************************** |
| 4242 | // willTellSystemCapabilityDidChange |
| 4243 | // |
| 4244 | // IOServicePM calls this from OurChangeTellCapabilityDidChange() when root |
| 4245 | // domain is raising its power state, immediately after notifying interested |
| 4246 | // drivers and power children. |
| 4247 | //****************************************************************************** |
| 4248 | |
| 4249 | void |
| 4250 | IOPMrootDomain::willTellSystemCapabilityDidChange( void ) |
| 4251 | { |
| 4252 | if ((_systemTransitionType == kSystemTransitionWake) && |
| 4253 | !CAP_GAIN(kIOPMSystemCapabilityGraphics)) { |
| 4254 | // After powering up drivers, dark->full promotion on the current wake |
| 4255 | // transition is no longer possible. That is because the next machine |
| 4256 | // state will issue the system capability change messages. |
| 4257 | // The darkWakePowerClamped flag may already be set if the system has |
| 4258 | // at least one driver that was power clamped due to dark wake. |
| 4259 | // This function sets the darkWakePowerClamped flag in case there |
| 4260 | // is no power-clamped driver in the system. |
| 4261 | // |
| 4262 | // Last opportunity to exit dark wake using: |
| 4263 | // requestFullWake( kFullWakeReasonLocalUser ); |
| 4264 | |
| 4265 | if (!darkWakePowerClamped) { |
| 4266 | if (darkWakeLogClamp) { |
| 4267 | AbsoluteTime now; |
| 4268 | uint64_t nsec; |
| 4269 | |
| 4270 | clock_get_uptime(result: &now); |
| 4271 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 4272 | absolutetime_to_nanoseconds(abstime: now, result: &nsec); |
| 4273 | DLOG("dark wake promotion disabled at %u ms\n" , |
| 4274 | ((int)((nsec) / NSEC_PER_MSEC))); |
| 4275 | } |
| 4276 | darkWakePowerClamped = true; |
| 4277 | } |
| 4278 | } |
| 4279 | } |
| 4280 | |
| 4281 | //****************************************************************************** |
| 4282 | // sleepOnClamshellClosed |
| 4283 | // |
| 4284 | // contains the logic to determine if the system should sleep when the clamshell |
| 4285 | // is closed. |
| 4286 | //****************************************************************************** |
| 4287 | |
| 4288 | bool |
| 4289 | IOPMrootDomain::shouldSleepOnClamshellClosed( void ) |
| 4290 | { |
| 4291 | if (!clamshellExists) { |
| 4292 | return false; |
| 4293 | } |
| 4294 | |
| 4295 | DLOG("clamshell closed %d, disabled %d/%x, desktopMode %d, ac %d\n" , |
| 4296 | clamshellClosed, clamshellDisabled, clamshellSleepDisableMask, desktopMode, acAdaptorConnected); |
| 4297 | |
| 4298 | return !clamshellDisabled && !(desktopMode && acAdaptorConnected) && !clamshellSleepDisableMask; |
| 4299 | } |
| 4300 | |
| 4301 | bool |
| 4302 | IOPMrootDomain::shouldSleepOnRTCAlarmWake( void ) |
| 4303 | { |
| 4304 | // Called once every RTC/Alarm wake. Device should go to sleep if on clamshell |
| 4305 | // closed && battery |
| 4306 | if (!clamshellExists) { |
| 4307 | return false; |
| 4308 | } |
| 4309 | |
| 4310 | DLOG("shouldSleepOnRTCAlarmWake: clamshell closed %d, disabled %d/%x, desktopMode %d, ac %d\n" , |
| 4311 | clamshellClosed, clamshellDisabled, clamshellSleepDisableMask, desktopMode, acAdaptorConnected); |
| 4312 | |
| 4313 | return !acAdaptorConnected && !clamshellSleepDisableMask; |
| 4314 | } |
| 4315 | |
| 4316 | void |
| 4317 | IOPMrootDomain::sendClientClamshellNotification( void ) |
| 4318 | { |
| 4319 | /* Only broadcast clamshell alert if clamshell exists. */ |
| 4320 | if (!clamshellExists) { |
| 4321 | return; |
| 4322 | } |
| 4323 | |
| 4324 | setProperty(kAppleClamshellStateKey, |
| 4325 | anObject: clamshellClosed ? kOSBooleanTrue : kOSBooleanFalse); |
| 4326 | |
| 4327 | setProperty(kAppleClamshellCausesSleepKey, |
| 4328 | anObject: shouldSleepOnClamshellClosed() ? kOSBooleanTrue : kOSBooleanFalse); |
| 4329 | |
| 4330 | /* Argument to message is a bitfiel of |
| 4331 | * ( kClamshellStateBit | kClamshellSleepBit ) |
| 4332 | */ |
| 4333 | messageClients(kIOPMMessageClamshellStateChange, |
| 4334 | argument: (void *)(uintptr_t) ((clamshellClosed ? kClamshellStateBit : 0) |
| 4335 | | (shouldSleepOnClamshellClosed() ? kClamshellSleepBit : 0))); |
| 4336 | } |
| 4337 | |
| 4338 | //****************************************************************************** |
| 4339 | // getSleepSupported |
| 4340 | // |
| 4341 | // Deprecated |
| 4342 | //****************************************************************************** |
| 4343 | |
| 4344 | IOOptionBits |
| 4345 | IOPMrootDomain::getSleepSupported( void ) |
| 4346 | { |
| 4347 | return platformSleepSupport; |
| 4348 | } |
| 4349 | |
| 4350 | //****************************************************************************** |
| 4351 | // setSleepSupported |
| 4352 | // |
| 4353 | // Deprecated |
| 4354 | //****************************************************************************** |
| 4355 | |
| 4356 | void |
| 4357 | IOPMrootDomain::setSleepSupported( IOOptionBits flags ) |
| 4358 | { |
| 4359 | DLOG("setSleepSupported(%x)\n" , (uint32_t) flags); |
| 4360 | OSBitOrAtomic(flags, &platformSleepSupport); |
| 4361 | } |
| 4362 | |
| 4363 | //****************************************************************************** |
| 4364 | // setClamShellSleepDisable |
| 4365 | // |
| 4366 | //****************************************************************************** |
| 4367 | |
| 4368 | void |
| 4369 | IOPMrootDomain::setClamShellSleepDisable( bool disable, uint32_t bitmask ) |
| 4370 | { |
| 4371 | uint32_t oldMask; |
| 4372 | |
| 4373 | // User client calls this in non-gated context |
| 4374 | if (gIOPMWorkLoop->inGate() == false) { |
| 4375 | gIOPMWorkLoop->runAction( |
| 4376 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 4377 | &IOPMrootDomain::setClamShellSleepDisable), |
| 4378 | target: (OSObject *) this, |
| 4379 | arg0: (void *) disable, arg1: (void *)(uintptr_t) bitmask); |
| 4380 | return; |
| 4381 | } |
| 4382 | |
| 4383 | oldMask = clamshellSleepDisableMask; |
| 4384 | if (disable) { |
| 4385 | clamshellSleepDisableMask |= bitmask; |
| 4386 | } else { |
| 4387 | clamshellSleepDisableMask &= ~bitmask; |
| 4388 | } |
| 4389 | DLOG("setClamShellSleepDisable(%x->%x)\n" , oldMask, clamshellSleepDisableMask); |
| 4390 | |
| 4391 | if (clamshellExists && clamshellClosed && |
| 4392 | (clamshellSleepDisableMask != oldMask) && |
| 4393 | (clamshellSleepDisableMask == 0)) { |
| 4394 | handlePowerNotification(kLocalEvalClamshellCommand); |
| 4395 | } |
| 4396 | } |
| 4397 | |
| 4398 | //****************************************************************************** |
| 4399 | // wakeFromDoze |
| 4400 | // |
| 4401 | // Deprecated. |
| 4402 | //****************************************************************************** |
| 4403 | |
| 4404 | void |
| 4405 | IOPMrootDomain::wakeFromDoze( void ) |
| 4406 | { |
| 4407 | // Preserve symbol for familes (IOUSBFamily and IOGraphics) |
| 4408 | } |
| 4409 | |
| 4410 | //****************************************************************************** |
| 4411 | // recordRTCAlarm |
| 4412 | // |
| 4413 | // Record the earliest scheduled RTC alarm to determine whether a RTC wake |
| 4414 | // should be a dark wake or a full wake. Both Maintenance and SleepService |
| 4415 | // alarms are dark wake, while AutoWake (WakeByCalendarDate) and DebugWake |
| 4416 | // (WakeRelativeToSleep) should trigger a full wake. Scheduled power-on |
| 4417 | // PMSettings are ignored. |
| 4418 | // |
| 4419 | // Caller serialized using settingsCtrlLock. |
| 4420 | //****************************************************************************** |
| 4421 | |
| 4422 | void |
| 4423 | IOPMrootDomain::recordRTCAlarm( |
| 4424 | const OSSymbol *type, |
| 4425 | OSObject *object ) |
| 4426 | { |
| 4427 | uint32_t previousAlarmMask = _scheduledAlarmMask; |
| 4428 | |
| 4429 | if (type == gIOPMSettingDebugWakeRelativeKey) { |
| 4430 | OSNumber * n = OSDynamicCast(OSNumber, object); |
| 4431 | if (n) { |
| 4432 | // Debug wake has highest scheduling priority so it overrides any |
| 4433 | // pre-existing alarm. |
| 4434 | uint32_t debugSecs = n->unsigned32BitValue(); |
| 4435 | _nextScheduledAlarmType.reset(p: type, OSRetain); |
| 4436 | _nextScheduledAlarmUTC = debugSecs; |
| 4437 | |
| 4438 | _debugWakeSeconds = debugSecs; |
| 4439 | OSBitOrAtomic(kIOPMAlarmBitDebugWake, &_scheduledAlarmMask); |
| 4440 | DLOG("next alarm (%s) in %u secs\n" , |
| 4441 | type->getCStringNoCopy(), debugSecs); |
| 4442 | } |
| 4443 | } else if ((type == gIOPMSettingAutoWakeCalendarKey.get()) || |
| 4444 | (type == gIOPMSettingMaintenanceWakeCalendarKey.get()) || |
| 4445 | (type == gIOPMSettingSleepServiceWakeCalendarKey.get())) { |
| 4446 | OSData * data = OSDynamicCast(OSData, object); |
| 4447 | if (data && (data->getLength() == sizeof(IOPMCalendarStruct))) { |
| 4448 | const IOPMCalendarStruct * cs; |
| 4449 | bool replaceNextAlarm = false; |
| 4450 | clock_sec_t secs; |
| 4451 | |
| 4452 | cs = (const IOPMCalendarStruct *) data->getBytesNoCopy(); |
| 4453 | secs = IOPMConvertCalendarToSeconds(dt: cs); |
| 4454 | DLOG("%s " YMDTF "\n" , type->getCStringNoCopy(), YMDT(cs)); |
| 4455 | |
| 4456 | // Update the next scheduled alarm type |
| 4457 | if ((_nextScheduledAlarmType == NULL) || |
| 4458 | ((_nextScheduledAlarmType != gIOPMSettingDebugWakeRelativeKey) && |
| 4459 | (secs < _nextScheduledAlarmUTC))) { |
| 4460 | replaceNextAlarm = true; |
| 4461 | } |
| 4462 | |
| 4463 | if (type == gIOPMSettingAutoWakeCalendarKey.get()) { |
| 4464 | if (cs->year) { |
| 4465 | _calendarWakeAlarmUTC = IOPMConvertCalendarToSeconds(dt: cs); |
| 4466 | OSBitOrAtomic(kIOPMAlarmBitCalendarWake, &_scheduledAlarmMask); |
| 4467 | } else { |
| 4468 | // TODO: can this else-block be removed? |
| 4469 | _calendarWakeAlarmUTC = 0; |
| 4470 | OSBitAndAtomic(~kIOPMAlarmBitCalendarWake, &_scheduledAlarmMask); |
| 4471 | } |
| 4472 | } |
| 4473 | if (type == gIOPMSettingMaintenanceWakeCalendarKey.get()) { |
| 4474 | OSBitOrAtomic(kIOPMAlarmBitMaintenanceWake, &_scheduledAlarmMask); |
| 4475 | } |
| 4476 | if (type == gIOPMSettingSleepServiceWakeCalendarKey.get()) { |
| 4477 | OSBitOrAtomic(kIOPMAlarmBitSleepServiceWake, &_scheduledAlarmMask); |
| 4478 | } |
| 4479 | |
| 4480 | if (replaceNextAlarm) { |
| 4481 | _nextScheduledAlarmType.reset(p: type, OSRetain); |
| 4482 | _nextScheduledAlarmUTC = secs; |
| 4483 | DLOG("next alarm (%s) " YMDTF "\n" , type->getCStringNoCopy(), YMDT(cs)); |
| 4484 | } |
| 4485 | } |
| 4486 | } |
| 4487 | |
| 4488 | if (_scheduledAlarmMask != previousAlarmMask) { |
| 4489 | DLOG("scheduled alarm mask 0x%x\n" , (uint32_t) _scheduledAlarmMask); |
| 4490 | } |
| 4491 | } |
| 4492 | |
| 4493 | // MARK: - |
| 4494 | // MARK: Features |
| 4495 | |
| 4496 | //****************************************************************************** |
| 4497 | // publishFeature |
| 4498 | // |
| 4499 | // Adds a new feature to the supported features dictionary |
| 4500 | //****************************************************************************** |
| 4501 | |
| 4502 | void |
| 4503 | IOPMrootDomain::publishFeature( const char * feature ) |
| 4504 | { |
| 4505 | publishFeature(feature, kRD_AllPowerSources, NULL); |
| 4506 | } |
| 4507 | |
| 4508 | //****************************************************************************** |
| 4509 | // publishFeature (with supported power source specified) |
| 4510 | // |
| 4511 | // Adds a new feature to the supported features dictionary |
| 4512 | //****************************************************************************** |
| 4513 | |
| 4514 | void |
| 4515 | IOPMrootDomain::publishFeature( |
| 4516 | const char *feature, |
| 4517 | uint32_t supportedWhere, |
| 4518 | uint32_t *uniqueFeatureID) |
| 4519 | { |
| 4520 | static uint16_t next_feature_id = 500; |
| 4521 | |
| 4522 | OSSharedPtr<OSNumber> new_feature_data; |
| 4523 | OSNumber *existing_feature = NULL; |
| 4524 | OSArray *existing_feature_arr_raw = NULL; |
| 4525 | OSSharedPtr<OSArray> existing_feature_arr; |
| 4526 | OSObject *osObj = NULL; |
| 4527 | uint32_t feature_value = 0; |
| 4528 | |
| 4529 | supportedWhere &= kRD_AllPowerSources; // mask off any craziness! |
| 4530 | |
| 4531 | if (!supportedWhere) { |
| 4532 | // Feature isn't supported anywhere! |
| 4533 | return; |
| 4534 | } |
| 4535 | |
| 4536 | if (next_feature_id > 5000) { |
| 4537 | // Far, far too many features! |
| 4538 | return; |
| 4539 | } |
| 4540 | |
| 4541 | if (featuresDictLock) { |
| 4542 | IOLockLock(featuresDictLock); |
| 4543 | } |
| 4544 | |
| 4545 | OSSharedPtr<OSObject> origFeaturesProp = copyProperty(kRootDomainSupportedFeatures); |
| 4546 | OSDictionary *origFeatures = OSDynamicCast(OSDictionary, origFeaturesProp.get()); |
| 4547 | OSSharedPtr<OSDictionary> features; |
| 4548 | |
| 4549 | // Create new features dict if necessary |
| 4550 | if (origFeatures) { |
| 4551 | features = OSDictionary::withDictionary(dict: origFeatures); |
| 4552 | } else { |
| 4553 | features = OSDictionary::withCapacity(capacity: 1); |
| 4554 | } |
| 4555 | |
| 4556 | // Create OSNumber to track new feature |
| 4557 | |
| 4558 | next_feature_id += 1; |
| 4559 | if (uniqueFeatureID) { |
| 4560 | // We don't really mind if the calling kext didn't give us a place |
| 4561 | // to stash their unique id. Many kexts don't plan to unload, and thus |
| 4562 | // have no need to remove themselves later. |
| 4563 | *uniqueFeatureID = next_feature_id; |
| 4564 | } |
| 4565 | |
| 4566 | feature_value = (uint32_t)next_feature_id; |
| 4567 | feature_value <<= 16; |
| 4568 | feature_value += supportedWhere; |
| 4569 | |
| 4570 | new_feature_data = OSNumber::withNumber( |
| 4571 | value: (unsigned long long)feature_value, numberOfBits: 32); |
| 4572 | |
| 4573 | // Does features object already exist? |
| 4574 | if ((osObj = features->getObject(aKey: feature))) { |
| 4575 | if ((existing_feature = OSDynamicCast(OSNumber, osObj))) { |
| 4576 | // We need to create an OSArray to hold the now 2 elements. |
| 4577 | existing_feature_arr = OSArray::withObjects( |
| 4578 | objects: (const OSObject **)&existing_feature, count: 1, capacity: 2); |
| 4579 | } else if ((existing_feature_arr_raw = OSDynamicCast(OSArray, osObj))) { |
| 4580 | // Add object to existing array |
| 4581 | existing_feature_arr = OSArray::withArray( |
| 4582 | array: existing_feature_arr_raw, |
| 4583 | capacity: existing_feature_arr_raw->getCount() + 1); |
| 4584 | } |
| 4585 | |
| 4586 | if (existing_feature_arr) { |
| 4587 | existing_feature_arr->setObject(new_feature_data.get()); |
| 4588 | features->setObject(aKey: feature, anObject: existing_feature_arr.get()); |
| 4589 | } |
| 4590 | } else { |
| 4591 | // The easy case: no previously existing features listed. We simply |
| 4592 | // set the OSNumber at key 'feature' and we're on our way. |
| 4593 | features->setObject(aKey: feature, anObject: new_feature_data.get()); |
| 4594 | } |
| 4595 | |
| 4596 | setProperty(kRootDomainSupportedFeatures, anObject: features.get()); |
| 4597 | |
| 4598 | if (featuresDictLock) { |
| 4599 | IOLockUnlock(featuresDictLock); |
| 4600 | } |
| 4601 | |
| 4602 | // Notify EnergySaver and all those in user space so they might |
| 4603 | // re-populate their feature specific UI |
| 4604 | if (pmPowerStateQueue) { |
| 4605 | pmPowerStateQueue->submitPowerEvent( eventType: kPowerEventFeatureChanged ); |
| 4606 | } |
| 4607 | } |
| 4608 | |
| 4609 | //****************************************************************************** |
| 4610 | // removePublishedFeature |
| 4611 | // |
| 4612 | // Removes previously published feature |
| 4613 | //****************************************************************************** |
| 4614 | |
| 4615 | IOReturn |
| 4616 | IOPMrootDomain::removePublishedFeature( uint32_t removeFeatureID ) |
| 4617 | { |
| 4618 | IOReturn ret = kIOReturnError; |
| 4619 | uint32_t feature_value = 0; |
| 4620 | uint16_t feature_id = 0; |
| 4621 | bool madeAChange = false; |
| 4622 | |
| 4623 | OSSymbol *dictKey = NULL; |
| 4624 | OSSharedPtr<OSCollectionIterator> dictIterator; |
| 4625 | OSArray *arrayMember = NULL; |
| 4626 | OSNumber *numberMember = NULL; |
| 4627 | OSObject *osObj = NULL; |
| 4628 | OSNumber *osNum = NULL; |
| 4629 | OSSharedPtr<OSArray> arrayMemberCopy; |
| 4630 | |
| 4631 | if (kBadPMFeatureID == removeFeatureID) { |
| 4632 | return kIOReturnNotFound; |
| 4633 | } |
| 4634 | |
| 4635 | if (featuresDictLock) { |
| 4636 | IOLockLock(featuresDictLock); |
| 4637 | } |
| 4638 | |
| 4639 | OSSharedPtr<OSObject> origFeaturesProp = copyProperty(kRootDomainSupportedFeatures); |
| 4640 | OSDictionary *origFeatures = OSDynamicCast(OSDictionary, origFeaturesProp.get()); |
| 4641 | OSSharedPtr<OSDictionary> features; |
| 4642 | |
| 4643 | if (origFeatures) { |
| 4644 | // Any modifications to the dictionary are made to the copy to prevent |
| 4645 | // races & crashes with userland clients. Dictionary updated |
| 4646 | // automically later. |
| 4647 | features = OSDictionary::withDictionary(dict: origFeatures); |
| 4648 | } else { |
| 4649 | features = NULL; |
| 4650 | ret = kIOReturnNotFound; |
| 4651 | goto exit; |
| 4652 | } |
| 4653 | |
| 4654 | // We iterate 'features' dictionary looking for an entry tagged |
| 4655 | // with 'removeFeatureID'. If found, we remove it from our tracking |
| 4656 | // structures and notify the OS via a general interest message. |
| 4657 | |
| 4658 | dictIterator = OSCollectionIterator::withCollection(inColl: features.get()); |
| 4659 | if (!dictIterator) { |
| 4660 | goto exit; |
| 4661 | } |
| 4662 | |
| 4663 | while ((dictKey = OSDynamicCast(OSSymbol, dictIterator->getNextObject()))) { |
| 4664 | osObj = features->getObject(aKey: dictKey); |
| 4665 | |
| 4666 | // Each Feature is either tracked by an OSNumber |
| 4667 | if (osObj && (numberMember = OSDynamicCast(OSNumber, osObj))) { |
| 4668 | feature_value = numberMember->unsigned32BitValue(); |
| 4669 | feature_id = (uint16_t)(feature_value >> 16); |
| 4670 | |
| 4671 | if (feature_id == (uint16_t)removeFeatureID) { |
| 4672 | // Remove this node |
| 4673 | features->removeObject(aKey: dictKey); |
| 4674 | madeAChange = true; |
| 4675 | break; |
| 4676 | } |
| 4677 | |
| 4678 | // Or tracked by an OSArray of OSNumbers |
| 4679 | } else if (osObj && (arrayMember = OSDynamicCast(OSArray, osObj))) { |
| 4680 | unsigned int arrayCount = arrayMember->getCount(); |
| 4681 | |
| 4682 | for (unsigned int i = 0; i < arrayCount; i++) { |
| 4683 | osNum = OSDynamicCast(OSNumber, arrayMember->getObject(i)); |
| 4684 | if (!osNum) { |
| 4685 | continue; |
| 4686 | } |
| 4687 | |
| 4688 | feature_value = osNum->unsigned32BitValue(); |
| 4689 | feature_id = (uint16_t)(feature_value >> 16); |
| 4690 | |
| 4691 | if (feature_id == (uint16_t)removeFeatureID) { |
| 4692 | // Remove this node |
| 4693 | if (1 == arrayCount) { |
| 4694 | // If the array only contains one element, remove |
| 4695 | // the whole thing. |
| 4696 | features->removeObject(aKey: dictKey); |
| 4697 | } else { |
| 4698 | // Otherwise remove the element from a copy of the array. |
| 4699 | arrayMemberCopy = OSArray::withArray(array: arrayMember); |
| 4700 | if (arrayMemberCopy) { |
| 4701 | arrayMemberCopy->removeObject(index: i); |
| 4702 | features->setObject(aKey: dictKey, anObject: arrayMemberCopy.get()); |
| 4703 | } |
| 4704 | } |
| 4705 | |
| 4706 | madeAChange = true; |
| 4707 | break; |
| 4708 | } |
| 4709 | } |
| 4710 | } |
| 4711 | } |
| 4712 | |
| 4713 | if (madeAChange) { |
| 4714 | ret = kIOReturnSuccess; |
| 4715 | |
| 4716 | setProperty(kRootDomainSupportedFeatures, anObject: features.get()); |
| 4717 | |
| 4718 | // Notify EnergySaver and all those in user space so they might |
| 4719 | // re-populate their feature specific UI |
| 4720 | if (pmPowerStateQueue) { |
| 4721 | pmPowerStateQueue->submitPowerEvent( eventType: kPowerEventFeatureChanged ); |
| 4722 | } |
| 4723 | } else { |
| 4724 | ret = kIOReturnNotFound; |
| 4725 | } |
| 4726 | |
| 4727 | exit: |
| 4728 | if (featuresDictLock) { |
| 4729 | IOLockUnlock(featuresDictLock); |
| 4730 | } |
| 4731 | return ret; |
| 4732 | } |
| 4733 | |
| 4734 | //****************************************************************************** |
| 4735 | // publishPMSetting (private) |
| 4736 | // |
| 4737 | // Should only be called by PMSettingObject to publish a PM Setting as a |
| 4738 | // supported feature. |
| 4739 | //****************************************************************************** |
| 4740 | |
| 4741 | void |
| 4742 | IOPMrootDomain::publishPMSetting( |
| 4743 | const OSSymbol * feature, uint32_t where, uint32_t * featureID ) |
| 4744 | { |
| 4745 | if (noPublishPMSettings && |
| 4746 | (noPublishPMSettings->getNextIndexOfObject(anObject: feature, index: 0) != (unsigned int)-1)) { |
| 4747 | // Setting found in noPublishPMSettings array |
| 4748 | *featureID = kBadPMFeatureID; |
| 4749 | return; |
| 4750 | } |
| 4751 | |
| 4752 | publishFeature( |
| 4753 | feature: feature->getCStringNoCopy(), supportedWhere: where, uniqueFeatureID: featureID); |
| 4754 | } |
| 4755 | |
| 4756 | //****************************************************************************** |
| 4757 | // setPMSetting (private) |
| 4758 | // |
| 4759 | // Internal helper to relay PM settings changes from user space to individual |
| 4760 | // drivers. Should be called only by IOPMrootDomain::setProperties. |
| 4761 | //****************************************************************************** |
| 4762 | |
| 4763 | IOReturn |
| 4764 | IOPMrootDomain::setPMSetting( |
| 4765 | const OSSymbol *type, |
| 4766 | OSObject *object ) |
| 4767 | { |
| 4768 | PMSettingCallEntry *entries = NULL; |
| 4769 | OSSharedPtr<OSArray> chosen; |
| 4770 | const OSArray *array; |
| 4771 | PMSettingObject *pmso; |
| 4772 | thread_t thisThread; |
| 4773 | int i, j, count, capacity; |
| 4774 | bool ok = false; |
| 4775 | IOReturn ret; |
| 4776 | |
| 4777 | if (NULL == type) { |
| 4778 | return kIOReturnBadArgument; |
| 4779 | } |
| 4780 | |
| 4781 | PMSETTING_LOCK(); |
| 4782 | |
| 4783 | // Update settings dict so changes are visible from copyPMSetting(). |
| 4784 | fPMSettingsDict->setObject(aKey: type, anObject: object); |
| 4785 | |
| 4786 | // Prep all PMSetting objects with the given 'type' for callout. |
| 4787 | array = OSDynamicCast(OSArray, settingsCallbacks->getObject(type)); |
| 4788 | if (!array || ((capacity = array->getCount()) == 0)) { |
| 4789 | goto unlock_exit; |
| 4790 | } |
| 4791 | |
| 4792 | // Array to retain PMSetting objects targeted for callout. |
| 4793 | chosen = OSArray::withCapacity(capacity); |
| 4794 | if (!chosen) { |
| 4795 | goto unlock_exit; // error |
| 4796 | } |
| 4797 | entries = IONew(PMSettingCallEntry, capacity); |
| 4798 | if (!entries) { |
| 4799 | goto unlock_exit; // error |
| 4800 | } |
| 4801 | memset(s: entries, c: 0, n: sizeof(PMSettingCallEntry) * capacity); |
| 4802 | |
| 4803 | thisThread = current_thread(); |
| 4804 | |
| 4805 | for (i = 0, j = 0; i < capacity; i++) { |
| 4806 | pmso = (PMSettingObject *) array->getObject(index: i); |
| 4807 | if (pmso->disabled) { |
| 4808 | continue; |
| 4809 | } |
| 4810 | entries[j].thread = thisThread; |
| 4811 | queue_enter(&pmso->calloutQueue, &entries[j], PMSettingCallEntry *, link); |
| 4812 | chosen->setObject(pmso); |
| 4813 | j++; |
| 4814 | } |
| 4815 | count = j; |
| 4816 | if (!count) { |
| 4817 | goto unlock_exit; |
| 4818 | } |
| 4819 | |
| 4820 | PMSETTING_UNLOCK(); |
| 4821 | |
| 4822 | // Call each pmso in the chosen array. |
| 4823 | for (i = 0; i < count; i++) { |
| 4824 | pmso = (PMSettingObject *) chosen->getObject(index: i); |
| 4825 | ret = pmso->dispatchPMSetting(type, object); |
| 4826 | if (ret == kIOReturnSuccess) { |
| 4827 | // At least one setting handler was successful |
| 4828 | ok = true; |
| 4829 | #if DEVELOPMENT || DEBUG |
| 4830 | } else { |
| 4831 | // Log the handler and kext that failed |
| 4832 | OSSharedPtr<const OSSymbol> kextName = copyKextIdentifierWithAddress((vm_address_t) pmso->func); |
| 4833 | if (kextName) { |
| 4834 | DLOG("PMSetting(%s) error 0x%x from %s\n" , |
| 4835 | type->getCStringNoCopy(), ret, kextName->getCStringNoCopy()); |
| 4836 | } |
| 4837 | #endif |
| 4838 | } |
| 4839 | } |
| 4840 | |
| 4841 | PMSETTING_LOCK(); |
| 4842 | for (i = 0; i < count; i++) { |
| 4843 | pmso = (PMSettingObject *) chosen->getObject(index: i); |
| 4844 | queue_remove(&pmso->calloutQueue, &entries[i], PMSettingCallEntry *, link); |
| 4845 | if (pmso->waitThread) { |
| 4846 | PMSETTING_WAKEUP(pmso); |
| 4847 | } |
| 4848 | } |
| 4849 | |
| 4850 | if (ok) { |
| 4851 | recordRTCAlarm(type, object); |
| 4852 | } |
| 4853 | unlock_exit: |
| 4854 | PMSETTING_UNLOCK(); |
| 4855 | |
| 4856 | if (entries) { |
| 4857 | IODelete(entries, PMSettingCallEntry, capacity); |
| 4858 | } |
| 4859 | |
| 4860 | return kIOReturnSuccess; |
| 4861 | } |
| 4862 | |
| 4863 | //****************************************************************************** |
| 4864 | // copyPMSetting (public) |
| 4865 | // |
| 4866 | // Allows kexts to safely read setting values, without being subscribed to |
| 4867 | // notifications. |
| 4868 | //****************************************************************************** |
| 4869 | |
| 4870 | OSSharedPtr<OSObject> |
| 4871 | IOPMrootDomain::copyPMSetting( |
| 4872 | OSSymbol *whichSetting) |
| 4873 | { |
| 4874 | OSSharedPtr<OSObject> obj; |
| 4875 | |
| 4876 | if (!whichSetting) { |
| 4877 | return NULL; |
| 4878 | } |
| 4879 | |
| 4880 | PMSETTING_LOCK(); |
| 4881 | obj.reset(p: fPMSettingsDict->getObject(aKey: whichSetting), OSRetain); |
| 4882 | PMSETTING_UNLOCK(); |
| 4883 | |
| 4884 | return obj; |
| 4885 | } |
| 4886 | |
| 4887 | //****************************************************************************** |
| 4888 | // registerPMSettingController (public) |
| 4889 | // |
| 4890 | // direct wrapper to registerPMSettingController with uint32_t power source arg |
| 4891 | //****************************************************************************** |
| 4892 | |
| 4893 | IOReturn |
| 4894 | IOPMrootDomain::registerPMSettingController( |
| 4895 | const OSSymbol * settings[], |
| 4896 | IOPMSettingControllerCallback func, |
| 4897 | OSObject *target, |
| 4898 | uintptr_t refcon, |
| 4899 | OSObject **handle) |
| 4900 | { |
| 4901 | return registerPMSettingController( |
| 4902 | settings, |
| 4903 | supportedPowerSources: (kIOPMSupportedOnAC | kIOPMSupportedOnBatt | kIOPMSupportedOnUPS), |
| 4904 | callout: func, target, refcon, handle); |
| 4905 | } |
| 4906 | |
| 4907 | //****************************************************************************** |
| 4908 | // registerPMSettingController (public) |
| 4909 | // |
| 4910 | // Kexts may register for notifications when a particular setting is changed. |
| 4911 | // A list of settings is available in IOPM.h. |
| 4912 | // Arguments: |
| 4913 | // * settings - An OSArray containing OSSymbols. Caller should populate this |
| 4914 | // array with a list of settings caller wants notifications from. |
| 4915 | // * func - A C function callback of the type IOPMSettingControllerCallback |
| 4916 | // * target - caller may provide an OSObject *, which PM will pass as an |
| 4917 | // target to calls to "func" |
| 4918 | // * refcon - caller may provide an void *, which PM will pass as an |
| 4919 | // argument to calls to "func" |
| 4920 | // * handle - This is a return argument. We will populate this pointer upon |
| 4921 | // call success. Hold onto this and pass this argument to |
| 4922 | // IOPMrootDomain::deRegisterPMSettingCallback when unloading your kext |
| 4923 | // Returns: |
| 4924 | // kIOReturnSuccess on success |
| 4925 | //****************************************************************************** |
| 4926 | |
| 4927 | IOReturn |
| 4928 | IOPMrootDomain::registerPMSettingController( |
| 4929 | const OSSymbol * settings[], |
| 4930 | uint32_t supportedPowerSources, |
| 4931 | IOPMSettingControllerCallback func, |
| 4932 | OSObject *target, |
| 4933 | uintptr_t refcon, |
| 4934 | OSObject **handle) |
| 4935 | { |
| 4936 | PMSettingObject *pmso = NULL; |
| 4937 | OSObject *pmsh = NULL; |
| 4938 | int i; |
| 4939 | |
| 4940 | if (NULL == settings || |
| 4941 | NULL == func || |
| 4942 | NULL == handle) { |
| 4943 | return kIOReturnBadArgument; |
| 4944 | } |
| 4945 | |
| 4946 | pmso = PMSettingObject::pmSettingObject( |
| 4947 | parent_arg: (IOPMrootDomain *) this, handler_arg: func, target_arg: target, |
| 4948 | refcon_arg: refcon, supportedPowerSources, settings, handle_obj: &pmsh); |
| 4949 | |
| 4950 | if (!pmso) { |
| 4951 | *handle = NULL; |
| 4952 | return kIOReturnInternalError; |
| 4953 | } |
| 4954 | |
| 4955 | PMSETTING_LOCK(); |
| 4956 | for (i = 0; settings[i]; i++) { |
| 4957 | OSSharedPtr<OSArray> newList; |
| 4958 | OSArray *list = OSDynamicCast(OSArray, settingsCallbacks->getObject(settings[i])); |
| 4959 | if (!list) { |
| 4960 | // New array of callbacks for this setting |
| 4961 | newList = OSArray::withCapacity(capacity: 1); |
| 4962 | settingsCallbacks->setObject(aKey: settings[i], anObject: newList.get()); |
| 4963 | list = newList.get(); |
| 4964 | } |
| 4965 | |
| 4966 | // Add caller to the callback list |
| 4967 | list->setObject(pmso); |
| 4968 | } |
| 4969 | PMSETTING_UNLOCK(); |
| 4970 | |
| 4971 | // Return handle to the caller, the setting object is private. |
| 4972 | *handle = pmsh; |
| 4973 | |
| 4974 | return kIOReturnSuccess; |
| 4975 | } |
| 4976 | |
| 4977 | //****************************************************************************** |
| 4978 | // deregisterPMSettingObject (private) |
| 4979 | // |
| 4980 | // Only called from PMSettingObject. |
| 4981 | //****************************************************************************** |
| 4982 | |
| 4983 | void |
| 4984 | IOPMrootDomain::deregisterPMSettingObject( PMSettingObject * pmso ) |
| 4985 | { |
| 4986 | thread_t thisThread = current_thread(); |
| 4987 | PMSettingCallEntry *callEntry; |
| 4988 | OSSharedPtr<OSCollectionIterator> iter; |
| 4989 | OSSymbol *sym; |
| 4990 | OSArray *array; |
| 4991 | int index; |
| 4992 | bool wait; |
| 4993 | |
| 4994 | PMSETTING_LOCK(); |
| 4995 | |
| 4996 | pmso->disabled = true; |
| 4997 | |
| 4998 | // Wait for all callout threads to finish. |
| 4999 | do { |
| 5000 | wait = false; |
| 5001 | queue_iterate(&pmso->calloutQueue, callEntry, PMSettingCallEntry *, link) |
| 5002 | { |
| 5003 | if (callEntry->thread != thisThread) { |
| 5004 | wait = true; |
| 5005 | break; |
| 5006 | } |
| 5007 | } |
| 5008 | if (wait) { |
| 5009 | assert(NULL == pmso->waitThread); |
| 5010 | pmso->waitThread = thisThread; |
| 5011 | PMSETTING_WAIT(pmso); |
| 5012 | pmso->waitThread = NULL; |
| 5013 | } |
| 5014 | } while (wait); |
| 5015 | |
| 5016 | // Search each PM settings array in the kernel. |
| 5017 | iter = OSCollectionIterator::withCollection(inColl: settingsCallbacks.get()); |
| 5018 | if (iter) { |
| 5019 | while ((sym = OSDynamicCast(OSSymbol, iter->getNextObject()))) { |
| 5020 | array = OSDynamicCast(OSArray, settingsCallbacks->getObject(sym)); |
| 5021 | index = array->getNextIndexOfObject(anObject: pmso, index: 0); |
| 5022 | if (-1 != index) { |
| 5023 | array->removeObject(index); |
| 5024 | } |
| 5025 | } |
| 5026 | } |
| 5027 | |
| 5028 | PMSETTING_UNLOCK(); |
| 5029 | |
| 5030 | pmso->release(); |
| 5031 | } |
| 5032 | |
| 5033 | //****************************************************************************** |
| 5034 | // informCPUStateChange |
| 5035 | // |
| 5036 | // Call into PM CPU code so that CPU power savings may dynamically adjust for |
| 5037 | // running on battery, with the lid closed, etc. |
| 5038 | // |
| 5039 | // informCPUStateChange is a no-op on non x86 systems |
| 5040 | // only x86 has explicit support in the IntelCPUPowerManagement kext |
| 5041 | //****************************************************************************** |
| 5042 | |
| 5043 | void |
| 5044 | IOPMrootDomain::informCPUStateChange( |
| 5045 | uint32_t type, |
| 5046 | uint32_t value ) |
| 5047 | { |
| 5048 | #if defined(__i386__) || defined(__x86_64__) |
| 5049 | |
| 5050 | pmioctlVariableInfo_t varInfoStruct; |
| 5051 | int pmCPUret = 0; |
| 5052 | const char *varNameStr = NULL; |
| 5053 | int32_t *varIndex = NULL; |
| 5054 | |
| 5055 | if (kInformAC == type) { |
| 5056 | varNameStr = kIOPMRootDomainBatPowerCString; |
| 5057 | varIndex = &idxPMCPULimitedPower; |
| 5058 | } else if (kInformLid == type) { |
| 5059 | varNameStr = kIOPMRootDomainLidCloseCString; |
| 5060 | varIndex = &idxPMCPUClamshell; |
| 5061 | } else { |
| 5062 | return; |
| 5063 | } |
| 5064 | |
| 5065 | // Set the new value! |
| 5066 | // pmCPUControl will assign us a new ID if one doesn't exist yet |
| 5067 | bzero(&varInfoStruct, sizeof(pmioctlVariableInfo_t)); |
| 5068 | varInfoStruct.varID = *varIndex; |
| 5069 | varInfoStruct.varType = vBool; |
| 5070 | varInfoStruct.varInitValue = value; |
| 5071 | varInfoStruct.varCurValue = value; |
| 5072 | strlcpy((char *)varInfoStruct.varName, |
| 5073 | (const char *)varNameStr, |
| 5074 | sizeof(varInfoStruct.varName)); |
| 5075 | |
| 5076 | // Set! |
| 5077 | pmCPUret = pmCPUControl( PMIOCSETVARINFO, (void *)&varInfoStruct ); |
| 5078 | |
| 5079 | // pmCPU only assigns numerical id's when a new varName is specified |
| 5080 | if ((0 == pmCPUret) |
| 5081 | && (*varIndex == kCPUUnknownIndex)) { |
| 5082 | // pmCPUControl has assigned us a new variable ID. |
| 5083 | // Let's re-read the structure we just SET to learn that ID. |
| 5084 | pmCPUret = pmCPUControl( PMIOCGETVARNAMEINFO, (void *)&varInfoStruct ); |
| 5085 | |
| 5086 | if (0 == pmCPUret) { |
| 5087 | // Store it in idxPMCPUClamshell or idxPMCPULimitedPower |
| 5088 | *varIndex = varInfoStruct.varID; |
| 5089 | } |
| 5090 | } |
| 5091 | |
| 5092 | return; |
| 5093 | |
| 5094 | #endif /* __i386__ || __x86_64__ */ |
| 5095 | } |
| 5096 | |
| 5097 | // MARK: - |
| 5098 | // MARK: Deep Sleep Policy |
| 5099 | |
| 5100 | #if HIBERNATION |
| 5101 | |
| 5102 | //****************************************************************************** |
| 5103 | // evaluateSystemSleepPolicy |
| 5104 | //****************************************************************************** |
| 5105 | |
| 5106 | #define kIOPlatformSystemSleepPolicyKey "IOPlatformSystemSleepPolicy" |
| 5107 | |
| 5108 | // Sleep flags |
| 5109 | enum { |
| 5110 | kIOPMSleepFlagHibernate = 0x00000001, |
| 5111 | kIOPMSleepFlagSleepTimerEnable = 0x00000002 |
| 5112 | }; |
| 5113 | |
| 5114 | struct IOPMSystemSleepPolicyEntry { |
| 5115 | uint32_t factorMask; |
| 5116 | uint32_t factorBits; |
| 5117 | uint32_t sleepFlags; |
| 5118 | uint32_t wakeEvents; |
| 5119 | } __attribute__((packed)); |
| 5120 | |
| 5121 | struct IOPMSystemSleepPolicyTable { |
| 5122 | uint32_t signature; |
| 5123 | uint16_t version; |
| 5124 | uint16_t entryCount; |
| 5125 | IOPMSystemSleepPolicyEntry entries[]; |
| 5126 | } __attribute__((packed)); |
| 5127 | |
| 5128 | enum { |
| 5129 | kIOPMSleepAttributeHibernateSetup = 0x00000001, |
| 5130 | kIOPMSleepAttributeHibernateSleep = 0x00000002 |
| 5131 | }; |
| 5132 | |
| 5133 | static uint32_t |
| 5134 | getSleepTypeAttributes( uint32_t sleepType ) |
| 5135 | { |
| 5136 | static const uint32_t sleepTypeAttributes[kIOPMSleepTypeLast] = |
| 5137 | { |
| 5138 | /* invalid */ 0, |
| 5139 | /* abort */ 0, |
| 5140 | /* normal */ 0, |
| 5141 | /* safesleep */ kIOPMSleepAttributeHibernateSetup, |
| 5142 | /* hibernate */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep, |
| 5143 | /* standby */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep, |
| 5144 | /* poweroff */ kIOPMSleepAttributeHibernateSetup | kIOPMSleepAttributeHibernateSleep, |
| 5145 | /* deepidle */ 0 |
| 5146 | }; |
| 5147 | |
| 5148 | if (sleepType >= kIOPMSleepTypeLast) { |
| 5149 | return 0; |
| 5150 | } |
| 5151 | |
| 5152 | return sleepTypeAttributes[sleepType]; |
| 5153 | } |
| 5154 | |
| 5155 | bool |
| 5156 | IOPMrootDomain::evaluateSystemSleepPolicy( |
| 5157 | IOPMSystemSleepParameters * params, int sleepPhase, uint32_t * hibMode ) |
| 5158 | { |
| 5159 | #define SLEEP_FACTOR(x) {(uint32_t) kIOPMSleepFactor ## x, #x} |
| 5160 | |
| 5161 | static const IONamedValue factorValues[] = { |
| 5162 | SLEEP_FACTOR( SleepTimerWake ), |
| 5163 | SLEEP_FACTOR( LidOpen ), |
| 5164 | SLEEP_FACTOR( ACPower ), |
| 5165 | SLEEP_FACTOR( BatteryLow ), |
| 5166 | SLEEP_FACTOR( StandbyNoDelay ), |
| 5167 | SLEEP_FACTOR( StandbyForced ), |
| 5168 | SLEEP_FACTOR( StandbyDisabled ), |
| 5169 | SLEEP_FACTOR( USBExternalDevice ), |
| 5170 | SLEEP_FACTOR( BluetoothHIDDevice ), |
| 5171 | SLEEP_FACTOR( ExternalMediaMounted ), |
| 5172 | SLEEP_FACTOR( ThunderboltDevice ), |
| 5173 | SLEEP_FACTOR( RTCAlarmScheduled ), |
| 5174 | SLEEP_FACTOR( MagicPacketWakeEnabled ), |
| 5175 | SLEEP_FACTOR( HibernateForced ), |
| 5176 | SLEEP_FACTOR( AutoPowerOffDisabled ), |
| 5177 | SLEEP_FACTOR( AutoPowerOffForced ), |
| 5178 | SLEEP_FACTOR( ExternalDisplay ), |
| 5179 | SLEEP_FACTOR( NetworkKeepAliveActive ), |
| 5180 | SLEEP_FACTOR( LocalUserActivity ), |
| 5181 | SLEEP_FACTOR( HibernateFailed ), |
| 5182 | SLEEP_FACTOR( ThermalWarning ), |
| 5183 | SLEEP_FACTOR( DisplayCaptured ), |
| 5184 | { 0, NULL } |
| 5185 | }; |
| 5186 | |
| 5187 | const IOPMSystemSleepPolicyTable * pt; |
| 5188 | OSSharedPtr<OSObject> prop; |
| 5189 | OSData * policyData; |
| 5190 | uint64_t currentFactors = 0; |
| 5191 | char currentFactorsBuf[512]; |
| 5192 | uint32_t standbyDelay = 0; |
| 5193 | uint32_t powerOffDelay = 0; |
| 5194 | uint32_t powerOffTimer = 0; |
| 5195 | uint32_t standbyTimer = 0; |
| 5196 | uint32_t mismatch; |
| 5197 | bool standbyEnabled; |
| 5198 | bool powerOffEnabled; |
| 5199 | bool found = false; |
| 5200 | |
| 5201 | // Get platform's sleep policy table |
| 5202 | if (!gSleepPolicyHandler) { |
| 5203 | prop = getServiceRoot()->copyProperty(kIOPlatformSystemSleepPolicyKey); |
| 5204 | if (!prop) { |
| 5205 | goto done; |
| 5206 | } |
| 5207 | } |
| 5208 | |
| 5209 | // Fetch additional settings |
| 5210 | standbyEnabled = (getSleepOption(kIOPMDeepSleepDelayKey, &standbyDelay) |
| 5211 | && propertyHasValue(kIOPMDeepSleepEnabledKey, kOSBooleanTrue)); |
| 5212 | powerOffEnabled = (getSleepOption(kIOPMAutoPowerOffDelayKey, &powerOffDelay) |
| 5213 | && propertyHasValue(kIOPMAutoPowerOffEnabledKey, kOSBooleanTrue)); |
| 5214 | if (!getSleepOption(kIOPMAutoPowerOffTimerKey, &powerOffTimer)) { |
| 5215 | powerOffTimer = powerOffDelay; |
| 5216 | } |
| 5217 | if (!getSleepOption(kIOPMDeepSleepTimerKey, &standbyTimer)) { |
| 5218 | standbyTimer = standbyDelay; |
| 5219 | } |
| 5220 | |
| 5221 | DLOG("phase %d, standby %d delay %u timer %u, poweroff %d delay %u timer %u, hibernate 0x%x\n" , |
| 5222 | sleepPhase, standbyEnabled, standbyDelay, standbyTimer, |
| 5223 | powerOffEnabled, powerOffDelay, powerOffTimer, *hibMode); |
| 5224 | |
| 5225 | currentFactorsBuf[0] = 0; |
| 5226 | // pmset level overrides |
| 5227 | if ((*hibMode & kIOHibernateModeOn) == 0) { |
| 5228 | if (!gSleepPolicyHandler) { |
| 5229 | standbyEnabled = false; |
| 5230 | powerOffEnabled = false; |
| 5231 | } |
| 5232 | } else if (!(*hibMode & kIOHibernateModeSleep)) { |
| 5233 | // Force hibernate (i.e. mode 25) |
| 5234 | // If standby is enabled, force standy. |
| 5235 | // If poweroff is enabled, force poweroff. |
| 5236 | if (standbyEnabled) { |
| 5237 | currentFactors |= kIOPMSleepFactorStandbyForced; |
| 5238 | } else if (powerOffEnabled) { |
| 5239 | currentFactors |= kIOPMSleepFactorAutoPowerOffForced; |
| 5240 | } else { |
| 5241 | currentFactors |= kIOPMSleepFactorHibernateForced; |
| 5242 | } |
| 5243 | } |
| 5244 | |
| 5245 | // Current factors based on environment and assertions |
| 5246 | if (sleepTimerMaintenance) { |
| 5247 | currentFactors |= kIOPMSleepFactorSleepTimerWake; |
| 5248 | } |
| 5249 | if (standbyEnabled && sleepToStandby && !gSleepPolicyHandler) { |
| 5250 | currentFactors |= kIOPMSleepFactorSleepTimerWake; |
| 5251 | } |
| 5252 | if (!clamshellClosed) { |
| 5253 | currentFactors |= kIOPMSleepFactorLidOpen; |
| 5254 | } |
| 5255 | if (acAdaptorConnected) { |
| 5256 | currentFactors |= kIOPMSleepFactorACPower; |
| 5257 | } |
| 5258 | if (lowBatteryCondition) { |
| 5259 | hibernateMode = 0; |
| 5260 | getSleepOption(kIOHibernateModeKey, &hibernateMode); |
| 5261 | if ((hibernateMode & kIOHibernateModeOn) == 0) { |
| 5262 | DLOG("HibernateMode is 0. Not sending LowBattery factor to IOPPF\n" ); |
| 5263 | } else { |
| 5264 | currentFactors |= kIOPMSleepFactorBatteryLow; |
| 5265 | } |
| 5266 | } |
| 5267 | if (!standbyDelay || !standbyTimer) { |
| 5268 | currentFactors |= kIOPMSleepFactorStandbyNoDelay; |
| 5269 | } |
| 5270 | if (standbyNixed || !standbyEnabled) { |
| 5271 | currentFactors |= kIOPMSleepFactorStandbyDisabled; |
| 5272 | } |
| 5273 | if (resetTimers) { |
| 5274 | currentFactors |= kIOPMSleepFactorLocalUserActivity; |
| 5275 | currentFactors &= ~kIOPMSleepFactorSleepTimerWake; |
| 5276 | } |
| 5277 | if (getPMAssertionLevel(kIOPMDriverAssertionUSBExternalDeviceBit) != |
| 5278 | kIOPMDriverAssertionLevelOff) { |
| 5279 | currentFactors |= kIOPMSleepFactorUSBExternalDevice; |
| 5280 | } |
| 5281 | if (getPMAssertionLevel(kIOPMDriverAssertionBluetoothHIDDevicePairedBit) != |
| 5282 | kIOPMDriverAssertionLevelOff) { |
| 5283 | currentFactors |= kIOPMSleepFactorBluetoothHIDDevice; |
| 5284 | } |
| 5285 | if (getPMAssertionLevel(kIOPMDriverAssertionExternalMediaMountedBit) != |
| 5286 | kIOPMDriverAssertionLevelOff) { |
| 5287 | currentFactors |= kIOPMSleepFactorExternalMediaMounted; |
| 5288 | } |
| 5289 | if (getPMAssertionLevel(kIOPMDriverAssertionReservedBit5) != |
| 5290 | kIOPMDriverAssertionLevelOff) { |
| 5291 | currentFactors |= kIOPMSleepFactorThunderboltDevice; |
| 5292 | } |
| 5293 | if (_scheduledAlarmMask != 0) { |
| 5294 | currentFactors |= kIOPMSleepFactorRTCAlarmScheduled; |
| 5295 | } |
| 5296 | if (getPMAssertionLevel(kIOPMDriverAssertionMagicPacketWakeEnabledBit) != |
| 5297 | kIOPMDriverAssertionLevelOff) { |
| 5298 | currentFactors |= kIOPMSleepFactorMagicPacketWakeEnabled; |
| 5299 | } |
| 5300 | #define TCPKEEPALIVE 1 |
| 5301 | #if TCPKEEPALIVE |
| 5302 | if (getPMAssertionLevel(kIOPMDriverAssertionNetworkKeepAliveActiveBit) != |
| 5303 | kIOPMDriverAssertionLevelOff) { |
| 5304 | currentFactors |= kIOPMSleepFactorNetworkKeepAliveActive; |
| 5305 | } |
| 5306 | #endif |
| 5307 | if (!powerOffEnabled) { |
| 5308 | currentFactors |= kIOPMSleepFactorAutoPowerOffDisabled; |
| 5309 | } |
| 5310 | if (desktopMode) { |
| 5311 | currentFactors |= kIOPMSleepFactorExternalDisplay; |
| 5312 | } |
| 5313 | if (userWasActive) { |
| 5314 | currentFactors |= kIOPMSleepFactorLocalUserActivity; |
| 5315 | } |
| 5316 | if (darkWakeHibernateError && !CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) { |
| 5317 | currentFactors |= kIOPMSleepFactorHibernateFailed; |
| 5318 | } |
| 5319 | if (thermalWarningState) { |
| 5320 | currentFactors |= kIOPMSleepFactorThermalWarning; |
| 5321 | } |
| 5322 | |
| 5323 | for (int factorBit = 0; factorBit < (8 * sizeof(uint32_t)); factorBit++) { |
| 5324 | uint32_t factor = 1 << factorBit; |
| 5325 | if (factor & currentFactors) { |
| 5326 | strlcat(currentFactorsBuf, ", " , sizeof(currentFactorsBuf)); |
| 5327 | strlcat(currentFactorsBuf, IOFindNameForValue(factor, factorValues), sizeof(currentFactorsBuf)); |
| 5328 | } |
| 5329 | } |
| 5330 | DLOG("sleep factors 0x%llx%s\n" , currentFactors, currentFactorsBuf); |
| 5331 | |
| 5332 | if (gSleepPolicyHandler) { |
| 5333 | uint32_t savedHibernateMode; |
| 5334 | IOReturn result; |
| 5335 | |
| 5336 | if (!gSleepPolicyVars) { |
| 5337 | gSleepPolicyVars = IOMallocType(IOPMSystemSleepPolicyVariables); |
| 5338 | } |
| 5339 | gSleepPolicyVars->signature = kIOPMSystemSleepPolicySignature; |
| 5340 | gSleepPolicyVars->version = kIOPMSystemSleepPolicyVersion; |
| 5341 | gSleepPolicyVars->currentCapability = _currentCapability; |
| 5342 | gSleepPolicyVars->highestCapability = _highestCapability; |
| 5343 | gSleepPolicyVars->sleepFactors = currentFactors; |
| 5344 | gSleepPolicyVars->sleepReason = lastSleepReason; |
| 5345 | gSleepPolicyVars->sleepPhase = sleepPhase; |
| 5346 | gSleepPolicyVars->standbyDelay = standbyDelay; |
| 5347 | gSleepPolicyVars->standbyTimer = standbyTimer; |
| 5348 | gSleepPolicyVars->poweroffDelay = powerOffDelay; |
| 5349 | gSleepPolicyVars->scheduledAlarms = _scheduledAlarmMask | _userScheduledAlarmMask; |
| 5350 | gSleepPolicyVars->poweroffTimer = powerOffTimer; |
| 5351 | |
| 5352 | if (kIOPMSleepPhase0 == sleepPhase) { |
| 5353 | // preserve hibernateMode |
| 5354 | savedHibernateMode = gSleepPolicyVars->hibernateMode; |
| 5355 | gSleepPolicyVars->hibernateMode = *hibMode; |
| 5356 | } else if (kIOPMSleepPhase1 == sleepPhase) { |
| 5357 | // use original hibernateMode for phase2 |
| 5358 | gSleepPolicyVars->hibernateMode = *hibMode; |
| 5359 | } |
| 5360 | |
| 5361 | result = gSleepPolicyHandler(gSleepPolicyTarget, gSleepPolicyVars, params); |
| 5362 | |
| 5363 | if (kIOPMSleepPhase0 == sleepPhase) { |
| 5364 | // restore hibernateMode |
| 5365 | gSleepPolicyVars->hibernateMode = savedHibernateMode; |
| 5366 | } |
| 5367 | |
| 5368 | if ((result != kIOReturnSuccess) || |
| 5369 | (kIOPMSleepTypeInvalid == params->sleepType) || |
| 5370 | (params->sleepType >= kIOPMSleepTypeLast) || |
| 5371 | (kIOPMSystemSleepParametersVersion != params->version)) { |
| 5372 | MSG("sleep policy handler error\n" ); |
| 5373 | goto done; |
| 5374 | } |
| 5375 | |
| 5376 | if ((getSleepTypeAttributes(params->sleepType) & |
| 5377 | kIOPMSleepAttributeHibernateSetup) && |
| 5378 | ((*hibMode & kIOHibernateModeOn) == 0)) { |
| 5379 | *hibMode |= (kIOHibernateModeOn | kIOHibernateModeSleep); |
| 5380 | } |
| 5381 | |
| 5382 | DLOG("sleep params v%u, type %u, flags 0x%x, wake 0x%x, timer %u, poweroff %u\n" , |
| 5383 | params->version, params->sleepType, params->sleepFlags, |
| 5384 | params->ecWakeEvents, params->ecWakeTimer, params->ecPoweroffTimer); |
| 5385 | found = true; |
| 5386 | goto done; |
| 5387 | } |
| 5388 | |
| 5389 | // Policy table is meaningless without standby enabled |
| 5390 | if (!standbyEnabled) { |
| 5391 | goto done; |
| 5392 | } |
| 5393 | |
| 5394 | // Validate the sleep policy table |
| 5395 | policyData = OSDynamicCast(OSData, prop.get()); |
| 5396 | if (!policyData || (policyData->getLength() <= sizeof(IOPMSystemSleepPolicyTable))) { |
| 5397 | goto done; |
| 5398 | } |
| 5399 | |
| 5400 | pt = (const IOPMSystemSleepPolicyTable *) policyData->getBytesNoCopy(); |
| 5401 | if ((pt->signature != kIOPMSystemSleepPolicySignature) || |
| 5402 | (pt->version != 1) || (0 == pt->entryCount)) { |
| 5403 | goto done; |
| 5404 | } |
| 5405 | |
| 5406 | if (((policyData->getLength() - sizeof(IOPMSystemSleepPolicyTable)) != |
| 5407 | (sizeof(IOPMSystemSleepPolicyEntry) * pt->entryCount))) { |
| 5408 | goto done; |
| 5409 | } |
| 5410 | |
| 5411 | for (uint32_t i = 0; i < pt->entryCount; i++) { |
| 5412 | const IOPMSystemSleepPolicyEntry * entry = &pt->entries[i]; |
| 5413 | mismatch = (((uint32_t)currentFactors ^ entry->factorBits) & entry->factorMask); |
| 5414 | |
| 5415 | DLOG("mask 0x%08x, bits 0x%08x, flags 0x%08x, wake 0x%08x, mismatch 0x%08x\n" , |
| 5416 | entry->factorMask, entry->factorBits, |
| 5417 | entry->sleepFlags, entry->wakeEvents, mismatch); |
| 5418 | if (mismatch) { |
| 5419 | continue; |
| 5420 | } |
| 5421 | |
| 5422 | DLOG("^ found match\n" ); |
| 5423 | found = true; |
| 5424 | |
| 5425 | params->version = kIOPMSystemSleepParametersVersion; |
| 5426 | params->reserved1 = 1; |
| 5427 | if (entry->sleepFlags & kIOPMSleepFlagHibernate) { |
| 5428 | params->sleepType = kIOPMSleepTypeStandby; |
| 5429 | } else { |
| 5430 | params->sleepType = kIOPMSleepTypeNormalSleep; |
| 5431 | } |
| 5432 | |
| 5433 | params->ecWakeEvents = entry->wakeEvents; |
| 5434 | if (entry->sleepFlags & kIOPMSleepFlagSleepTimerEnable) { |
| 5435 | if (kIOPMSleepPhase2 == sleepPhase) { |
| 5436 | clock_sec_t now_secs = gIOLastSleepTime.tv_sec; |
| 5437 | |
| 5438 | if (!_standbyTimerResetSeconds || |
| 5439 | (now_secs <= _standbyTimerResetSeconds)) { |
| 5440 | // Reset standby timer adjustment |
| 5441 | _standbyTimerResetSeconds = now_secs; |
| 5442 | DLOG("standby delay %u, reset %u\n" , |
| 5443 | standbyDelay, (uint32_t) _standbyTimerResetSeconds); |
| 5444 | } else if (standbyDelay) { |
| 5445 | // Shorten the standby delay timer |
| 5446 | clock_sec_t elapsed = now_secs - _standbyTimerResetSeconds; |
| 5447 | if (standbyDelay > elapsed) { |
| 5448 | standbyDelay -= elapsed; |
| 5449 | } else { |
| 5450 | standbyDelay = 1; // must be > 0 |
| 5451 | } |
| 5452 | DLOG("standby delay %u, elapsed %u\n" , |
| 5453 | standbyDelay, (uint32_t) elapsed); |
| 5454 | } |
| 5455 | } |
| 5456 | params->ecWakeTimer = standbyDelay; |
| 5457 | } else if (kIOPMSleepPhase2 == sleepPhase) { |
| 5458 | // A sleep that does not enable the sleep timer will reset |
| 5459 | // the standby delay adjustment. |
| 5460 | _standbyTimerResetSeconds = 0; |
| 5461 | } |
| 5462 | break; |
| 5463 | } |
| 5464 | |
| 5465 | done: |
| 5466 | return found; |
| 5467 | } |
| 5468 | |
| 5469 | static IOPMSystemSleepParameters gEarlySystemSleepParams; |
| 5470 | |
| 5471 | void |
| 5472 | IOPMrootDomain::evaluateSystemSleepPolicyEarly( void ) |
| 5473 | { |
| 5474 | // Evaluate early (priority interest phase), before drivers sleep. |
| 5475 | |
| 5476 | DLOG("%s\n" , __FUNCTION__); |
| 5477 | removeProperty(kIOPMSystemSleepParametersKey); |
| 5478 | |
| 5479 | // Full wake resets the standby timer delay adjustment |
| 5480 | if (_highestCapability & kIOPMSystemCapabilityGraphics) { |
| 5481 | _standbyTimerResetSeconds = 0; |
| 5482 | } |
| 5483 | |
| 5484 | hibernateDisabled = false; |
| 5485 | hibernateMode = 0; |
| 5486 | getSleepOption(kIOHibernateModeKey, &hibernateMode); |
| 5487 | |
| 5488 | // Save for late evaluation if sleep is aborted |
| 5489 | bzero(&gEarlySystemSleepParams, sizeof(gEarlySystemSleepParams)); |
| 5490 | |
| 5491 | if (evaluateSystemSleepPolicy(&gEarlySystemSleepParams, kIOPMSleepPhase1, |
| 5492 | &hibernateMode)) { |
| 5493 | if (!hibernateRetry && |
| 5494 | ((getSleepTypeAttributes(gEarlySystemSleepParams.sleepType) & |
| 5495 | kIOPMSleepAttributeHibernateSetup) == 0)) { |
| 5496 | // skip hibernate setup |
| 5497 | hibernateDisabled = true; |
| 5498 | } |
| 5499 | } |
| 5500 | |
| 5501 | // Publish IOPMSystemSleepType |
| 5502 | uint32_t sleepType = gEarlySystemSleepParams.sleepType; |
| 5503 | if (sleepType == kIOPMSleepTypeInvalid) { |
| 5504 | // no sleep policy |
| 5505 | sleepType = kIOPMSleepTypeNormalSleep; |
| 5506 | if (hibernateMode & kIOHibernateModeOn) { |
| 5507 | sleepType = (hibernateMode & kIOHibernateModeSleep) ? |
| 5508 | kIOPMSleepTypeSafeSleep : kIOPMSleepTypeHibernate; |
| 5509 | } |
| 5510 | } else if ((sleepType == kIOPMSleepTypeStandby) && |
| 5511 | (gEarlySystemSleepParams.ecPoweroffTimer)) { |
| 5512 | // report the lowest possible sleep state |
| 5513 | sleepType = kIOPMSleepTypePowerOff; |
| 5514 | } |
| 5515 | |
| 5516 | setProperty(kIOPMSystemSleepTypeKey, sleepType, 32); |
| 5517 | } |
| 5518 | |
| 5519 | void |
| 5520 | IOPMrootDomain::evaluateSystemSleepPolicyFinal( void ) |
| 5521 | { |
| 5522 | IOPMSystemSleepParameters params; |
| 5523 | OSSharedPtr<OSData> paramsData; |
| 5524 | bool wakeNow; |
| 5525 | // Evaluate sleep policy after sleeping drivers but before platform sleep. |
| 5526 | |
| 5527 | DLOG("%s\n" , __FUNCTION__); |
| 5528 | |
| 5529 | bzero(¶ms, sizeof(params)); |
| 5530 | wakeNow = false; |
| 5531 | if (evaluateSystemSleepPolicy(¶ms, kIOPMSleepPhase2, &hibernateMode)) { |
| 5532 | if ((kIOPMSleepTypeStandby == params.sleepType) |
| 5533 | && gIOHibernateStandbyDisabled && gSleepPolicyVars |
| 5534 | && (!((kIOPMSleepFactorStandbyForced | kIOPMSleepFactorAutoPowerOffForced | kIOPMSleepFactorHibernateForced) |
| 5535 | & gSleepPolicyVars->sleepFactors))) { |
| 5536 | standbyNixed = true; |
| 5537 | wakeNow = true; |
| 5538 | } |
| 5539 | if (wakeNow |
| 5540 | || ((hibernateDisabled || hibernateAborted) && |
| 5541 | (getSleepTypeAttributes(params.sleepType) & |
| 5542 | kIOPMSleepAttributeHibernateSetup))) { |
| 5543 | // Final evaluation picked a state requiring hibernation, |
| 5544 | // but hibernate isn't going to proceed. Arm a short sleep using |
| 5545 | // the early non-hibernate sleep parameters. |
| 5546 | bcopy(&gEarlySystemSleepParams, ¶ms, sizeof(params)); |
| 5547 | params.sleepType = kIOPMSleepTypeAbortedSleep; |
| 5548 | params.ecWakeTimer = 1; |
| 5549 | if (standbyNixed) { |
| 5550 | resetTimers = true; |
| 5551 | } else { |
| 5552 | // Set hibernateRetry flag to force hibernate setup on the |
| 5553 | // next sleep. |
| 5554 | hibernateRetry = true; |
| 5555 | } |
| 5556 | DLOG("wake in %u secs for hibernateDisabled %d, hibernateAborted %d, standbyNixed %d\n" , |
| 5557 | params.ecWakeTimer, hibernateDisabled, hibernateAborted, standbyNixed); |
| 5558 | } else { |
| 5559 | hibernateRetry = false; |
| 5560 | } |
| 5561 | |
| 5562 | if (kIOPMSleepTypeAbortedSleep != params.sleepType) { |
| 5563 | resetTimers = false; |
| 5564 | } |
| 5565 | |
| 5566 | paramsData = OSData::withValue(params); |
| 5567 | if (paramsData) { |
| 5568 | setProperty(kIOPMSystemSleepParametersKey, paramsData.get()); |
| 5569 | } |
| 5570 | |
| 5571 | if (getSleepTypeAttributes(params.sleepType) & |
| 5572 | kIOPMSleepAttributeHibernateSleep) { |
| 5573 | // Disable sleep to force hibernation |
| 5574 | gIOHibernateMode &= ~kIOHibernateModeSleep; |
| 5575 | } |
| 5576 | } |
| 5577 | } |
| 5578 | |
| 5579 | bool |
| 5580 | IOPMrootDomain::getHibernateSettings( |
| 5581 | uint32_t * hibernateModePtr, |
| 5582 | uint32_t * hibernateFreeRatio, |
| 5583 | uint32_t * hibernateFreeTime ) |
| 5584 | { |
| 5585 | // Called by IOHibernateSystemSleep() after evaluateSystemSleepPolicyEarly() |
| 5586 | // has updated the hibernateDisabled flag. |
| 5587 | |
| 5588 | bool ok = getSleepOption(kIOHibernateModeKey, hibernateModePtr); |
| 5589 | getSleepOption(kIOHibernateFreeRatioKey, hibernateFreeRatio); |
| 5590 | getSleepOption(kIOHibernateFreeTimeKey, hibernateFreeTime); |
| 5591 | if (hibernateDisabled) { |
| 5592 | *hibernateModePtr = 0; |
| 5593 | } else if (gSleepPolicyHandler) { |
| 5594 | *hibernateModePtr = hibernateMode; |
| 5595 | } |
| 5596 | DLOG("hibernateMode 0x%x\n" , *hibernateModePtr); |
| 5597 | return ok; |
| 5598 | } |
| 5599 | |
| 5600 | bool |
| 5601 | IOPMrootDomain::getSleepOption( const char * key, uint32_t * option ) |
| 5602 | { |
| 5603 | OSSharedPtr<OSObject> optionsProp; |
| 5604 | OSDictionary * optionsDict; |
| 5605 | OSSharedPtr<OSObject> obj; |
| 5606 | OSNumber * num; |
| 5607 | bool ok = false; |
| 5608 | |
| 5609 | optionsProp = copyProperty(kRootDomainSleepOptionsKey); |
| 5610 | optionsDict = OSDynamicCast(OSDictionary, optionsProp.get()); |
| 5611 | |
| 5612 | if (optionsDict) { |
| 5613 | obj.reset(optionsDict->getObject(key), OSRetain); |
| 5614 | } |
| 5615 | if (!obj) { |
| 5616 | obj = copyProperty(key); |
| 5617 | } |
| 5618 | if (obj) { |
| 5619 | if ((num = OSDynamicCast(OSNumber, obj.get()))) { |
| 5620 | *option = num->unsigned32BitValue(); |
| 5621 | ok = true; |
| 5622 | } else if (OSDynamicCast(OSBoolean, obj.get())) { |
| 5623 | *option = (obj == kOSBooleanTrue) ? 1 : 0; |
| 5624 | ok = true; |
| 5625 | } |
| 5626 | } |
| 5627 | |
| 5628 | return ok; |
| 5629 | } |
| 5630 | #endif /* HIBERNATION */ |
| 5631 | |
| 5632 | IOReturn |
| 5633 | IOPMrootDomain::getSystemSleepType( uint32_t * sleepType, uint32_t * standbyTimer ) |
| 5634 | { |
| 5635 | #if HIBERNATION |
| 5636 | IOPMSystemSleepParameters params; |
| 5637 | uint32_t hibMode = 0; |
| 5638 | bool ok; |
| 5639 | |
| 5640 | if (gIOPMWorkLoop->inGate() == false) { |
| 5641 | IOReturn ret = gIOPMWorkLoop->runAction( |
| 5642 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 5643 | &IOPMrootDomain::getSystemSleepType), |
| 5644 | (OSObject *) this, |
| 5645 | (void *) sleepType, (void *) standbyTimer); |
| 5646 | return ret; |
| 5647 | } |
| 5648 | |
| 5649 | getSleepOption(kIOHibernateModeKey, &hibMode); |
| 5650 | bzero(¶ms, sizeof(params)); |
| 5651 | |
| 5652 | ok = evaluateSystemSleepPolicy(¶ms, kIOPMSleepPhase0, &hibMode); |
| 5653 | if (ok) { |
| 5654 | *sleepType = params.sleepType; |
| 5655 | if (!getSleepOption(kIOPMDeepSleepTimerKey, standbyTimer) && |
| 5656 | !getSleepOption(kIOPMDeepSleepDelayKey, standbyTimer)) { |
| 5657 | DLOG("Standby delay is not set\n" ); |
| 5658 | *standbyTimer = 0; |
| 5659 | } |
| 5660 | return kIOReturnSuccess; |
| 5661 | } |
| 5662 | #endif |
| 5663 | |
| 5664 | return kIOReturnUnsupported; |
| 5665 | } |
| 5666 | |
| 5667 | // MARK: - |
| 5668 | // MARK: Shutdown and Restart |
| 5669 | |
| 5670 | //****************************************************************************** |
| 5671 | // handlePlatformHaltRestart |
| 5672 | // |
| 5673 | //****************************************************************************** |
| 5674 | |
| 5675 | // Phases while performing shutdown/restart |
| 5676 | typedef enum { |
| 5677 | kNotifyDone = 0x00, |
| 5678 | kNotifyPriorityClients = 0x10, |
| 5679 | kNotifyPowerPlaneDrivers = 0x20, |
| 5680 | kNotifyHaltRestartAction = 0x30, |
| 5681 | kQuiescePM = 0x40, |
| 5682 | } shutdownPhase_t; |
| 5683 | |
| 5684 | |
| 5685 | struct HaltRestartApplierContext { |
| 5686 | IOPMrootDomain * RootDomain; |
| 5687 | unsigned long PowerState; |
| 5688 | IOPMPowerFlags PowerFlags; |
| 5689 | UInt32 MessageType; |
| 5690 | UInt32 Counter; |
| 5691 | const char * LogString; |
| 5692 | shutdownPhase_t phase; |
| 5693 | |
| 5694 | IOServiceInterestHandler handler; |
| 5695 | } gHaltRestartCtx; |
| 5696 | |
| 5697 | const char * |
| 5698 | shutdownPhase2String(shutdownPhase_t phase) |
| 5699 | { |
| 5700 | switch (phase) { |
| 5701 | case kNotifyDone: |
| 5702 | return "Notifications completed" ; |
| 5703 | case kNotifyPriorityClients: |
| 5704 | return "Notifying priority clients" ; |
| 5705 | case kNotifyPowerPlaneDrivers: |
| 5706 | return "Notifying power plane drivers" ; |
| 5707 | case kNotifyHaltRestartAction: |
| 5708 | return "Notifying HaltRestart action handlers" ; |
| 5709 | case kQuiescePM: |
| 5710 | return "Quiescing PM" ; |
| 5711 | default: |
| 5712 | return "Unknown" ; |
| 5713 | } |
| 5714 | } |
| 5715 | |
| 5716 | static void |
| 5717 | platformHaltRestartApplier( OSObject * object, void * context ) |
| 5718 | { |
| 5719 | IOPowerStateChangeNotification notify; |
| 5720 | HaltRestartApplierContext * ctx; |
| 5721 | AbsoluteTime startTime, elapsedTime; |
| 5722 | uint32_t deltaTime; |
| 5723 | |
| 5724 | ctx = (HaltRestartApplierContext *) context; |
| 5725 | |
| 5726 | _IOServiceInterestNotifier * notifier; |
| 5727 | notifier = OSDynamicCast(_IOServiceInterestNotifier, object); |
| 5728 | memset(s: ¬ify, c: 0, n: sizeof(notify)); |
| 5729 | notify.powerRef = (void *)(uintptr_t)ctx->Counter; |
| 5730 | notify.returnValue = 0; |
| 5731 | notify.stateNumber = ctx->PowerState; |
| 5732 | notify.stateFlags = ctx->PowerFlags; |
| 5733 | |
| 5734 | if (notifier) { |
| 5735 | ctx->handler = notifier->handler; |
| 5736 | } |
| 5737 | |
| 5738 | clock_get_uptime(result: &startTime); |
| 5739 | ctx->RootDomain->messageClient( messageType: ctx->MessageType, client: object, messageArgument: (void *)¬ify ); |
| 5740 | deltaTime = computeDeltaTimeMS(startTime: &startTime, elapsedTime: &elapsedTime); |
| 5741 | |
| 5742 | if ((deltaTime > kPMHaltTimeoutMS) && notifier) { |
| 5743 | LOG("%s handler %p took %u ms\n" , |
| 5744 | ctx->LogString, OBFUSCATE(notifier->handler), deltaTime); |
| 5745 | halt_log_enter(what: "PowerOff/Restart message to priority client" , pc: (const void *) notifier->handler, time: elapsedTime); |
| 5746 | } |
| 5747 | |
| 5748 | ctx->handler = NULL; |
| 5749 | ctx->Counter++; |
| 5750 | } |
| 5751 | |
| 5752 | static void |
| 5753 | quiescePowerTreeCallback( void * target, void * param ) |
| 5754 | { |
| 5755 | IOLockLock(gPMHaltLock); |
| 5756 | gPMQuiesced = true; |
| 5757 | thread_wakeup(param); |
| 5758 | IOLockUnlock(gPMHaltLock); |
| 5759 | } |
| 5760 | |
| 5761 | void |
| 5762 | IOPMrootDomain::handlePlatformHaltRestart( UInt32 pe_type ) |
| 5763 | { |
| 5764 | AbsoluteTime startTime, elapsedTime; |
| 5765 | uint32_t deltaTime; |
| 5766 | bool nvramSync = false; |
| 5767 | |
| 5768 | memset(s: &gHaltRestartCtx, c: 0, n: sizeof(gHaltRestartCtx)); |
| 5769 | gHaltRestartCtx.RootDomain = this; |
| 5770 | |
| 5771 | clock_get_uptime(result: &startTime); |
| 5772 | switch (pe_type) { |
| 5773 | case kPEHaltCPU: |
| 5774 | case kPEUPSDelayHaltCPU: |
| 5775 | gHaltRestartCtx.PowerState = OFF_STATE; |
| 5776 | gHaltRestartCtx.MessageType = kIOMessageSystemWillPowerOff; |
| 5777 | gHaltRestartCtx.LogString = "PowerOff" ; |
| 5778 | nvramSync = true; |
| 5779 | break; |
| 5780 | |
| 5781 | case kPERestartCPU: |
| 5782 | gHaltRestartCtx.PowerState = RESTART_STATE; |
| 5783 | gHaltRestartCtx.MessageType = kIOMessageSystemWillRestart; |
| 5784 | gHaltRestartCtx.LogString = "Restart" ; |
| 5785 | nvramSync = true; |
| 5786 | break; |
| 5787 | |
| 5788 | case kPEPagingOff: |
| 5789 | gHaltRestartCtx.PowerState = ON_STATE; |
| 5790 | gHaltRestartCtx.MessageType = kIOMessageSystemPagingOff; |
| 5791 | gHaltRestartCtx.LogString = "PagingOff" ; |
| 5792 | IOService::updateConsoleUsers(NULL, kIOMessageSystemPagingOff); |
| 5793 | #if HIBERNATION |
| 5794 | IOHibernateSystemRestart(); |
| 5795 | #endif |
| 5796 | break; |
| 5797 | |
| 5798 | default: |
| 5799 | return; |
| 5800 | } |
| 5801 | |
| 5802 | if (nvramSync) { |
| 5803 | PESyncNVRAM(); |
| 5804 | } |
| 5805 | |
| 5806 | gHaltRestartCtx.phase = kNotifyPriorityClients; |
| 5807 | // Notify legacy clients |
| 5808 | applyToInterested(typeOfInterest: gIOPriorityPowerStateInterest, applier: platformHaltRestartApplier, context: &gHaltRestartCtx); |
| 5809 | |
| 5810 | // For normal shutdown, turn off File Server Mode. |
| 5811 | if (kPEHaltCPU == pe_type) { |
| 5812 | OSSharedPtr<const OSSymbol> setting = OSSymbol::withCString(kIOPMSettingRestartOnPowerLossKey); |
| 5813 | OSSharedPtr<OSNumber> num = OSNumber::withNumber(value: (unsigned long long) 0, numberOfBits: 32); |
| 5814 | if (setting && num) { |
| 5815 | setPMSetting(type: setting.get(), object: num.get()); |
| 5816 | } |
| 5817 | } |
| 5818 | |
| 5819 | if (kPEPagingOff != pe_type) { |
| 5820 | gHaltRestartCtx.phase = kNotifyPowerPlaneDrivers; |
| 5821 | // Notify in power tree order |
| 5822 | notifySystemShutdown(root: this, messageType: gHaltRestartCtx.MessageType); |
| 5823 | } |
| 5824 | |
| 5825 | gHaltRestartCtx.phase = kNotifyHaltRestartAction; |
| 5826 | #if defined(XNU_TARGET_OS_OSX) |
| 5827 | IOCPURunPlatformHaltRestartActions(message: pe_type); |
| 5828 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
| 5829 | if (kPEPagingOff != pe_type) { |
| 5830 | IOCPURunPlatformHaltRestartActions(pe_type); |
| 5831 | } |
| 5832 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
| 5833 | |
| 5834 | // Wait for PM to quiesce |
| 5835 | if ((kPEPagingOff != pe_type) && gPMHaltLock) { |
| 5836 | gHaltRestartCtx.phase = kQuiescePM; |
| 5837 | AbsoluteTime quiesceTime = mach_absolute_time(); |
| 5838 | |
| 5839 | IOLockLock(gPMHaltLock); |
| 5840 | gPMQuiesced = false; |
| 5841 | if (quiescePowerTree(target: this, action: &quiescePowerTreeCallback, param: &gPMQuiesced) == |
| 5842 | kIOReturnSuccess) { |
| 5843 | while (!gPMQuiesced) { |
| 5844 | IOLockSleep(lock: gPMHaltLock, event: &gPMQuiesced, THREAD_UNINT); |
| 5845 | } |
| 5846 | } |
| 5847 | IOLockUnlock(gPMHaltLock); |
| 5848 | deltaTime = computeDeltaTimeMS(startTime: &quiesceTime, elapsedTime: &elapsedTime); |
| 5849 | DLOG("PM quiesce took %u ms\n" , deltaTime); |
| 5850 | halt_log_enter(what: "Quiesce" , NULL, time: elapsedTime); |
| 5851 | } |
| 5852 | gHaltRestartCtx.phase = kNotifyDone; |
| 5853 | |
| 5854 | deltaTime = computeDeltaTimeMS(startTime: &startTime, elapsedTime: &elapsedTime); |
| 5855 | LOG("%s all drivers took %u ms\n" , gHaltRestartCtx.LogString, deltaTime); |
| 5856 | |
| 5857 | halt_log_enter(what: gHaltRestartCtx.LogString, NULL, time: elapsedTime); |
| 5858 | |
| 5859 | deltaTime = computeDeltaTimeMS(startTime: &gHaltStartTime, elapsedTime: &elapsedTime); |
| 5860 | LOG("%s total %u ms\n" , gHaltRestartCtx.LogString, deltaTime); |
| 5861 | |
| 5862 | if (gHaltLog && gHaltTimeMaxLog && (deltaTime >= gHaltTimeMaxLog)) { |
| 5863 | printf("%s total %d ms:%s\n" , gHaltRestartCtx.LogString, deltaTime, gHaltLog); |
| 5864 | } |
| 5865 | |
| 5866 | checkShutdownTimeout(); |
| 5867 | } |
| 5868 | |
| 5869 | bool |
| 5870 | IOPMrootDomain::checkShutdownTimeout() |
| 5871 | { |
| 5872 | AbsoluteTime elapsedTime; |
| 5873 | uint32_t deltaTime = computeDeltaTimeMS(startTime: &gHaltStartTime, elapsedTime: &elapsedTime); |
| 5874 | |
| 5875 | if (gHaltTimeMaxPanic && (deltaTime >= gHaltTimeMaxPanic)) { |
| 5876 | return true; |
| 5877 | } |
| 5878 | return false; |
| 5879 | } |
| 5880 | |
| 5881 | void |
| 5882 | IOPMrootDomain::panicWithShutdownLog(uint32_t timeoutInMs) |
| 5883 | { |
| 5884 | if (gHaltLog) { |
| 5885 | if ((gHaltRestartCtx.phase == kNotifyPriorityClients) && gHaltRestartCtx.handler) { |
| 5886 | halt_log_enter(what: "Blocked on priority client" , pc: (void *)gHaltRestartCtx.handler, time: mach_absolute_time() - gHaltStartTime); |
| 5887 | } |
| 5888 | panic("%s timed out in phase '%s'. Total %d ms:%s" , |
| 5889 | gHaltRestartCtx.LogString, shutdownPhase2String(gHaltRestartCtx.phase), timeoutInMs, gHaltLog); |
| 5890 | } else { |
| 5891 | panic("%s timed out in phase \'%s\'. Total %d ms" , |
| 5892 | gHaltRestartCtx.LogString, shutdownPhase2String(gHaltRestartCtx.phase), timeoutInMs); |
| 5893 | } |
| 5894 | } |
| 5895 | |
| 5896 | //****************************************************************************** |
| 5897 | // shutdownSystem |
| 5898 | // |
| 5899 | //****************************************************************************** |
| 5900 | |
| 5901 | IOReturn |
| 5902 | IOPMrootDomain::shutdownSystem( void ) |
| 5903 | { |
| 5904 | return kIOReturnUnsupported; |
| 5905 | } |
| 5906 | |
| 5907 | //****************************************************************************** |
| 5908 | // restartSystem |
| 5909 | // |
| 5910 | //****************************************************************************** |
| 5911 | |
| 5912 | IOReturn |
| 5913 | IOPMrootDomain::restartSystem( void ) |
| 5914 | { |
| 5915 | return kIOReturnUnsupported; |
| 5916 | } |
| 5917 | |
| 5918 | // MARK: - |
| 5919 | // MARK: System Capability |
| 5920 | |
| 5921 | //****************************************************************************** |
| 5922 | // tagPowerPlaneService |
| 5923 | // |
| 5924 | // Running on PM work loop thread. |
| 5925 | //****************************************************************************** |
| 5926 | |
| 5927 | void |
| 5928 | IOPMrootDomain::tagPowerPlaneService( |
| 5929 | IOService * service, |
| 5930 | IOPMActions * actions, |
| 5931 | IOPMPowerStateIndex maxPowerState ) |
| 5932 | { |
| 5933 | uint32_t flags = 0; |
| 5934 | |
| 5935 | memset(s: actions, c: 0, n: sizeof(*actions)); |
| 5936 | actions->target = this; |
| 5937 | |
| 5938 | if (service == this) { |
| 5939 | actions->actionPowerChangeStart = |
| 5940 | OSMemberFunctionCast( |
| 5941 | IOPMActionPowerChangeStart, this, |
| 5942 | &IOPMrootDomain::handleOurPowerChangeStart); |
| 5943 | |
| 5944 | actions->actionPowerChangeDone = |
| 5945 | OSMemberFunctionCast( |
| 5946 | IOPMActionPowerChangeDone, this, |
| 5947 | &IOPMrootDomain::handleOurPowerChangeDone); |
| 5948 | |
| 5949 | actions->actionPowerChangeOverride = |
| 5950 | OSMemberFunctionCast( |
| 5951 | IOPMActionPowerChangeOverride, this, |
| 5952 | &IOPMrootDomain::overrideOurPowerChange); |
| 5953 | return; |
| 5954 | } |
| 5955 | |
| 5956 | #if DISPLAY_WRANGLER_PRESENT |
| 5957 | if (NULL != service->metaCast("IODisplayWrangler" )) { |
| 5958 | // XXX should this really retain? |
| 5959 | wrangler.reset(service, OSRetain); |
| 5960 | wrangler->registerInterest(gIOGeneralInterest, |
| 5961 | &displayWranglerNotification, this, NULL); |
| 5962 | |
| 5963 | // found the display wrangler, check for any display assertions already created |
| 5964 | if (pmAssertions->getActivatedAssertions() & kIOPMDriverAssertionPreventDisplaySleepBit) { |
| 5965 | DLOG("wrangler setIgnoreIdleTimer\(1) due to pre-existing assertion\n" ); |
| 5966 | wrangler->setIgnoreIdleTimer( true ); |
| 5967 | } |
| 5968 | flags |= kPMActionsFlagIsDisplayWrangler; |
| 5969 | } |
| 5970 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 5971 | |
| 5972 | if (service->propertyExists(aKey: "IOPMStrictTreeOrder" )) { |
| 5973 | flags |= kPMActionsFlagIsGraphicsDriver; |
| 5974 | } |
| 5975 | if (service->propertyExists(aKey: "IOPMUnattendedWakePowerState" )) { |
| 5976 | flags |= kPMActionsFlagIsAudioDriver; |
| 5977 | } |
| 5978 | |
| 5979 | // Find the power connection object that is a child of the PCI host |
| 5980 | // bridge, and has a graphics/audio device attached below. Mark the |
| 5981 | // power branch for delayed child notifications. |
| 5982 | |
| 5983 | if (flags) { |
| 5984 | IORegistryEntry * child = service; |
| 5985 | IORegistryEntry * parent = child->getParentEntry(plane: gIOPowerPlane); |
| 5986 | |
| 5987 | while (child != this) { |
| 5988 | if (child->propertyHasValue(aKey: "IOPCITunnelled" , value: kOSBooleanTrue)) { |
| 5989 | // Skip delaying notifications and clamping power on external graphics and audio devices. |
| 5990 | DLOG("Avoiding delayChildNotification on object 0x%llx. flags: 0x%x\n" , service->getRegistryEntryID(), flags); |
| 5991 | flags = 0; |
| 5992 | break; |
| 5993 | } |
| 5994 | if ((parent == pciHostBridgeDriver) || |
| 5995 | (parent == this)) { |
| 5996 | if (OSDynamicCast(IOPowerConnection, child)) { |
| 5997 | IOPowerConnection * conn = (IOPowerConnection *) child; |
| 5998 | conn->delayChildNotification = true; |
| 5999 | DLOG("delayChildNotification for 0x%llx\n" , conn->getRegistryEntryID()); |
| 6000 | } |
| 6001 | break; |
| 6002 | } |
| 6003 | child = parent; |
| 6004 | parent = child->getParentEntry(plane: gIOPowerPlane); |
| 6005 | } |
| 6006 | } |
| 6007 | |
| 6008 | OSSharedPtr<OSObject> prop = service->copyProperty(kIOPMDarkWakeMaxPowerStateKey); |
| 6009 | if (prop) { |
| 6010 | OSNumber * num = OSDynamicCast(OSNumber, prop.get()); |
| 6011 | if (num) { |
| 6012 | actions->darkWakePowerState = num->unsigned32BitValue(); |
| 6013 | if (actions->darkWakePowerState < maxPowerState) { |
| 6014 | flags |= kPMActionsFlagHasDarkWakePowerState; |
| 6015 | } |
| 6016 | } |
| 6017 | } |
| 6018 | |
| 6019 | |
| 6020 | if (flags) { |
| 6021 | DLOG("%s tag flags %x\n" , service->getName(), flags); |
| 6022 | actions->flags |= flags; |
| 6023 | actions->actionPowerChangeOverride = |
| 6024 | OSMemberFunctionCast( |
| 6025 | IOPMActionPowerChangeOverride, this, |
| 6026 | &IOPMrootDomain::overridePowerChangeForService); |
| 6027 | |
| 6028 | if (flags & kPMActionsFlagIsDisplayWrangler) { |
| 6029 | actions->actionActivityTickle = |
| 6030 | OSMemberFunctionCast( |
| 6031 | IOPMActionActivityTickle, this, |
| 6032 | &IOPMrootDomain::handleActivityTickleForDisplayWrangler); |
| 6033 | |
| 6034 | actions->actionUpdatePowerClient = |
| 6035 | OSMemberFunctionCast( |
| 6036 | IOPMActionUpdatePowerClient, this, |
| 6037 | &IOPMrootDomain::handleUpdatePowerClientForDisplayWrangler); |
| 6038 | } |
| 6039 | return; |
| 6040 | } |
| 6041 | |
| 6042 | // Locate the first PCI host bridge for PMTrace. |
| 6043 | if (!pciHostBridgeDevice && service->metaCast(toMeta: "IOPCIBridge" )) { |
| 6044 | IOService * provider = service->getProvider(); |
| 6045 | if (OSDynamicCast(IOPlatformDevice, provider) && |
| 6046 | provider->inPlane(plane: gIODTPlane)) { |
| 6047 | pciHostBridgeDevice.reset(p: provider, OSNoRetain); |
| 6048 | pciHostBridgeDriver.reset(p: service, OSNoRetain); |
| 6049 | DLOG("PMTrace found PCI host bridge %s->%s\n" , |
| 6050 | provider->getName(), service->getName()); |
| 6051 | } |
| 6052 | } |
| 6053 | |
| 6054 | // Tag top-level PCI devices. The order of PMinit() call does not |
| 6055 | // change across boots and is used as the PCI bit number. |
| 6056 | if (pciHostBridgeDevice && service->metaCast(toMeta: "IOPCIDevice" )) { |
| 6057 | // Would prefer to check built-in property, but tagPowerPlaneService() |
| 6058 | // is called before pciDevice->registerService(). |
| 6059 | IORegistryEntry * parent = service->getParentEntry(plane: gIODTPlane); |
| 6060 | if ((parent == pciHostBridgeDevice) && service->propertyExists(aKey: "acpi-device" )) { |
| 6061 | int bit = pmTracer->recordTopLevelPCIDevice( service ); |
| 6062 | if (bit >= 0) { |
| 6063 | // Save the assigned bit for fast lookup. |
| 6064 | actions->flags |= (bit & kPMActionsPCIBitNumberMask); |
| 6065 | |
| 6066 | actions->actionPowerChangeStart = |
| 6067 | OSMemberFunctionCast( |
| 6068 | IOPMActionPowerChangeStart, this, |
| 6069 | &IOPMrootDomain::handlePowerChangeStartForPCIDevice); |
| 6070 | |
| 6071 | actions->actionPowerChangeDone = |
| 6072 | OSMemberFunctionCast( |
| 6073 | IOPMActionPowerChangeDone, this, |
| 6074 | &IOPMrootDomain::handlePowerChangeDoneForPCIDevice); |
| 6075 | } |
| 6076 | } |
| 6077 | } |
| 6078 | } |
| 6079 | |
| 6080 | //****************************************************************************** |
| 6081 | // PM actions for root domain |
| 6082 | //****************************************************************************** |
| 6083 | |
| 6084 | void |
| 6085 | IOPMrootDomain::overrideOurPowerChange( |
| 6086 | IOService * service, |
| 6087 | IOPMActions * actions, |
| 6088 | const IOPMRequest * request, |
| 6089 | IOPMPowerStateIndex * inOutPowerState, |
| 6090 | IOPMPowerChangeFlags * inOutChangeFlags ) |
| 6091 | { |
| 6092 | uint32_t changeFlags = *inOutChangeFlags; |
| 6093 | uint32_t desiredPowerState = (uint32_t) *inOutPowerState; |
| 6094 | uint32_t currentPowerState = (uint32_t) getPowerState(); |
| 6095 | |
| 6096 | if (request->getTag() == 0) { |
| 6097 | // Set a tag for any request that originates from IOServicePM |
| 6098 | (const_cast<IOPMRequest *>(request))->fTag = nextRequestTag(tag: kCPSReasonPMInternals); |
| 6099 | } |
| 6100 | |
| 6101 | DLOG("PowerChangeOverride (%s->%s, %x, 0x%x) tag 0x%x\n" , |
| 6102 | getPowerStateString(currentPowerState), |
| 6103 | getPowerStateString(desiredPowerState), |
| 6104 | _currentCapability, changeFlags, |
| 6105 | request->getTag()); |
| 6106 | |
| 6107 | |
| 6108 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 6109 | /* |
| 6110 | * ASBM send lowBattery notifications every 1 second until the device |
| 6111 | * enters hibernation. This queues up multiple sleep requests. |
| 6112 | * After the device wakes from hibernation, none of these previously |
| 6113 | * queued sleep requests are valid. |
| 6114 | * lowBattteryCondition variable is set when ASBM notifies rootDomain |
| 6115 | * and is cleared at the very last point in sleep. |
| 6116 | * Any attempt to sleep with reason kIOPMSleepReasonLowPower without |
| 6117 | * lowBatteryCondition is invalid |
| 6118 | */ |
| 6119 | if (REQUEST_TAG_TO_REASON(request->getTag()) == kIOPMSleepReasonLowPower) { |
| 6120 | if (!lowBatteryCondition) { |
| 6121 | DLOG("Duplicate lowBattery sleep" ); |
| 6122 | *inOutChangeFlags |= kIOPMNotDone; |
| 6123 | return; |
| 6124 | } |
| 6125 | } |
| 6126 | #endif |
| 6127 | |
| 6128 | if ((AOT_STATE == desiredPowerState) && (ON_STATE == currentPowerState)) { |
| 6129 | // Assertion may have been taken in AOT leading to changePowerStateTo(AOT) |
| 6130 | *inOutChangeFlags |= kIOPMNotDone; |
| 6131 | return; |
| 6132 | } |
| 6133 | |
| 6134 | if (changeFlags & kIOPMParentInitiated) { |
| 6135 | // Root parent is permanently pegged at max power, |
| 6136 | // a parent initiated power change is unexpected. |
| 6137 | *inOutChangeFlags |= kIOPMNotDone; |
| 6138 | return; |
| 6139 | } |
| 6140 | |
| 6141 | if (desiredPowerState < currentPowerState) { |
| 6142 | if (CAP_CURRENT(kIOPMSystemCapabilityGraphics)) { |
| 6143 | // Root domain is dropping power state from ON->SLEEP. |
| 6144 | // If system is in full wake, first enter dark wake by |
| 6145 | // converting the power drop to a capability change. |
| 6146 | // Once in dark wake, transition to sleep state ASAP. |
| 6147 | |
| 6148 | darkWakeToSleepASAP = true; |
| 6149 | |
| 6150 | // Drop graphics and audio capability |
| 6151 | _desiredCapability &= ~( |
| 6152 | kIOPMSystemCapabilityGraphics | |
| 6153 | kIOPMSystemCapabilityAudio); |
| 6154 | |
| 6155 | // Convert to capability change (ON->ON) |
| 6156 | *inOutPowerState = getRUN_STATE(); |
| 6157 | *inOutChangeFlags |= kIOPMSynchronize; |
| 6158 | |
| 6159 | // Revert device desire from SLEEP to ON |
| 6160 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonPowerOverride); |
| 6161 | } else { |
| 6162 | // System is already in dark wake, ok to drop power state. |
| 6163 | // Broadcast root power down to entire tree. |
| 6164 | *inOutChangeFlags |= kIOPMRootChangeDown; |
| 6165 | } |
| 6166 | } else if (desiredPowerState > currentPowerState) { |
| 6167 | if ((_currentCapability & kIOPMSystemCapabilityCPU) == 0) { |
| 6168 | // Broadcast power up when waking from sleep, but not for the |
| 6169 | // initial power change at boot by checking for cpu capability. |
| 6170 | *inOutChangeFlags |= kIOPMRootChangeUp; |
| 6171 | } |
| 6172 | } |
| 6173 | } |
| 6174 | |
| 6175 | void |
| 6176 | IOPMrootDomain::handleOurPowerChangeStart( |
| 6177 | IOService * service, |
| 6178 | IOPMActions * actions, |
| 6179 | const IOPMRequest * request, |
| 6180 | IOPMPowerStateIndex newPowerState, |
| 6181 | IOPMPowerChangeFlags * inOutChangeFlags ) |
| 6182 | { |
| 6183 | IOPMRequestTag requestTag = request->getTag(); |
| 6184 | IOPMRequestTag sleepReason; |
| 6185 | |
| 6186 | uint32_t changeFlags = *inOutChangeFlags; |
| 6187 | uint32_t currentPowerState = (uint32_t) getPowerState(); |
| 6188 | bool publishSleepReason = false; |
| 6189 | |
| 6190 | // Check if request has a valid sleep reason |
| 6191 | sleepReason = REQUEST_TAG_TO_REASON(requestTag); |
| 6192 | if (sleepReason < kIOPMSleepReasonClamshell) { |
| 6193 | sleepReason = kIOPMSleepReasonIdle; |
| 6194 | } |
| 6195 | |
| 6196 | _systemTransitionType = kSystemTransitionNone; |
| 6197 | _systemMessageClientMask = 0; |
| 6198 | capabilityLoss = false; |
| 6199 | toldPowerdCapWillChange = false; |
| 6200 | |
| 6201 | // Emergency notifications may arrive after the initial sleep request |
| 6202 | // has been queued. Override the sleep reason so powerd and others can |
| 6203 | // treat this as an emergency sleep. |
| 6204 | if (lowBatteryCondition) { |
| 6205 | sleepReason = kIOPMSleepReasonLowPower; |
| 6206 | } else if (thermalEmergencyState) { |
| 6207 | sleepReason = kIOPMSleepReasonThermalEmergency; |
| 6208 | } |
| 6209 | |
| 6210 | // 1. Explicit capability change. |
| 6211 | if (changeFlags & kIOPMSynchronize) { |
| 6212 | if (newPowerState == ON_STATE) { |
| 6213 | if (changeFlags & kIOPMSyncNoChildNotify) { |
| 6214 | _systemTransitionType = kSystemTransitionNewCapClient; |
| 6215 | } else { |
| 6216 | _systemTransitionType = kSystemTransitionCapability; |
| 6217 | } |
| 6218 | } |
| 6219 | } |
| 6220 | // 2. Going to sleep (cancellation still possible). |
| 6221 | else if (newPowerState < currentPowerState) { |
| 6222 | _systemTransitionType = kSystemTransitionSleep; |
| 6223 | } |
| 6224 | // 3. Woke from (idle or demand) sleep. |
| 6225 | else if (!systemBooting && |
| 6226 | (changeFlags & kIOPMSelfInitiated) && |
| 6227 | (newPowerState > currentPowerState)) { |
| 6228 | _systemTransitionType = kSystemTransitionWake; |
| 6229 | _desiredCapability = kIOPMSystemCapabilityCPU | kIOPMSystemCapabilityNetwork; |
| 6230 | |
| 6231 | // Early exit from dark wake to full (e.g. LID open) |
| 6232 | if (kFullWakeReasonNone != fullWakeReason) { |
| 6233 | _desiredCapability |= ( |
| 6234 | kIOPMSystemCapabilityGraphics | |
| 6235 | kIOPMSystemCapabilityAudio); |
| 6236 | |
| 6237 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 6238 | if (fullWakeReason == kFullWakeReasonLocalUser) { |
| 6239 | darkWakeExit = true; |
| 6240 | darkWakeToSleepASAP = false; |
| 6241 | setProperty(kIOPMRootDomainWakeTypeKey, aString: isRTCAlarmWake ? |
| 6242 | kIOPMRootDomainWakeTypeAlarm : kIOPMRootDomainWakeTypeUser); |
| 6243 | } |
| 6244 | #endif |
| 6245 | } |
| 6246 | #if HIBERNATION |
| 6247 | IOHibernateSetWakeCapabilities(_desiredCapability); |
| 6248 | #endif |
| 6249 | } |
| 6250 | |
| 6251 | // Update pending wake capability at the beginning of every |
| 6252 | // state transition (including synchronize). This will become |
| 6253 | // the current capability at the end of the transition. |
| 6254 | |
| 6255 | if (kSystemTransitionSleep == _systemTransitionType) { |
| 6256 | _pendingCapability = 0; |
| 6257 | capabilityLoss = true; |
| 6258 | } else if (kSystemTransitionNewCapClient != _systemTransitionType) { |
| 6259 | _pendingCapability = _desiredCapability | |
| 6260 | kIOPMSystemCapabilityCPU | |
| 6261 | kIOPMSystemCapabilityNetwork; |
| 6262 | |
| 6263 | if (_pendingCapability & kIOPMSystemCapabilityGraphics) { |
| 6264 | _pendingCapability |= kIOPMSystemCapabilityAudio; |
| 6265 | } |
| 6266 | |
| 6267 | if ((kSystemTransitionCapability == _systemTransitionType) && |
| 6268 | (_pendingCapability == _currentCapability)) { |
| 6269 | // Cancel the PM state change. |
| 6270 | _systemTransitionType = kSystemTransitionNone; |
| 6271 | *inOutChangeFlags |= kIOPMNotDone; |
| 6272 | } |
| 6273 | if (__builtin_popcount(_pendingCapability) < |
| 6274 | __builtin_popcount(_currentCapability)) { |
| 6275 | capabilityLoss = true; |
| 6276 | } |
| 6277 | } |
| 6278 | |
| 6279 | // 1. Capability change. |
| 6280 | if (kSystemTransitionCapability == _systemTransitionType) { |
| 6281 | // Dark to Full transition. |
| 6282 | if (CAP_GAIN(kIOPMSystemCapabilityGraphics)) { |
| 6283 | tracePoint( point: kIOPMTracePointDarkWakeExit ); |
| 6284 | |
| 6285 | #if defined(XNU_TARGET_OS_OSX) |
| 6286 | // rdar://problem/65627936 |
| 6287 | // When a dark->full wake promotion is scheduled before an ON->SLEEP |
| 6288 | // power state drop, invalidate any request to drop power state already |
| 6289 | // in the queue, including the override variant, unless full wake cannot |
| 6290 | // be sustained. Any power state drop queued after this SustainFullWake |
| 6291 | // request will not be affected. |
| 6292 | if (checkSystemCanSustainFullWake()) { |
| 6293 | changePowerStateWithOverrideTo(ordinal: getRUN_STATE(), reason: kCPSReasonSustainFullWake); |
| 6294 | } |
| 6295 | #endif |
| 6296 | |
| 6297 | willEnterFullWake(); |
| 6298 | } |
| 6299 | |
| 6300 | // Full to Dark transition. |
| 6301 | if (CAP_LOSS(kIOPMSystemCapabilityGraphics)) { |
| 6302 | // Clear previous stats |
| 6303 | IOLockLock(pmStatsLock); |
| 6304 | if (pmStatsAppResponses) { |
| 6305 | pmStatsAppResponses = OSArray::withCapacity(capacity: 5); |
| 6306 | } |
| 6307 | IOLockUnlock(pmStatsLock); |
| 6308 | |
| 6309 | tracePoint( point: kIOPMTracePointDarkWakeEntry ); |
| 6310 | *inOutChangeFlags |= kIOPMSyncTellPowerDown; |
| 6311 | _systemMessageClientMask = kSystemMessageClientPowerd | |
| 6312 | kSystemMessageClientLegacyApp; |
| 6313 | |
| 6314 | // rdar://15971327 |
| 6315 | // Prevent user active transitions before notifying clients |
| 6316 | // that system will sleep. |
| 6317 | preventTransitionToUserActive(prevent: true); |
| 6318 | |
| 6319 | IOService::setAdvisoryTickleEnable( false ); |
| 6320 | |
| 6321 | // Publish the sleep reason for full to dark wake |
| 6322 | publishSleepReason = true; |
| 6323 | lastSleepReason = fullToDarkReason = sleepReason; |
| 6324 | |
| 6325 | // Publish a UUID for the Sleep --> Wake cycle |
| 6326 | handlePublishSleepWakeUUID(shouldPublish: true); |
| 6327 | if (sleepDelaysReport) { |
| 6328 | clock_get_uptime(result: &ts_sleepStart); |
| 6329 | DLOG("sleepDelaysReport f->9 start at 0x%llx\n" , ts_sleepStart); |
| 6330 | } |
| 6331 | |
| 6332 | darkWakeExit = false; |
| 6333 | } |
| 6334 | } |
| 6335 | // 2. System sleep. |
| 6336 | else if (kSystemTransitionSleep == _systemTransitionType) { |
| 6337 | // Beginning of a system sleep transition. |
| 6338 | // Cancellation is still possible. |
| 6339 | tracePoint( point: kIOPMTracePointSleepStarted ); |
| 6340 | |
| 6341 | _systemMessageClientMask = kSystemMessageClientAll; |
| 6342 | if ((_currentCapability & kIOPMSystemCapabilityGraphics) == 0) { |
| 6343 | _systemMessageClientMask &= ~kSystemMessageClientLegacyApp; |
| 6344 | } |
| 6345 | if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0) { |
| 6346 | // Kernel priority clients are only notified on the initial |
| 6347 | // transition to full wake, so don't notify them unless system |
| 6348 | // has gained graphics capability since the last system wake. |
| 6349 | _systemMessageClientMask &= ~kSystemMessageClientKernel; |
| 6350 | } else { |
| 6351 | // System was in full wake, but the downwards power transition is driven |
| 6352 | // by a request that originates from IOServicePM, so it isn't tagged with |
| 6353 | // a valid system sleep reason. |
| 6354 | if (REQUEST_TAG_TO_REASON(requestTag) == kCPSReasonPMInternals) { |
| 6355 | // Publish the same reason for full to dark |
| 6356 | sleepReason = fullToDarkReason; |
| 6357 | } |
| 6358 | } |
| 6359 | #if HIBERNATION |
| 6360 | gIOHibernateState = 0; |
| 6361 | #endif |
| 6362 | |
| 6363 | // Record the reason for dark wake back to sleep |
| 6364 | // System may not have ever achieved full wake |
| 6365 | |
| 6366 | publishSleepReason = true; |
| 6367 | lastSleepReason = sleepReason; |
| 6368 | if (sleepDelaysReport) { |
| 6369 | clock_get_uptime(result: &ts_sleepStart); |
| 6370 | DLOG("sleepDelaysReport 9->0 start at 0x%llx\n" , ts_sleepStart); |
| 6371 | } |
| 6372 | } |
| 6373 | // 3. System wake. |
| 6374 | else if (kSystemTransitionWake == _systemTransitionType) { |
| 6375 | tracePoint( point: kIOPMTracePointWakeWillPowerOnClients ); |
| 6376 | // Clear stats about sleep |
| 6377 | |
| 6378 | if (AOT_STATE == newPowerState) { |
| 6379 | _pendingCapability = 0; |
| 6380 | } |
| 6381 | |
| 6382 | if (AOT_STATE == currentPowerState) { |
| 6383 | // Wake events are no longer accepted after waking to AOT_STATE. |
| 6384 | // Re-enable wake event acceptance to append wake events claimed |
| 6385 | // during the AOT to ON_STATE transition. |
| 6386 | acceptSystemWakeEvents(control: kAcceptSystemWakeEvents_Reenable); |
| 6387 | } |
| 6388 | |
| 6389 | if (_pendingCapability & kIOPMSystemCapabilityGraphics) { |
| 6390 | willEnterFullWake(); |
| 6391 | } |
| 6392 | } |
| 6393 | |
| 6394 | // The only location where the sleep reason is published. At this point |
| 6395 | // sleep can still be cancelled, but sleep reason should be published |
| 6396 | // early for logging purposes. |
| 6397 | |
| 6398 | if (publishSleepReason) { |
| 6399 | static const char * IOPMSleepReasons[] = |
| 6400 | { |
| 6401 | kIOPMClamshellSleepKey, |
| 6402 | kIOPMPowerButtonSleepKey, |
| 6403 | kIOPMSoftwareSleepKey, |
| 6404 | kIOPMOSSwitchHibernationKey, |
| 6405 | kIOPMIdleSleepKey, |
| 6406 | kIOPMLowPowerSleepKey, |
| 6407 | kIOPMThermalEmergencySleepKey, |
| 6408 | kIOPMMaintenanceSleepKey, |
| 6409 | kIOPMSleepServiceExitKey, |
| 6410 | kIOPMDarkWakeThermalEmergencyKey, |
| 6411 | kIOPMNotificationWakeExitKey |
| 6412 | }; |
| 6413 | |
| 6414 | // Record sleep cause in IORegistry |
| 6415 | uint32_t reasonIndex = sleepReason - kIOPMSleepReasonClamshell; |
| 6416 | if (reasonIndex < sizeof(IOPMSleepReasons) / sizeof(IOPMSleepReasons[0])) { |
| 6417 | DLOG("sleep reason %s\n" , IOPMSleepReasons[reasonIndex]); |
| 6418 | #if DEVELOPMENT || DEBUG |
| 6419 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 6420 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 6421 | "Sleep Reason" , "%s\n" , IOPMSleepReasons[reasonIndex] |
| 6422 | ); |
| 6423 | #endif /* DEVELOPMENT || DEBUG */ |
| 6424 | setProperty(kRootDomainSleepReasonKey, aString: IOPMSleepReasons[reasonIndex]); |
| 6425 | } |
| 6426 | } |
| 6427 | |
| 6428 | if ((kSystemTransitionNone != _systemTransitionType) && |
| 6429 | (kSystemTransitionNewCapClient != _systemTransitionType)) { |
| 6430 | _systemStateGeneration++; |
| 6431 | systemDarkWake = false; |
| 6432 | |
| 6433 | DLOG("=== START (%s->%s, %x->%x, 0x%x) gen %u, msg %x, tag %x\n" , |
| 6434 | getPowerStateString(currentPowerState), |
| 6435 | getPowerStateString((uint32_t) newPowerState), |
| 6436 | _currentCapability, _pendingCapability, |
| 6437 | *inOutChangeFlags, _systemStateGeneration, _systemMessageClientMask, |
| 6438 | requestTag); |
| 6439 | #if DEVELOPMENT || DEBUG |
| 6440 | if (currentPowerState != (uint32_t) newPowerState) { |
| 6441 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 6442 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 6443 | "Start Power State Trans." , |
| 6444 | "(%s->%s, %x->%x, 0x%x) gen %u, msg %x, tag %x\n" , |
| 6445 | getPowerStateString(currentPowerState), |
| 6446 | getPowerStateString((uint32_t) newPowerState), |
| 6447 | _currentCapability, |
| 6448 | _pendingCapability, |
| 6449 | *inOutChangeFlags, |
| 6450 | _systemStateGeneration, |
| 6451 | _systemMessageClientMask, |
| 6452 | requestTag |
| 6453 | ); |
| 6454 | } |
| 6455 | #endif /* DEVELOPMENT || DEBUG */ |
| 6456 | } |
| 6457 | |
| 6458 | if ((AOT_STATE == newPowerState) && (SLEEP_STATE != currentPowerState)) { |
| 6459 | panic("illegal AOT entry from %s" , getPowerStateString(currentPowerState)); |
| 6460 | } |
| 6461 | if (_aotNow && (ON_STATE == newPowerState)) { |
| 6462 | WAKEEVENT_LOCK(); |
| 6463 | aotShouldExit(checkTimeSet: false, software: true); |
| 6464 | WAKEEVENT_UNLOCK(); |
| 6465 | aotExit(cps: false); |
| 6466 | } |
| 6467 | } |
| 6468 | |
| 6469 | void |
| 6470 | IOPMrootDomain::handleOurPowerChangeDone( |
| 6471 | IOService * service, |
| 6472 | IOPMActions * actions, |
| 6473 | const IOPMRequest * request, |
| 6474 | IOPMPowerStateIndex oldPowerState, |
| 6475 | IOPMPowerChangeFlags changeFlags ) |
| 6476 | { |
| 6477 | if (kSystemTransitionNewCapClient == _systemTransitionType) { |
| 6478 | _systemTransitionType = kSystemTransitionNone; |
| 6479 | return; |
| 6480 | } |
| 6481 | |
| 6482 | if (_systemTransitionType != kSystemTransitionNone) { |
| 6483 | uint32_t currentPowerState = (uint32_t) getPowerState(); |
| 6484 | |
| 6485 | if (changeFlags & kIOPMNotDone) { |
| 6486 | // Power down was cancelled or vetoed. |
| 6487 | _pendingCapability = _currentCapability; |
| 6488 | lastSleepReason = 0; |
| 6489 | |
| 6490 | // When sleep is cancelled or reverted, don't report |
| 6491 | // the target (lower) power state as the previous state. |
| 6492 | oldPowerState = currentPowerState; |
| 6493 | |
| 6494 | if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics) && |
| 6495 | CAP_CURRENT(kIOPMSystemCapabilityCPU)) { |
| 6496 | #if defined(XNU_TARGET_OS_OSX) |
| 6497 | pmPowerStateQueue->submitPowerEvent( |
| 6498 | eventType: kPowerEventPolicyStimulus, |
| 6499 | arg0: (void *) kStimulusDarkWakeReentry, |
| 6500 | arg1: _systemStateGeneration ); |
| 6501 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
| 6502 | // On embedded, there are no factors that can prolong a |
| 6503 | // "darkWake" when a power down is vetoed. We need to |
| 6504 | // promote to "fullWake" at least once so that factors |
| 6505 | // that prevent idle sleep can assert themselves if required |
| 6506 | pmPowerStateQueue->submitPowerEvent( |
| 6507 | kPowerEventPolicyStimulus, |
| 6508 | (void *) kStimulusDarkWakeActivityTickle); |
| 6509 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
| 6510 | } |
| 6511 | |
| 6512 | // Revert device desire to max. |
| 6513 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonPowerDownCancel); |
| 6514 | } else { |
| 6515 | // Send message on dark wake to full wake promotion. |
| 6516 | // tellChangeUp() handles the normal SLEEP->ON case. |
| 6517 | |
| 6518 | if (kSystemTransitionCapability == _systemTransitionType) { |
| 6519 | if (CAP_GAIN(kIOPMSystemCapabilityGraphics)) { |
| 6520 | lastSleepReason = 0; // stop logging wrangler tickles |
| 6521 | tellClients(kIOMessageSystemHasPoweredOn); |
| 6522 | } |
| 6523 | if (CAP_LOSS(kIOPMSystemCapabilityGraphics)) { |
| 6524 | // Going dark, reset full wake state |
| 6525 | // userIsActive will be cleared by wrangler powering down |
| 6526 | fullWakeReason = kFullWakeReasonNone; |
| 6527 | |
| 6528 | if (ts_sleepStart) { |
| 6529 | clock_get_uptime(result: &wake2DarkwakeDelay); |
| 6530 | SUB_ABSOLUTETIME(&wake2DarkwakeDelay, &ts_sleepStart); |
| 6531 | DLOG("sleepDelaysReport f->9 end 0x%llx\n" , wake2DarkwakeDelay); |
| 6532 | ts_sleepStart = 0; |
| 6533 | } |
| 6534 | } |
| 6535 | } |
| 6536 | |
| 6537 | // Reset state after exiting from dark wake. |
| 6538 | |
| 6539 | if (CAP_GAIN(kIOPMSystemCapabilityGraphics) || |
| 6540 | CAP_LOSS(kIOPMSystemCapabilityCPU)) { |
| 6541 | darkWakeMaintenance = false; |
| 6542 | darkWakeToSleepASAP = false; |
| 6543 | pciCantSleepValid = false; |
| 6544 | darkWakeSleepService = false; |
| 6545 | |
| 6546 | if (CAP_LOSS(kIOPMSystemCapabilityCPU)) { |
| 6547 | // Remove the influence of display power assertion |
| 6548 | // before next system wake. |
| 6549 | if (wrangler) { |
| 6550 | wrangler->changePowerStateForRootDomain( |
| 6551 | ordinal: kWranglerPowerStateMin ); |
| 6552 | } |
| 6553 | removeProperty(aKey: gIOPMUserTriggeredFullWakeKey.get()); |
| 6554 | } |
| 6555 | } |
| 6556 | |
| 6557 | // Entered dark mode. |
| 6558 | |
| 6559 | if (((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) && |
| 6560 | (_pendingCapability & kIOPMSystemCapabilityCPU)) { |
| 6561 | // Queue an evaluation of whether to remain in dark wake, |
| 6562 | // and for how long. This serves the purpose of draining |
| 6563 | // any assertions from the queue. |
| 6564 | |
| 6565 | pmPowerStateQueue->submitPowerEvent( |
| 6566 | eventType: kPowerEventPolicyStimulus, |
| 6567 | arg0: (void *) kStimulusDarkWakeEntry, |
| 6568 | arg1: _systemStateGeneration ); |
| 6569 | } |
| 6570 | } |
| 6571 | |
| 6572 | #if DEVELOPMENT || DEBUG |
| 6573 | if (currentPowerState != (uint32_t) oldPowerState) { |
| 6574 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 6575 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 6576 | "Finish Power State Trans." , |
| 6577 | "(%s->%s, %x->%x, 0x%x) gen %u, msg %x, tag %x\n" , |
| 6578 | getPowerStateString((uint32_t)oldPowerState), |
| 6579 | getPowerStateString(currentPowerState), |
| 6580 | _currentCapability, |
| 6581 | _pendingCapability, |
| 6582 | changeFlags, |
| 6583 | _systemStateGeneration, |
| 6584 | _systemMessageClientMask, |
| 6585 | request->getTag() |
| 6586 | ); |
| 6587 | } |
| 6588 | #endif /* DEVELOPMENT || DEBUG */ |
| 6589 | |
| 6590 | DLOG("=== FINISH (%s->%s, %x->%x, 0x%x) gen %u, msg %x, tag %x\n" , |
| 6591 | getPowerStateString((uint32_t) oldPowerState), getPowerStateString(currentPowerState), |
| 6592 | _currentCapability, _pendingCapability, |
| 6593 | changeFlags, _systemStateGeneration, _systemMessageClientMask, |
| 6594 | request->getTag()); |
| 6595 | |
| 6596 | if ((currentPowerState == ON_STATE) && pmAssertions) { |
| 6597 | pmAssertions->reportCPUBitAccounting(); |
| 6598 | } |
| 6599 | |
| 6600 | if (_pendingCapability & kIOPMSystemCapabilityGraphics) { |
| 6601 | displayWakeCnt++; |
| 6602 | #if DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY |
| 6603 | if (clamshellExists && fullWakeThreadCall) { |
| 6604 | AbsoluteTime deadline; |
| 6605 | clock_interval_to_deadline(DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY, kSecondScale, &deadline); |
| 6606 | thread_call_enter_delayed(fullWakeThreadCall, deadline); |
| 6607 | } |
| 6608 | #endif |
| 6609 | } else if (CAP_GAIN(kIOPMSystemCapabilityCPU)) { |
| 6610 | darkWakeCnt++; |
| 6611 | } |
| 6612 | |
| 6613 | // Update current system capability. |
| 6614 | if (_currentCapability != _pendingCapability) { |
| 6615 | _currentCapability = _pendingCapability; |
| 6616 | } |
| 6617 | |
| 6618 | // Update highest system capability. |
| 6619 | |
| 6620 | _highestCapability |= _currentCapability; |
| 6621 | |
| 6622 | if (darkWakePostTickle && |
| 6623 | (kSystemTransitionWake == _systemTransitionType) && |
| 6624 | (gDarkWakeFlags & kDarkWakeFlagPromotionMask) == |
| 6625 | kDarkWakeFlagPromotionLate) { |
| 6626 | darkWakePostTickle = false; |
| 6627 | reportUserInput(); |
| 6628 | } else if (darkWakeExit) { |
| 6629 | requestFullWake( reason: kFullWakeReasonLocalUser ); |
| 6630 | } |
| 6631 | |
| 6632 | // Reset tracepoint at completion of capability change, |
| 6633 | // completion of wake transition, and aborted sleep transition. |
| 6634 | |
| 6635 | if ((_systemTransitionType == kSystemTransitionCapability) || |
| 6636 | (_systemTransitionType == kSystemTransitionWake) || |
| 6637 | ((_systemTransitionType == kSystemTransitionSleep) && |
| 6638 | (changeFlags & kIOPMNotDone))) { |
| 6639 | setProperty(kIOPMSystemCapabilitiesKey, aValue: _currentCapability, aNumberOfBits: 64); |
| 6640 | tracePoint( point: kIOPMTracePointSystemUp ); |
| 6641 | } |
| 6642 | |
| 6643 | _systemTransitionType = kSystemTransitionNone; |
| 6644 | _systemMessageClientMask = 0; |
| 6645 | toldPowerdCapWillChange = false; |
| 6646 | |
| 6647 | darkWakeLogClamp = false; |
| 6648 | |
| 6649 | if (lowBatteryCondition) { |
| 6650 | privateSleepSystem(sleepReason: kIOPMSleepReasonLowPower); |
| 6651 | } else if (thermalEmergencyState) { |
| 6652 | privateSleepSystem(sleepReason: kIOPMSleepReasonThermalEmergency); |
| 6653 | } else if ((fullWakeReason == kFullWakeReasonDisplayOn) && !displayPowerOnRequested) { |
| 6654 | // Request for full wake is removed while system is waking up to full wake |
| 6655 | DLOG("DisplayOn fullwake request is removed\n" ); |
| 6656 | handleSetDisplayPowerOn(powerOn: false); |
| 6657 | } |
| 6658 | |
| 6659 | if ((gClamshellFlags & kClamshell_WAR_47715679) && isRTCAlarmWake) { |
| 6660 | pmPowerStateQueue->submitPowerEvent( |
| 6661 | eventType: kPowerEventReceivedPowerNotification, arg0: (void *)(uintptr_t) kLocalEvalClamshellCommand ); |
| 6662 | } |
| 6663 | } |
| 6664 | } |
| 6665 | |
| 6666 | //****************************************************************************** |
| 6667 | // PM actions for graphics and audio. |
| 6668 | //****************************************************************************** |
| 6669 | |
| 6670 | void |
| 6671 | IOPMrootDomain::overridePowerChangeForService( |
| 6672 | IOService * service, |
| 6673 | IOPMActions * actions, |
| 6674 | const IOPMRequest * request, |
| 6675 | IOPMPowerStateIndex * inOutPowerState, |
| 6676 | IOPMPowerChangeFlags * inOutChangeFlags ) |
| 6677 | { |
| 6678 | uint32_t powerState = (uint32_t) *inOutPowerState; |
| 6679 | uint32_t changeFlags = (uint32_t) *inOutChangeFlags; |
| 6680 | const uint32_t actionFlags = actions->flags; |
| 6681 | |
| 6682 | if (kSystemTransitionNone == _systemTransitionType) { |
| 6683 | // Not in midst of a system transition. |
| 6684 | // Do not set kPMActionsStatePowerClamped. |
| 6685 | } else if ((actions->state & kPMActionsStatePowerClamped) == 0) { |
| 6686 | bool enableClamp = false; |
| 6687 | |
| 6688 | // For most drivers, enable the clamp during ON->Dark transition |
| 6689 | // which has the kIOPMSynchronize flag set in changeFlags. |
| 6690 | if ((actionFlags & kPMActionsFlagIsDisplayWrangler) && |
| 6691 | ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) && |
| 6692 | (changeFlags & kIOPMSynchronize)) { |
| 6693 | enableClamp = true; |
| 6694 | } else if ((actionFlags & kPMActionsFlagIsAudioDriver) && |
| 6695 | ((gDarkWakeFlags & kDarkWakeFlagAudioNotSuppressed) == 0) && |
| 6696 | ((_pendingCapability & kIOPMSystemCapabilityAudio) == 0) && |
| 6697 | (changeFlags & kIOPMSynchronize)) { |
| 6698 | enableClamp = true; |
| 6699 | } else if ((actionFlags & kPMActionsFlagHasDarkWakePowerState) && |
| 6700 | ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0) && |
| 6701 | (changeFlags & kIOPMSynchronize)) { |
| 6702 | enableClamp = true; |
| 6703 | } else if ((actionFlags & kPMActionsFlagIsGraphicsDriver) && |
| 6704 | (_systemTransitionType == kSystemTransitionSleep)) { |
| 6705 | // For graphics drivers, clamp power when entering |
| 6706 | // system sleep. Not when dropping to dark wake. |
| 6707 | enableClamp = true; |
| 6708 | } |
| 6709 | |
| 6710 | if (enableClamp) { |
| 6711 | actions->state |= kPMActionsStatePowerClamped; |
| 6712 | DLOG("power clamp enabled %s %qx, pendingCap 0x%x, ps %d, cflags 0x%x\n" , |
| 6713 | service->getName(), service->getRegistryEntryID(), |
| 6714 | _pendingCapability, powerState, changeFlags); |
| 6715 | } |
| 6716 | } else if ((actions->state & kPMActionsStatePowerClamped) != 0) { |
| 6717 | bool disableClamp = false; |
| 6718 | |
| 6719 | if ((actionFlags & ( |
| 6720 | kPMActionsFlagIsDisplayWrangler | |
| 6721 | kPMActionsFlagIsGraphicsDriver)) && |
| 6722 | (_pendingCapability & kIOPMSystemCapabilityGraphics)) { |
| 6723 | disableClamp = true; |
| 6724 | } else if ((actionFlags & kPMActionsFlagIsAudioDriver) && |
| 6725 | (_pendingCapability & kIOPMSystemCapabilityAudio)) { |
| 6726 | disableClamp = true; |
| 6727 | } else if ((actionFlags & kPMActionsFlagHasDarkWakePowerState) && |
| 6728 | (_pendingCapability & kIOPMSystemCapabilityGraphics)) { |
| 6729 | disableClamp = true; |
| 6730 | } |
| 6731 | |
| 6732 | if (disableClamp) { |
| 6733 | actions->state &= ~kPMActionsStatePowerClamped; |
| 6734 | DLOG("power clamp removed %s %qx, pendingCap 0x%x, ps %d, cflags 0x%x\n" , |
| 6735 | service->getName(), service->getRegistryEntryID(), |
| 6736 | _pendingCapability, powerState, changeFlags); |
| 6737 | } |
| 6738 | } |
| 6739 | |
| 6740 | if (actions->state & kPMActionsStatePowerClamped) { |
| 6741 | uint32_t maxPowerState = 0; |
| 6742 | |
| 6743 | // Determine the max power state allowed when clamp is enabled |
| 6744 | if (changeFlags & (kIOPMDomainDidChange | kIOPMDomainWillChange)) { |
| 6745 | // Parent intiated power state changes |
| 6746 | if ((service->getPowerState() > maxPowerState) && |
| 6747 | (actionFlags & kPMActionsFlagIsDisplayWrangler)) { |
| 6748 | maxPowerState++; |
| 6749 | |
| 6750 | // Remove lingering effects of any tickle before entering |
| 6751 | // dark wake. It will take a new tickle to return to full |
| 6752 | // wake, so the existing tickle state is useless. |
| 6753 | |
| 6754 | if (changeFlags & kIOPMDomainDidChange) { |
| 6755 | *inOutChangeFlags |= kIOPMExpireIdleTimer; |
| 6756 | } |
| 6757 | } else if (actionFlags & kPMActionsFlagIsGraphicsDriver) { |
| 6758 | maxPowerState++; |
| 6759 | } else if (actionFlags & kPMActionsFlagHasDarkWakePowerState) { |
| 6760 | maxPowerState = actions->darkWakePowerState; |
| 6761 | } |
| 6762 | } else { |
| 6763 | // Deny all self-initiated changes when power is limited. |
| 6764 | // Wrangler tickle should never defeat the limiter. |
| 6765 | maxPowerState = service->getPowerState(); |
| 6766 | } |
| 6767 | |
| 6768 | if (powerState > maxPowerState) { |
| 6769 | DLOG("power clamped %s %qx, ps %u->%u, cflags 0x%x)\n" , |
| 6770 | service->getName(), service->getRegistryEntryID(), |
| 6771 | powerState, maxPowerState, changeFlags); |
| 6772 | *inOutPowerState = maxPowerState; |
| 6773 | |
| 6774 | if (darkWakePostTickle && |
| 6775 | (actionFlags & kPMActionsFlagIsDisplayWrangler) && |
| 6776 | (changeFlags & kIOPMDomainWillChange) && |
| 6777 | ((gDarkWakeFlags & kDarkWakeFlagPromotionMask) == |
| 6778 | kDarkWakeFlagPromotionEarly)) { |
| 6779 | darkWakePostTickle = false; |
| 6780 | reportUserInput(); |
| 6781 | } |
| 6782 | } |
| 6783 | |
| 6784 | if (!darkWakePowerClamped && (changeFlags & kIOPMDomainDidChange)) { |
| 6785 | if (darkWakeLogClamp) { |
| 6786 | AbsoluteTime now; |
| 6787 | uint64_t nsec; |
| 6788 | |
| 6789 | clock_get_uptime(result: &now); |
| 6790 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 6791 | absolutetime_to_nanoseconds(abstime: now, result: &nsec); |
| 6792 | DLOG("dark wake power clamped after %u ms\n" , |
| 6793 | ((int)((nsec) / NSEC_PER_MSEC))); |
| 6794 | } |
| 6795 | darkWakePowerClamped = true; |
| 6796 | } |
| 6797 | } |
| 6798 | } |
| 6799 | |
| 6800 | void |
| 6801 | IOPMrootDomain::handleActivityTickleForDisplayWrangler( |
| 6802 | IOService * service, |
| 6803 | IOPMActions * actions ) |
| 6804 | { |
| 6805 | #if DISPLAY_WRANGLER_PRESENT |
| 6806 | // Warning: Not running in PM work loop context - don't modify state !!! |
| 6807 | // Trap tickle directed to IODisplayWrangler while running with graphics |
| 6808 | // capability suppressed. |
| 6809 | |
| 6810 | assert(service == wrangler); |
| 6811 | |
| 6812 | clock_get_uptime(&userActivityTime); |
| 6813 | bool aborting = ((lastSleepReason == kIOPMSleepReasonIdle) |
| 6814 | || (lastSleepReason == kIOPMSleepReasonMaintenance) |
| 6815 | || (lastSleepReason == kIOPMSleepReasonSoftware)); |
| 6816 | if (aborting) { |
| 6817 | userActivityCount++; |
| 6818 | DLOG("display wrangler tickled1 %d lastSleepReason %d\n" , |
| 6819 | userActivityCount, lastSleepReason); |
| 6820 | } |
| 6821 | |
| 6822 | if (!darkWakeExit && ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0)) { |
| 6823 | DLOG("display wrangler tickled\n" ); |
| 6824 | if (kIOLogPMRootDomain & gIOKitDebug) { |
| 6825 | OSReportWithBacktrace("Dark wake display tickle" ); |
| 6826 | } |
| 6827 | if (pmPowerStateQueue) { |
| 6828 | pmPowerStateQueue->submitPowerEvent( |
| 6829 | kPowerEventPolicyStimulus, |
| 6830 | (void *) kStimulusDarkWakeActivityTickle, |
| 6831 | true /* set wake type */ ); |
| 6832 | } |
| 6833 | } |
| 6834 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 6835 | } |
| 6836 | |
| 6837 | void |
| 6838 | IOPMrootDomain::handleUpdatePowerClientForDisplayWrangler( |
| 6839 | IOService * service, |
| 6840 | IOPMActions * actions, |
| 6841 | const OSSymbol * powerClient, |
| 6842 | IOPMPowerStateIndex oldPowerState, |
| 6843 | IOPMPowerStateIndex newPowerState ) |
| 6844 | { |
| 6845 | #if DISPLAY_WRANGLER_PRESENT |
| 6846 | assert(service == wrangler); |
| 6847 | |
| 6848 | // This function implements half of the user active detection |
| 6849 | // by monitoring changes to the display wrangler's device desire. |
| 6850 | // |
| 6851 | // User becomes active when either: |
| 6852 | // 1. Wrangler's DeviceDesire increases to max, but wrangler is already |
| 6853 | // in max power state. This desire change in absence of a power state |
| 6854 | // change is detected within. This handles the case when user becomes |
| 6855 | // active while the display is already lit by setDisplayPowerOn(). |
| 6856 | // |
| 6857 | // 2. Power state change to max, and DeviceDesire is also at max. |
| 6858 | // Handled by displayWranglerNotification(). |
| 6859 | // |
| 6860 | // User becomes inactive when DeviceDesire drops to sleep state or below. |
| 6861 | |
| 6862 | DLOG("wrangler %s (ps %u, %u->%u)\n" , |
| 6863 | powerClient->getCStringNoCopy(), |
| 6864 | (uint32_t) service->getPowerState(), |
| 6865 | (uint32_t) oldPowerState, (uint32_t) newPowerState); |
| 6866 | |
| 6867 | if (powerClient == gIOPMPowerClientDevice) { |
| 6868 | if ((newPowerState > oldPowerState) && |
| 6869 | (newPowerState == kWranglerPowerStateMax) && |
| 6870 | (service->getPowerState() == kWranglerPowerStateMax)) { |
| 6871 | evaluatePolicy( kStimulusEnterUserActiveState ); |
| 6872 | } else if ((newPowerState < oldPowerState) && |
| 6873 | (newPowerState <= kWranglerPowerStateSleep)) { |
| 6874 | evaluatePolicy( kStimulusLeaveUserActiveState ); |
| 6875 | } |
| 6876 | } |
| 6877 | |
| 6878 | if (newPowerState <= kWranglerPowerStateSleep) { |
| 6879 | evaluatePolicy( kStimulusDisplayWranglerSleep ); |
| 6880 | } else if (newPowerState == kWranglerPowerStateMax) { |
| 6881 | evaluatePolicy( kStimulusDisplayWranglerWake ); |
| 6882 | } |
| 6883 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 6884 | } |
| 6885 | |
| 6886 | //****************************************************************************** |
| 6887 | // User active state management |
| 6888 | //****************************************************************************** |
| 6889 | |
| 6890 | void |
| 6891 | IOPMrootDomain::preventTransitionToUserActive( bool prevent ) |
| 6892 | { |
| 6893 | #if DISPLAY_WRANGLER_PRESENT |
| 6894 | _preventUserActive = prevent; |
| 6895 | if (wrangler && !_preventUserActive) { |
| 6896 | // Allowing transition to user active, but the wrangler may have |
| 6897 | // already powered ON in case of sleep cancel/revert. Poll the |
| 6898 | // same conditions checked for in displayWranglerNotification() |
| 6899 | // to bring the user active state up to date. |
| 6900 | |
| 6901 | if ((wrangler->getPowerState() == kWranglerPowerStateMax) && |
| 6902 | (wrangler->getPowerStateForClient(gIOPMPowerClientDevice) == |
| 6903 | kWranglerPowerStateMax)) { |
| 6904 | evaluatePolicy( kStimulusEnterUserActiveState ); |
| 6905 | } |
| 6906 | } |
| 6907 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 6908 | } |
| 6909 | |
| 6910 | //****************************************************************************** |
| 6911 | // Approve usage of delayed child notification by PM. |
| 6912 | //****************************************************************************** |
| 6913 | |
| 6914 | bool |
| 6915 | IOPMrootDomain::shouldDelayChildNotification( |
| 6916 | IOService * service ) |
| 6917 | { |
| 6918 | if ((kFullWakeReasonNone == fullWakeReason) && |
| 6919 | (kSystemTransitionWake == _systemTransitionType)) { |
| 6920 | DLOG("%s: delay child notify\n" , service->getName()); |
| 6921 | return true; |
| 6922 | } |
| 6923 | return false; |
| 6924 | } |
| 6925 | |
| 6926 | //****************************************************************************** |
| 6927 | // PM actions for PCI device. |
| 6928 | //****************************************************************************** |
| 6929 | |
| 6930 | void |
| 6931 | IOPMrootDomain::handlePowerChangeStartForPCIDevice( |
| 6932 | IOService * service, |
| 6933 | IOPMActions * actions, |
| 6934 | const IOPMRequest * request, |
| 6935 | IOPMPowerStateIndex powerState, |
| 6936 | IOPMPowerChangeFlags * inOutChangeFlags ) |
| 6937 | { |
| 6938 | pmTracer->tracePCIPowerChange( |
| 6939 | PMTraceWorker::kPowerChangeStart, |
| 6940 | service, *inOutChangeFlags, |
| 6941 | (actions->flags & kPMActionsPCIBitNumberMask)); |
| 6942 | } |
| 6943 | |
| 6944 | void |
| 6945 | IOPMrootDomain::handlePowerChangeDoneForPCIDevice( |
| 6946 | IOService * service, |
| 6947 | IOPMActions * actions, |
| 6948 | const IOPMRequest * request, |
| 6949 | IOPMPowerStateIndex powerState, |
| 6950 | IOPMPowerChangeFlags changeFlags ) |
| 6951 | { |
| 6952 | pmTracer->tracePCIPowerChange( |
| 6953 | PMTraceWorker::kPowerChangeCompleted, |
| 6954 | service, changeFlags, |
| 6955 | (actions->flags & kPMActionsPCIBitNumberMask)); |
| 6956 | } |
| 6957 | |
| 6958 | //****************************************************************************** |
| 6959 | // registerInterest |
| 6960 | // |
| 6961 | // Override IOService::registerInterest() for root domain clients. |
| 6962 | //****************************************************************************** |
| 6963 | |
| 6964 | class IOPMServiceInterestNotifier : public _IOServiceInterestNotifier |
| 6965 | { |
| 6966 | friend class IOPMrootDomain; |
| 6967 | OSDeclareDefaultStructors(IOPMServiceInterestNotifier); |
| 6968 | |
| 6969 | protected: |
| 6970 | uint32_t ackTimeoutCnt; |
| 6971 | uint32_t msgType; // Last type seen by the message filter |
| 6972 | uint32_t lastSleepWakeMsgType; |
| 6973 | uint32_t msgIndex; |
| 6974 | uint32_t maxMsgDelayMS; |
| 6975 | uint32_t maxAckDelayMS; |
| 6976 | uint64_t msgAbsTime; |
| 6977 | uint64_t uuid0; |
| 6978 | uint64_t uuid1; |
| 6979 | OSSharedPtr<const OSSymbol> identifier; |
| 6980 | OSSharedPtr<const OSSymbol> clientName; |
| 6981 | }; |
| 6982 | |
| 6983 | OSDefineMetaClassAndStructors(IOPMServiceInterestNotifier, _IOServiceInterestNotifier) |
| 6984 | |
| 6985 | OSSharedPtr<IONotifier> |
| 6986 | IOPMrootDomain::registerInterest( |
| 6987 | const OSSymbol * typeOfInterest, |
| 6988 | IOServiceInterestHandler handler, |
| 6989 | void * target, void * ref ) |
| 6990 | { |
| 6991 | IOPMServiceInterestNotifier* notifier; |
| 6992 | bool isSystemCapabilityClient; |
| 6993 | bool isKernelCapabilityClient; |
| 6994 | IOReturn rc = kIOReturnError; |
| 6995 | |
| 6996 | isSystemCapabilityClient = typeOfInterest && |
| 6997 | typeOfInterest->isEqualTo(kIOPMSystemCapabilityInterest); |
| 6998 | |
| 6999 | isKernelCapabilityClient = typeOfInterest && |
| 7000 | typeOfInterest->isEqualTo(aSymbol: gIOPriorityPowerStateInterest); |
| 7001 | |
| 7002 | if (isSystemCapabilityClient) { |
| 7003 | typeOfInterest = gIOAppPowerStateInterest; |
| 7004 | } |
| 7005 | |
| 7006 | notifier = new IOPMServiceInterestNotifier; |
| 7007 | if (!notifier) { |
| 7008 | return NULL; |
| 7009 | } |
| 7010 | |
| 7011 | if (notifier->init()) { |
| 7012 | rc = super::registerInterestForNotifier(notify: notifier, typeOfInterest, handler, target, ref); |
| 7013 | } |
| 7014 | if (rc != kIOReturnSuccess) { |
| 7015 | OSSafeReleaseNULL(notifier); |
| 7016 | return NULL; |
| 7017 | } |
| 7018 | |
| 7019 | notifier->ackTimeoutCnt = 0; |
| 7020 | |
| 7021 | if (pmPowerStateQueue) { |
| 7022 | if (isSystemCapabilityClient) { |
| 7023 | notifier->retain(); |
| 7024 | if (pmPowerStateQueue->submitPowerEvent( |
| 7025 | eventType: kPowerEventRegisterSystemCapabilityClient, arg0: notifier) == false) { |
| 7026 | notifier->release(); |
| 7027 | } |
| 7028 | } |
| 7029 | |
| 7030 | if (isKernelCapabilityClient) { |
| 7031 | notifier->retain(); |
| 7032 | if (pmPowerStateQueue->submitPowerEvent( |
| 7033 | eventType: kPowerEventRegisterKernelCapabilityClient, arg0: notifier) == false) { |
| 7034 | notifier->release(); |
| 7035 | } |
| 7036 | } |
| 7037 | } |
| 7038 | |
| 7039 | OSSharedPtr<OSData> data; |
| 7040 | uint8_t *uuid = NULL; |
| 7041 | OSSharedPtr<OSKext> kext = OSKext::lookupKextWithAddress(address: (vm_address_t)handler); |
| 7042 | if (kext) { |
| 7043 | data = kext->copyUUID(); |
| 7044 | } |
| 7045 | if (data && (data->getLength() == sizeof(uuid_t))) { |
| 7046 | uuid = (uint8_t *)(data->getBytesNoCopy()); |
| 7047 | |
| 7048 | notifier->uuid0 = ((uint64_t)(uuid[0]) << 56) | ((uint64_t)(uuid[1]) << 48) | ((uint64_t)(uuid[2]) << 40) | |
| 7049 | ((uint64_t)(uuid[3]) << 32) | ((uint64_t)(uuid[4]) << 24) | ((uint64_t)(uuid[5]) << 16) | |
| 7050 | ((uint64_t)(uuid[6]) << 8) | (uuid[7]); |
| 7051 | notifier->uuid1 = ((uint64_t)(uuid[8]) << 56) | ((uint64_t)(uuid[9]) << 48) | ((uint64_t)(uuid[10]) << 40) | |
| 7052 | ((uint64_t)(uuid[11]) << 32) | ((uint64_t)(uuid[12]) << 24) | ((uint64_t)(uuid[13]) << 16) | |
| 7053 | ((uint64_t)(uuid[14]) << 8) | (uuid[15]); |
| 7054 | |
| 7055 | notifier->identifier = copyKextIdentifierWithAddress(address: (vm_address_t) handler); |
| 7056 | } |
| 7057 | return OSSharedPtr<IOPMServiceInterestNotifier>(notifier, OSNoRetain); |
| 7058 | } |
| 7059 | |
| 7060 | //****************************************************************************** |
| 7061 | // systemMessageFilter |
| 7062 | // |
| 7063 | //****************************************************************************** |
| 7064 | |
| 7065 | bool |
| 7066 | IOPMrootDomain::systemMessageFilter( |
| 7067 | void * object, void * arg1, void * arg2, void * arg3 ) |
| 7068 | { |
| 7069 | const IOPMInterestContext * context = (const IOPMInterestContext *) arg1; |
| 7070 | bool isCapMsg = (context->messageType == kIOMessageSystemCapabilityChange); |
| 7071 | bool isCapPowerd = (object == (void *) systemCapabilityNotifier.get()); |
| 7072 | bool isCapClient = false; |
| 7073 | bool allow = false; |
| 7074 | OSBoolean **waitForReply = (typeof(waitForReply))arg3; |
| 7075 | IOPMServiceInterestNotifier *notifier; |
| 7076 | |
| 7077 | notifier = OSDynamicCast(IOPMServiceInterestNotifier, (OSObject *)object); |
| 7078 | |
| 7079 | do { |
| 7080 | // When powerd and kernel priority clients register capability interest, |
| 7081 | // the power tree is sync'ed to inform those clients about the current |
| 7082 | // system capability. Only allow capability change messages during sync. |
| 7083 | if ((kSystemTransitionNewCapClient == _systemTransitionType) && |
| 7084 | (!isCapMsg || !_joinedCapabilityClients || |
| 7085 | !_joinedCapabilityClients->containsObject(anObject: (OSObject *) object))) { |
| 7086 | break; |
| 7087 | } |
| 7088 | |
| 7089 | // Capability change message for powerd and kernel clients |
| 7090 | if (isCapMsg) { |
| 7091 | // Kernel priority clients |
| 7092 | if ((context->notifyType == kNotifyPriority) || |
| 7093 | (context->notifyType == kNotifyCapabilityChangePriority)) { |
| 7094 | isCapClient = true; |
| 7095 | } |
| 7096 | |
| 7097 | // powerd will maintain two client registrations with root domain. |
| 7098 | // isCapPowerd will be TRUE for any message targeting the powerd |
| 7099 | // exclusive (capability change) interest registration. |
| 7100 | if (isCapPowerd && (context->notifyType == kNotifyCapabilityChangeApps)) { |
| 7101 | isCapClient = true; |
| 7102 | } |
| 7103 | } |
| 7104 | |
| 7105 | if (isCapClient) { |
| 7106 | IOPMSystemCapabilityChangeParameters * capArgs = |
| 7107 | (IOPMSystemCapabilityChangeParameters *) arg2; |
| 7108 | |
| 7109 | if (kSystemTransitionNewCapClient == _systemTransitionType) { |
| 7110 | capArgs->fromCapabilities = 0; |
| 7111 | capArgs->toCapabilities = _currentCapability; |
| 7112 | capArgs->changeFlags = 0; |
| 7113 | } else { |
| 7114 | capArgs->fromCapabilities = _currentCapability; |
| 7115 | capArgs->toCapabilities = _pendingCapability; |
| 7116 | |
| 7117 | if (context->isPreChange) { |
| 7118 | capArgs->changeFlags = kIOPMSystemCapabilityWillChange; |
| 7119 | } else { |
| 7120 | capArgs->changeFlags = kIOPMSystemCapabilityDidChange; |
| 7121 | } |
| 7122 | |
| 7123 | if (isCapPowerd && context->isPreChange) { |
| 7124 | toldPowerdCapWillChange = true; |
| 7125 | } |
| 7126 | } |
| 7127 | |
| 7128 | // App level capability change messages must only go to powerd. |
| 7129 | // Wait for response post-change if capabilitiy is increasing. |
| 7130 | // Wait for response pre-change if capability is decreasing. |
| 7131 | |
| 7132 | if ((context->notifyType == kNotifyCapabilityChangeApps) && waitForReply && |
| 7133 | ((capabilityLoss && context->isPreChange) || |
| 7134 | (!capabilityLoss && !context->isPreChange))) { |
| 7135 | *waitForReply = kOSBooleanTrue; |
| 7136 | } |
| 7137 | |
| 7138 | allow = true; |
| 7139 | break; |
| 7140 | } |
| 7141 | |
| 7142 | // powerd will always receive CanSystemSleep, even for a demand sleep. |
| 7143 | // It will also have a final chance to veto sleep after all clients |
| 7144 | // have responded to SystemWillSleep |
| 7145 | |
| 7146 | if ((kIOMessageCanSystemSleep == context->messageType) || |
| 7147 | (kIOMessageSystemWillNotSleep == context->messageType)) { |
| 7148 | if (isCapPowerd) { |
| 7149 | allow = true; |
| 7150 | break; |
| 7151 | } |
| 7152 | |
| 7153 | // Demand sleep, don't ask apps for permission |
| 7154 | if (context->changeFlags & kIOPMSkipAskPowerDown) { |
| 7155 | break; |
| 7156 | } |
| 7157 | } |
| 7158 | |
| 7159 | if (kIOPMMessageLastCallBeforeSleep == context->messageType) { |
| 7160 | if (isCapPowerd && CAP_HIGHEST(kIOPMSystemCapabilityGraphics) && |
| 7161 | (fullToDarkReason == kIOPMSleepReasonIdle)) { |
| 7162 | allow = true; |
| 7163 | } |
| 7164 | break; |
| 7165 | } |
| 7166 | |
| 7167 | // Drop capability change messages for legacy clients. |
| 7168 | // Drop legacy system sleep messages for powerd capability interest. |
| 7169 | if (isCapMsg || isCapPowerd) { |
| 7170 | break; |
| 7171 | } |
| 7172 | |
| 7173 | // Not a capability change message. |
| 7174 | // Perform message filtering based on _systemMessageClientMask. |
| 7175 | |
| 7176 | if ((context->notifyType == kNotifyApps) && |
| 7177 | (_systemMessageClientMask & kSystemMessageClientLegacyApp)) { |
| 7178 | if (!notifier) { |
| 7179 | break; |
| 7180 | } |
| 7181 | |
| 7182 | if ((notifier->lastSleepWakeMsgType == context->messageType) && |
| 7183 | (notifier->lastSleepWakeMsgType == kIOMessageSystemWillPowerOn)) { |
| 7184 | break; // drop any duplicate WillPowerOn for AOT devices |
| 7185 | } |
| 7186 | |
| 7187 | allow = true; |
| 7188 | |
| 7189 | if (waitForReply) { |
| 7190 | if (notifier->ackTimeoutCnt >= 3) { |
| 7191 | *waitForReply = kOSBooleanFalse; |
| 7192 | } else { |
| 7193 | *waitForReply = kOSBooleanTrue; |
| 7194 | } |
| 7195 | } |
| 7196 | } else if ((context->notifyType == kNotifyPriority) && |
| 7197 | (_systemMessageClientMask & kSystemMessageClientKernel)) { |
| 7198 | allow = true; |
| 7199 | } |
| 7200 | |
| 7201 | // Check sleep/wake message ordering |
| 7202 | if (allow) { |
| 7203 | if (context->messageType == kIOMessageSystemWillSleep || |
| 7204 | context->messageType == kIOMessageSystemWillPowerOn || |
| 7205 | context->messageType == kIOMessageSystemHasPoweredOn) { |
| 7206 | notifier->lastSleepWakeMsgType = context->messageType; |
| 7207 | } |
| 7208 | } |
| 7209 | } while (false); |
| 7210 | |
| 7211 | if (allow && isCapMsg && _joinedCapabilityClients) { |
| 7212 | _joinedCapabilityClients->removeObject(anObject: (OSObject *) object); |
| 7213 | if (_joinedCapabilityClients->getCount() == 0) { |
| 7214 | DMSG("destroyed capability client set %p\n" , |
| 7215 | OBFUSCATE(_joinedCapabilityClients.get())); |
| 7216 | _joinedCapabilityClients.reset(); |
| 7217 | } |
| 7218 | } |
| 7219 | if (notifier) { |
| 7220 | // Record the last seen message type even if the message is dropped |
| 7221 | // for traceFilteredNotification(). |
| 7222 | notifier->msgType = context->messageType; |
| 7223 | } |
| 7224 | |
| 7225 | return allow; |
| 7226 | } |
| 7227 | |
| 7228 | //****************************************************************************** |
| 7229 | // setMaintenanceWakeCalendar |
| 7230 | // |
| 7231 | //****************************************************************************** |
| 7232 | |
| 7233 | IOReturn |
| 7234 | IOPMrootDomain::setMaintenanceWakeCalendar( |
| 7235 | const IOPMCalendarStruct * calendar ) |
| 7236 | { |
| 7237 | OSSharedPtr<OSData> data; |
| 7238 | IOReturn ret = 0; |
| 7239 | |
| 7240 | if (!calendar) { |
| 7241 | return kIOReturnBadArgument; |
| 7242 | } |
| 7243 | |
| 7244 | data = OSData::withValue(value: *calendar); |
| 7245 | if (!data) { |
| 7246 | return kIOReturnNoMemory; |
| 7247 | } |
| 7248 | |
| 7249 | if (kPMCalendarTypeMaintenance == calendar->selector) { |
| 7250 | ret = setPMSetting(type: gIOPMSettingMaintenanceWakeCalendarKey.get(), object: data.get()); |
| 7251 | } else if (kPMCalendarTypeSleepService == calendar->selector) { |
| 7252 | ret = setPMSetting(type: gIOPMSettingSleepServiceWakeCalendarKey.get(), object: data.get()); |
| 7253 | } |
| 7254 | |
| 7255 | return ret; |
| 7256 | } |
| 7257 | |
| 7258 | // MARK: - |
| 7259 | // MARK: Display Wrangler |
| 7260 | |
| 7261 | //****************************************************************************** |
| 7262 | // displayWranglerNotification |
| 7263 | // |
| 7264 | // Handle the notification when the IODisplayWrangler changes power state. |
| 7265 | //****************************************************************************** |
| 7266 | |
| 7267 | IOReturn |
| 7268 | IOPMrootDomain::displayWranglerNotification( |
| 7269 | void * target, void * refCon, |
| 7270 | UInt32 messageType, IOService * service, |
| 7271 | void * messageArgument, vm_size_t argSize ) |
| 7272 | { |
| 7273 | #if DISPLAY_WRANGLER_PRESENT |
| 7274 | IOPMPowerStateIndex displayPowerState; |
| 7275 | IOPowerStateChangeNotification * params = |
| 7276 | (IOPowerStateChangeNotification *) messageArgument; |
| 7277 | |
| 7278 | if ((messageType != kIOMessageDeviceWillPowerOff) && |
| 7279 | (messageType != kIOMessageDeviceHasPoweredOn)) { |
| 7280 | return kIOReturnUnsupported; |
| 7281 | } |
| 7282 | |
| 7283 | ASSERT_GATED(); |
| 7284 | if (!gRootDomain) { |
| 7285 | return kIOReturnUnsupported; |
| 7286 | } |
| 7287 | |
| 7288 | displayPowerState = params->stateNumber; |
| 7289 | DLOG("wrangler %s ps %d\n" , |
| 7290 | getIOMessageString(messageType), (uint32_t) displayPowerState); |
| 7291 | |
| 7292 | switch (messageType) { |
| 7293 | case kIOMessageDeviceWillPowerOff: |
| 7294 | // Display wrangler has dropped power due to display idle |
| 7295 | // or force system sleep. |
| 7296 | // |
| 7297 | // 4 Display ON kWranglerPowerStateMax |
| 7298 | // 3 Display Dim kWranglerPowerStateDim |
| 7299 | // 2 Display Sleep kWranglerPowerStateSleep |
| 7300 | // 1 Not visible to user |
| 7301 | // 0 Not visible to user kWranglerPowerStateMin |
| 7302 | |
| 7303 | if (displayPowerState <= kWranglerPowerStateSleep) { |
| 7304 | gRootDomain->evaluatePolicy( kStimulusDisplayWranglerSleep ); |
| 7305 | } |
| 7306 | break; |
| 7307 | |
| 7308 | case kIOMessageDeviceHasPoweredOn: |
| 7309 | // Display wrangler has powered on due to user activity |
| 7310 | // or wake from sleep. |
| 7311 | |
| 7312 | if (kWranglerPowerStateMax == displayPowerState) { |
| 7313 | gRootDomain->evaluatePolicy( kStimulusDisplayWranglerWake ); |
| 7314 | |
| 7315 | // See comment in handleUpdatePowerClientForDisplayWrangler |
| 7316 | if (service->getPowerStateForClient(gIOPMPowerClientDevice) == |
| 7317 | kWranglerPowerStateMax) { |
| 7318 | gRootDomain->evaluatePolicy( kStimulusEnterUserActiveState ); |
| 7319 | } |
| 7320 | } |
| 7321 | break; |
| 7322 | } |
| 7323 | #endif /* DISPLAY_WRANGLER_PRESENT */ |
| 7324 | return kIOReturnUnsupported; |
| 7325 | } |
| 7326 | |
| 7327 | //****************************************************************************** |
| 7328 | // reportUserInput |
| 7329 | // |
| 7330 | //****************************************************************************** |
| 7331 | |
| 7332 | void |
| 7333 | IOPMrootDomain::updateUserActivity( void ) |
| 7334 | { |
| 7335 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 7336 | clock_get_uptime(result: &userActivityTime); |
| 7337 | bool aborting = ((lastSleepReason == kIOPMSleepReasonSoftware) |
| 7338 | || (lastSleepReason == kIOPMSleepReasonIdle) |
| 7339 | || (lastSleepReason == kIOPMSleepReasonMaintenance)); |
| 7340 | if (aborting) { |
| 7341 | userActivityCount++; |
| 7342 | DLOG("user activity reported %d lastSleepReason %d\n" , userActivityCount, lastSleepReason); |
| 7343 | } |
| 7344 | #endif |
| 7345 | } |
| 7346 | void |
| 7347 | IOPMrootDomain::reportUserInput( void ) |
| 7348 | { |
| 7349 | if (wrangler) { |
| 7350 | wrangler->activityTickle(type: 0, stateNumber: 0); |
| 7351 | } |
| 7352 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 7353 | // Update user activity |
| 7354 | updateUserActivity(); |
| 7355 | |
| 7356 | if (!darkWakeExit && ((_pendingCapability & kIOPMSystemCapabilityGraphics) == 0)) { |
| 7357 | // update user active abs time |
| 7358 | clock_get_uptime(result: &gUserActiveAbsTime); |
| 7359 | pmPowerStateQueue->submitPowerEvent( |
| 7360 | eventType: kPowerEventPolicyStimulus, |
| 7361 | arg0: (void *) kStimulusDarkWakeActivityTickle, |
| 7362 | arg1: true /* set wake type */ ); |
| 7363 | } |
| 7364 | #endif |
| 7365 | } |
| 7366 | |
| 7367 | void |
| 7368 | IOPMrootDomain::requestUserActive(IOService *device, const char *reason) |
| 7369 | { |
| 7370 | #if DISPLAY_WRANGLER_PRESENT |
| 7371 | if (wrangler) { |
| 7372 | wrangler->activityTickle(0, 0); |
| 7373 | } |
| 7374 | #else |
| 7375 | if (!device) { |
| 7376 | DLOG("requestUserActive: device is null\n" ); |
| 7377 | return; |
| 7378 | } |
| 7379 | OSSharedPtr<const OSSymbol> deviceName = device->copyName(); |
| 7380 | uint64_t registryID = device->getRegistryEntryID(); |
| 7381 | |
| 7382 | if (!deviceName || !registryID) { |
| 7383 | DLOG("requestUserActive: no device name or registry entry\n" ); |
| 7384 | return; |
| 7385 | } |
| 7386 | const char *name = deviceName->getCStringNoCopy(); |
| 7387 | char payload[128]; |
| 7388 | snprintf(payload, count: sizeof(payload), "%s:%s" , name, reason); |
| 7389 | DLOG("requestUserActive from %s (0x%llx) for %s\n" , name, registryID, reason); |
| 7390 | messageClient(kIOPMMessageRequestUserActive, client: systemCapabilityNotifier.get(), messageArgument: (void *)payload, argSize: sizeof(payload)); |
| 7391 | #endif |
| 7392 | } |
| 7393 | |
| 7394 | //****************************************************************************** |
| 7395 | // latchDisplayWranglerTickle |
| 7396 | //****************************************************************************** |
| 7397 | |
| 7398 | bool |
| 7399 | IOPMrootDomain::latchDisplayWranglerTickle( bool latch ) |
| 7400 | { |
| 7401 | #if DISPLAY_WRANGLER_PRESENT |
| 7402 | if (latch) { |
| 7403 | if (!(_currentCapability & kIOPMSystemCapabilityGraphics) && |
| 7404 | !(_pendingCapability & kIOPMSystemCapabilityGraphics) && |
| 7405 | !checkSystemCanSustainFullWake()) { |
| 7406 | // Currently in dark wake, and not transitioning to full wake. |
| 7407 | // Full wake is unsustainable, so latch the tickle to prevent |
| 7408 | // the display from lighting up momentarily. |
| 7409 | wranglerTickled = true; |
| 7410 | } else { |
| 7411 | wranglerTickled = false; |
| 7412 | } |
| 7413 | } else if (wranglerTickled && checkSystemCanSustainFullWake()) { |
| 7414 | wranglerTickled = false; |
| 7415 | |
| 7416 | pmPowerStateQueue->submitPowerEvent( |
| 7417 | kPowerEventPolicyStimulus, |
| 7418 | (void *) kStimulusDarkWakeActivityTickle ); |
| 7419 | } |
| 7420 | |
| 7421 | return wranglerTickled; |
| 7422 | #else /* ! DISPLAY_WRANGLER_PRESENT */ |
| 7423 | return false; |
| 7424 | #endif /* ! DISPLAY_WRANGLER_PRESENT */ |
| 7425 | } |
| 7426 | |
| 7427 | //****************************************************************************** |
| 7428 | // setDisplayPowerOn |
| 7429 | // |
| 7430 | // For root domain user client |
| 7431 | //****************************************************************************** |
| 7432 | |
| 7433 | void |
| 7434 | IOPMrootDomain::setDisplayPowerOn( uint32_t options ) |
| 7435 | { |
| 7436 | pmPowerStateQueue->submitPowerEvent( eventType: kPowerEventSetDisplayPowerOn, |
| 7437 | arg0: (void *) NULL, arg1: options ); |
| 7438 | } |
| 7439 | |
| 7440 | // MARK: - |
| 7441 | // MARK: System PM Policy |
| 7442 | |
| 7443 | //****************************************************************************** |
| 7444 | // checkSystemSleepAllowed |
| 7445 | // |
| 7446 | //****************************************************************************** |
| 7447 | |
| 7448 | bool |
| 7449 | IOPMrootDomain::checkSystemSleepAllowed( IOOptionBits options, |
| 7450 | uint32_t sleepReason ) |
| 7451 | { |
| 7452 | uint32_t err = 0; |
| 7453 | |
| 7454 | // Conditions that prevent idle and demand system sleep. |
| 7455 | |
| 7456 | do { |
| 7457 | if (gSleepDisabledFlag) { |
| 7458 | err = kPMConfigPreventSystemSleep; |
| 7459 | break; |
| 7460 | } |
| 7461 | |
| 7462 | if (userDisabledAllSleep) { |
| 7463 | err = kPMUserDisabledAllSleep; // 1. user-space sleep kill switch |
| 7464 | break; |
| 7465 | } |
| 7466 | |
| 7467 | if (systemBooting || systemShutdown || gWillShutdown) { |
| 7468 | err = kPMSystemRestartBootingInProgress; // 2. restart or shutdown in progress |
| 7469 | break; |
| 7470 | } |
| 7471 | |
| 7472 | if (options == 0) { |
| 7473 | break; |
| 7474 | } |
| 7475 | |
| 7476 | // Conditions above pegs the system at full wake. |
| 7477 | // Conditions below prevent system sleep but does not prevent |
| 7478 | // dark wake, and must be called from gated context. |
| 7479 | |
| 7480 | #if !CONFIG_SLEEP |
| 7481 | err = kPMConfigPreventSystemSleep; // 3. config does not support sleep |
| 7482 | break; |
| 7483 | #endif |
| 7484 | |
| 7485 | if (lowBatteryCondition || thermalWarningState || thermalEmergencyState) { |
| 7486 | break; // always sleep on low battery or when in thermal warning/emergency state |
| 7487 | } |
| 7488 | |
| 7489 | if (sleepReason == kIOPMSleepReasonDarkWakeThermalEmergency) { |
| 7490 | break; // always sleep on dark wake thermal emergencies |
| 7491 | } |
| 7492 | |
| 7493 | if (preventSystemSleepList->getCount() != 0) { |
| 7494 | err = kPMChildPreventSystemSleep; // 4. child prevent system sleep clamp |
| 7495 | break; |
| 7496 | } |
| 7497 | |
| 7498 | if (_driverKitMatchingAssertionCount != 0) { |
| 7499 | err = kPMCPUAssertion; |
| 7500 | break; |
| 7501 | } |
| 7502 | |
| 7503 | // Check for any dexts currently being added to the PM tree. Sleeping while |
| 7504 | // this is in flight can cause IOServicePH to timeout. |
| 7505 | if (!IOServicePH::checkPMReady()) { |
| 7506 | #if !defined(XNU_TARGET_OS_OSX) |
| 7507 | // 116893363: kPMDKNotReady sleep cancellations often leaves embedded devices |
| 7508 | // in dark wake for long periods of time, which causes issues as apps were |
| 7509 | // already informed of sleep during the f->9 transition. As a temporary |
| 7510 | // measure, always full wake if we hit this specific condition. |
| 7511 | pmPowerStateQueue->submitPowerEvent( |
| 7512 | kPowerEventPolicyStimulus, |
| 7513 | (void *) kStimulusDarkWakeActivityTickle); |
| 7514 | #endif |
| 7515 | err = kPMDKNotReady; |
| 7516 | break; |
| 7517 | } |
| 7518 | |
| 7519 | if (getPMAssertionLevel( whichAssertionBits: kIOPMDriverAssertionCPUBit ) == |
| 7520 | kIOPMDriverAssertionLevelOn) { |
| 7521 | err = kPMCPUAssertion; // 5. CPU assertion |
| 7522 | break; |
| 7523 | } |
| 7524 | |
| 7525 | if (pciCantSleepValid) { |
| 7526 | if (pciCantSleepFlag) { |
| 7527 | err = kPMPCIUnsupported; // 6. PCI card does not support PM (cached) |
| 7528 | } |
| 7529 | break; |
| 7530 | } else if (sleepSupportedPEFunction && |
| 7531 | CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) { |
| 7532 | IOReturn ret; |
| 7533 | OSBitAndAtomic(~kPCICantSleep, &platformSleepSupport); |
| 7534 | ret = getPlatform()->callPlatformFunction( |
| 7535 | functionName: sleepSupportedPEFunction.get(), waitForFunction: false, |
| 7536 | NULL, NULL, NULL, NULL); |
| 7537 | pciCantSleepValid = true; |
| 7538 | pciCantSleepFlag = false; |
| 7539 | if ((platformSleepSupport & kPCICantSleep) || |
| 7540 | ((ret != kIOReturnSuccess) && (ret != kIOReturnUnsupported))) { |
| 7541 | err = 6; // 6. PCI card does not support PM |
| 7542 | pciCantSleepFlag = true; |
| 7543 | break; |
| 7544 | } |
| 7545 | } |
| 7546 | }while (false); |
| 7547 | |
| 7548 | if (err) { |
| 7549 | DLOG("System sleep prevented by %s\n" , getSystemSleepPreventerString(err)); |
| 7550 | return false; |
| 7551 | } |
| 7552 | return true; |
| 7553 | } |
| 7554 | |
| 7555 | bool |
| 7556 | IOPMrootDomain::checkSystemSleepEnabled( void ) |
| 7557 | { |
| 7558 | return checkSystemSleepAllowed(options: 0, sleepReason: 0); |
| 7559 | } |
| 7560 | |
| 7561 | bool |
| 7562 | IOPMrootDomain::checkSystemCanSleep( uint32_t sleepReason ) |
| 7563 | { |
| 7564 | ASSERT_GATED(); |
| 7565 | return checkSystemSleepAllowed(options: 1, sleepReason); |
| 7566 | } |
| 7567 | |
| 7568 | //****************************************************************************** |
| 7569 | // checkSystemCanSustainFullWake |
| 7570 | //****************************************************************************** |
| 7571 | |
| 7572 | bool |
| 7573 | IOPMrootDomain::checkSystemCanSustainFullWake( void ) |
| 7574 | { |
| 7575 | if (lowBatteryCondition || thermalWarningState || thermalEmergencyState) { |
| 7576 | // Low battery wake, or received a low battery notification |
| 7577 | // while system is awake. This condition will persist until |
| 7578 | // the following wake. |
| 7579 | return false; |
| 7580 | } |
| 7581 | |
| 7582 | if (clamshellExists && clamshellClosed && !clamshellSleepDisableMask) { |
| 7583 | // Graphics state is unknown and external display might not be probed. |
| 7584 | // Do not incorporate state that requires graphics to be in max power |
| 7585 | // such as desktopMode or clamshellDisabled. |
| 7586 | |
| 7587 | if (!acAdaptorConnected) { |
| 7588 | DLOG("full wake check: no AC\n" ); |
| 7589 | return false; |
| 7590 | } |
| 7591 | } |
| 7592 | return true; |
| 7593 | } |
| 7594 | |
| 7595 | //****************************************************************************** |
| 7596 | // mustHibernate |
| 7597 | //****************************************************************************** |
| 7598 | |
| 7599 | #if HIBERNATION |
| 7600 | |
| 7601 | bool |
| 7602 | IOPMrootDomain::mustHibernate( void ) |
| 7603 | { |
| 7604 | return lowBatteryCondition || thermalWarningState; |
| 7605 | } |
| 7606 | |
| 7607 | #endif /* HIBERNATION */ |
| 7608 | |
| 7609 | //****************************************************************************** |
| 7610 | // AOT |
| 7611 | //****************************************************************************** |
| 7612 | |
| 7613 | // Tables for accumulated days in year by month, latter used for leap years |
| 7614 | |
| 7615 | static const unsigned int daysbymonth[] = |
| 7616 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; |
| 7617 | |
| 7618 | static const unsigned int lydaysbymonth[] = |
| 7619 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; |
| 7620 | |
| 7621 | static int __unused |
| 7622 | IOPMConvertSecondsToCalendar(clock_sec_t secs, IOPMCalendarStruct * dt) |
| 7623 | { |
| 7624 | const unsigned int * dbm = daysbymonth; |
| 7625 | clock_sec_t n, x, y, z; |
| 7626 | |
| 7627 | // Calculate seconds, minutes and hours |
| 7628 | |
| 7629 | n = secs % (24 * 3600); |
| 7630 | dt->second = n % 60; |
| 7631 | n /= 60; |
| 7632 | dt->minute = n % 60; |
| 7633 | dt->hour = (typeof(dt->hour))(n / 60); |
| 7634 | |
| 7635 | // Calculate day of week |
| 7636 | |
| 7637 | n = secs / (24 * 3600); |
| 7638 | // dt->dayWeek = (n + 4) % 7; |
| 7639 | |
| 7640 | // Calculate year |
| 7641 | // Rebase from days since Unix epoch (1/1/1970) store in 'n', |
| 7642 | // to days since 1/1/1968 to start on 4 year cycle, beginning |
| 7643 | // on a leap year. |
| 7644 | |
| 7645 | n += (366 + 365); |
| 7646 | |
| 7647 | // Every 4 year cycle will be exactly (366 + 365 * 3) = 1461 days. |
| 7648 | // Valid before 2100, since 2100 is not a leap year. |
| 7649 | |
| 7650 | x = n / 1461; // number of 4 year cycles |
| 7651 | y = n % 1461; // days into current 4 year cycle |
| 7652 | z = 1968 + (4 * x); |
| 7653 | |
| 7654 | // Add in years in the current 4 year cycle |
| 7655 | |
| 7656 | if (y >= 366) { |
| 7657 | y -= 366; // days after the leap year |
| 7658 | n = y % 365; // days into the current year |
| 7659 | z += (1 + y / 365); // years after the past 4-yr cycle |
| 7660 | } else { |
| 7661 | n = y; |
| 7662 | dbm = lydaysbymonth; |
| 7663 | } |
| 7664 | if (z > 2099) { |
| 7665 | return 0; |
| 7666 | } |
| 7667 | |
| 7668 | dt->year = (typeof(dt->year))z; |
| 7669 | |
| 7670 | // Adjust remaining days value to start at 1 |
| 7671 | |
| 7672 | n += 1; |
| 7673 | |
| 7674 | // Calculate month |
| 7675 | |
| 7676 | for (x = 1; (n > dbm[x]) && (x < 12); x++) { |
| 7677 | continue; |
| 7678 | } |
| 7679 | dt->month = (typeof(dt->month))x; |
| 7680 | |
| 7681 | // Calculate day of month |
| 7682 | |
| 7683 | dt->day = (typeof(dt->day))(n - dbm[x - 1]); |
| 7684 | |
| 7685 | return 1; |
| 7686 | } |
| 7687 | |
| 7688 | static clock_sec_t |
| 7689 | IOPMConvertCalendarToSeconds(const IOPMCalendarStruct * dt) |
| 7690 | { |
| 7691 | const unsigned int * dbm = daysbymonth; |
| 7692 | long y, secs, days; |
| 7693 | |
| 7694 | if (dt->year < 1970 || dt->month > 12) { |
| 7695 | return 0; |
| 7696 | } |
| 7697 | |
| 7698 | // Seconds elapsed in the current day |
| 7699 | |
| 7700 | secs = dt->second + 60 * dt->minute + 3600 * dt->hour; |
| 7701 | |
| 7702 | // Number of days from 1/1/70 to beginning of current year |
| 7703 | // Account for extra day every 4 years starting at 1973 |
| 7704 | |
| 7705 | y = dt->year - 1970; |
| 7706 | days = (y * 365) + ((y + 1) / 4); |
| 7707 | |
| 7708 | // Change table if current year is a leap year |
| 7709 | |
| 7710 | if ((dt->year % 4) == 0) { |
| 7711 | dbm = lydaysbymonth; |
| 7712 | } |
| 7713 | |
| 7714 | // Add in days elapsed in the current year |
| 7715 | |
| 7716 | days += (dt->day - 1) + dbm[dt->month - 1]; |
| 7717 | |
| 7718 | // Add accumulated days to accumulated seconds |
| 7719 | |
| 7720 | secs += 24 * 3600 * days; |
| 7721 | |
| 7722 | return secs; |
| 7723 | } |
| 7724 | |
| 7725 | unsigned long |
| 7726 | IOPMrootDomain::getRUN_STATE(void) |
| 7727 | { |
| 7728 | return (_aotNow && !(kIOPMWakeEventAOTExitFlags & _aotPendingFlags)) ? AOT_STATE : ON_STATE; |
| 7729 | } |
| 7730 | |
| 7731 | bool |
| 7732 | IOPMrootDomain::isAOTMode() |
| 7733 | { |
| 7734 | return _aotNow; |
| 7735 | } |
| 7736 | |
| 7737 | IOReturn |
| 7738 | IOPMrootDomain::setWakeTime(uint64_t wakeContinuousTime) |
| 7739 | { |
| 7740 | clock_sec_t nowsecs, wakesecs; |
| 7741 | clock_usec_t nowmicrosecs, wakemicrosecs; |
| 7742 | uint64_t nowAbs, wakeAbs; |
| 7743 | |
| 7744 | if (!_aotMode) { |
| 7745 | return kIOReturnNotReady; |
| 7746 | } |
| 7747 | |
| 7748 | clock_gettimeofday_and_absolute_time(secs: &nowsecs, microsecs: &nowmicrosecs, absolute_time: &nowAbs); |
| 7749 | wakeAbs = continuoustime_to_absolutetime(conttime: wakeContinuousTime); |
| 7750 | if (wakeAbs < nowAbs) { |
| 7751 | printf(LOG_PREFIX "wakeAbs %qd < nowAbs %qd\n" , wakeAbs, nowAbs); |
| 7752 | wakeAbs = nowAbs; |
| 7753 | } |
| 7754 | wakeAbs -= nowAbs; |
| 7755 | absolutetime_to_microtime(abstime: wakeAbs, secs: &wakesecs, microsecs: &wakemicrosecs); |
| 7756 | |
| 7757 | wakesecs += nowsecs; |
| 7758 | wakemicrosecs += nowmicrosecs; |
| 7759 | if (wakemicrosecs >= USEC_PER_SEC) { |
| 7760 | wakesecs++; |
| 7761 | wakemicrosecs -= USEC_PER_SEC; |
| 7762 | } |
| 7763 | if (wakemicrosecs >= (USEC_PER_SEC / 10)) { |
| 7764 | wakesecs++; |
| 7765 | } |
| 7766 | |
| 7767 | IOPMConvertSecondsToCalendar(secs: wakesecs, dt: &_aotWakeTimeCalendar); |
| 7768 | |
| 7769 | if (_aotWakeTimeContinuous != wakeContinuousTime) { |
| 7770 | _aotWakeTimeContinuous = wakeContinuousTime; |
| 7771 | IOLog(LOG_PREFIX "setWakeTime: " YMDTF "\n" , YMDT(&_aotWakeTimeCalendar)); |
| 7772 | } |
| 7773 | _aotWakeTimeCalendar.selector = kPMCalendarTypeMaintenance; |
| 7774 | _aotWakeTimeUTC = wakesecs; |
| 7775 | |
| 7776 | return kIOReturnSuccess; |
| 7777 | } |
| 7778 | |
| 7779 | // assumes WAKEEVENT_LOCK |
| 7780 | bool |
| 7781 | IOPMrootDomain::aotShouldExit(bool checkTimeSet, bool software) |
| 7782 | { |
| 7783 | bool exitNow = false; |
| 7784 | const char * reason = "" ; |
| 7785 | |
| 7786 | if (!_aotNow) { |
| 7787 | return false; |
| 7788 | } |
| 7789 | |
| 7790 | if (software) { |
| 7791 | exitNow = true; |
| 7792 | _aotMetrics->softwareRequestCount++; |
| 7793 | reason = "software request" ; |
| 7794 | } else if (kIOPMWakeEventAOTExitFlags & _aotPendingFlags) { |
| 7795 | exitNow = true; |
| 7796 | reason = gWakeReasonString; |
| 7797 | } else if (checkTimeSet && (kPMCalendarTypeInvalid == _aotWakeTimeCalendar.selector)) { |
| 7798 | exitNow = true; |
| 7799 | _aotMetrics->noTimeSetCount++; |
| 7800 | reason = "flipbook expired" ; |
| 7801 | } else if ((kIOPMAOTModeRespectTimers & _aotMode) && _calendarWakeAlarmUTC) { |
| 7802 | clock_sec_t sec; |
| 7803 | clock_usec_t usec; |
| 7804 | clock_get_calendar_microtime(secs: &sec, microsecs: &usec); |
| 7805 | if (_calendarWakeAlarmUTC <= sec) { |
| 7806 | exitNow = true; |
| 7807 | _aotMetrics->rtcAlarmsCount++; |
| 7808 | reason = "user alarm" ; |
| 7809 | } |
| 7810 | } |
| 7811 | if (exitNow) { |
| 7812 | _aotPendingFlags |= kIOPMWakeEventAOTExit; |
| 7813 | IOLog(LOG_PREFIX "AOT exit for %s, sc %d po %d, cp %d, rj %d, ex %d, nt %d, rt %d\n" , |
| 7814 | reason, |
| 7815 | _aotMetrics->sleepCount, |
| 7816 | _aotMetrics->possibleCount, |
| 7817 | _aotMetrics->confirmedPossibleCount, |
| 7818 | _aotMetrics->rejectedPossibleCount, |
| 7819 | _aotMetrics->expiredPossibleCount, |
| 7820 | _aotMetrics->noTimeSetCount, |
| 7821 | _aotMetrics->rtcAlarmsCount); |
| 7822 | } |
| 7823 | return exitNow; |
| 7824 | } |
| 7825 | |
| 7826 | void |
| 7827 | IOPMrootDomain::aotExit(bool cps) |
| 7828 | { |
| 7829 | uint32_t savedMessageMask; |
| 7830 | |
| 7831 | ASSERT_GATED(); |
| 7832 | _aotNow = false; |
| 7833 | _aotReadyToFullWake = false; |
| 7834 | if (_aotTimerScheduled) { |
| 7835 | _aotTimerES->cancelTimeout(); |
| 7836 | _aotTimerScheduled = false; |
| 7837 | } |
| 7838 | updateTasksSuspend(newTasksSuspended: kTasksSuspendNoChange, newAOTTasksSuspended: kTasksSuspendUnsuspended); |
| 7839 | |
| 7840 | _aotMetrics->totalTime += mach_absolute_time() - _aotLastWakeTime; |
| 7841 | _aotLastWakeTime = 0; |
| 7842 | if (_aotMetrics->sleepCount && (_aotMetrics->sleepCount <= kIOPMAOTMetricsKernelWakeCountMax)) { |
| 7843 | WAKEEVENT_LOCK(); |
| 7844 | strlcpy(dst: &_aotMetrics->kernelWakeReason[_aotMetrics->sleepCount - 1][0], |
| 7845 | src: gWakeReasonString, |
| 7846 | n: sizeof(_aotMetrics->kernelWakeReason[_aotMetrics->sleepCount])); |
| 7847 | WAKEEVENT_UNLOCK(); |
| 7848 | } |
| 7849 | |
| 7850 | _aotWakeTimeCalendar.selector = kPMCalendarTypeInvalid; |
| 7851 | |
| 7852 | // Preserve the message mask since a system wake transition |
| 7853 | // may have already started and initialized the mask. |
| 7854 | savedMessageMask = _systemMessageClientMask; |
| 7855 | _systemMessageClientMask = kSystemMessageClientLegacyApp; |
| 7856 | tellClients(kIOMessageSystemWillPowerOn); |
| 7857 | _systemMessageClientMask = savedMessageMask | kSystemMessageClientLegacyApp; |
| 7858 | |
| 7859 | if (cps) { |
| 7860 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonAOTExit); |
| 7861 | } |
| 7862 | } |
| 7863 | |
| 7864 | void |
| 7865 | IOPMrootDomain::aotEvaluate(IOTimerEventSource * timer) |
| 7866 | { |
| 7867 | bool exitNow; |
| 7868 | |
| 7869 | IOLog(format: "aotEvaluate(%d) 0x%x\n" , (timer != NULL), _aotPendingFlags); |
| 7870 | |
| 7871 | WAKEEVENT_LOCK(); |
| 7872 | exitNow = aotShouldExit(checkTimeSet: false, software: false); |
| 7873 | if (timer != NULL) { |
| 7874 | _aotTimerScheduled = false; |
| 7875 | } |
| 7876 | WAKEEVENT_UNLOCK(); |
| 7877 | if (exitNow) { |
| 7878 | aotExit(cps: true); |
| 7879 | } else { |
| 7880 | #if 0 |
| 7881 | if (_aotLingerTime) { |
| 7882 | uint64_t deadline; |
| 7883 | IOLog("aot linger before sleep\n" ); |
| 7884 | clock_absolutetime_interval_to_deadline(_aotLingerTime, &deadline); |
| 7885 | clock_delay_until(deadline); |
| 7886 | } |
| 7887 | #endif |
| 7888 | privateSleepSystem(sleepReason: kIOPMSleepReasonSoftware); |
| 7889 | } |
| 7890 | } |
| 7891 | |
| 7892 | //****************************************************************************** |
| 7893 | // adjustPowerState |
| 7894 | // |
| 7895 | // Conditions that affect our wake/sleep decision has changed. |
| 7896 | // If conditions dictate that the system must remain awake, clamp power |
| 7897 | // state to max with changePowerStateToPriv(ON). Otherwise if sleepASAP |
| 7898 | // is TRUE, then remove the power clamp and allow the power state to drop |
| 7899 | // to SLEEP_STATE. |
| 7900 | //****************************************************************************** |
| 7901 | |
| 7902 | void |
| 7903 | IOPMrootDomain::adjustPowerState( bool sleepASAP ) |
| 7904 | { |
| 7905 | DEBUG_LOG("adjustPowerState %s, asap %d, idleSleepEnabled %d\n" , |
| 7906 | getPowerStateString((uint32_t) getPowerState()), sleepASAP, idleSleepEnabled); |
| 7907 | |
| 7908 | ASSERT_GATED(); |
| 7909 | |
| 7910 | if (_aotNow) { |
| 7911 | bool exitNow; |
| 7912 | |
| 7913 | if (AOT_STATE != getPowerState()) { |
| 7914 | return; |
| 7915 | } |
| 7916 | WAKEEVENT_LOCK(); |
| 7917 | exitNow = aotShouldExit(checkTimeSet: true, software: false); |
| 7918 | if (!exitNow |
| 7919 | && !_aotTimerScheduled |
| 7920 | && (kIOPMWakeEventAOTPossibleExit == (kIOPMWakeEventAOTPossibleFlags & _aotPendingFlags))) { |
| 7921 | _aotTimerScheduled = true; |
| 7922 | if (_aotLingerTime) { |
| 7923 | _aotTimerES->setTimeout(_aotLingerTime); |
| 7924 | } else { |
| 7925 | _aotTimerES->setTimeout(interval: 800, scale_factor: kMillisecondScale); |
| 7926 | } |
| 7927 | } |
| 7928 | WAKEEVENT_UNLOCK(); |
| 7929 | if (exitNow) { |
| 7930 | aotExit(cps: true); |
| 7931 | } else { |
| 7932 | _aotReadyToFullWake = true; |
| 7933 | if (!_aotTimerScheduled) { |
| 7934 | if (kIOPMDriverAssertionLevelOn == getPMAssertionLevel(whichAssertionBits: kIOPMDriverAssertionCPUBit)) { |
| 7935 | // Don't try to force sleep during AOT while IOMobileFramebuffer is holding a power assertion. |
| 7936 | // Doing so will result in the sleep being cancelled anyway, |
| 7937 | // but this check avoids unnecessary thrashing in the power state engine. |
| 7938 | return; |
| 7939 | } |
| 7940 | privateSleepSystem(sleepReason: kIOPMSleepReasonSoftware); |
| 7941 | } |
| 7942 | } |
| 7943 | return; |
| 7944 | } |
| 7945 | |
| 7946 | if ((!idleSleepEnabled) || !checkSystemSleepEnabled()) { |
| 7947 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonAdjustPowerState); |
| 7948 | } else if (sleepASAP) { |
| 7949 | changePowerStateWithTagToPriv(ordinal: SLEEP_STATE, reason: kCPSReasonAdjustPowerState); |
| 7950 | } |
| 7951 | } |
| 7952 | |
| 7953 | void |
| 7954 | IOPMrootDomain::handleSetDisplayPowerOn(bool powerOn) |
| 7955 | { |
| 7956 | if (powerOn) { |
| 7957 | if (!checkSystemCanSustainFullWake()) { |
| 7958 | DLOG("System cannot sustain full wake\n" ); |
| 7959 | return; |
| 7960 | } |
| 7961 | |
| 7962 | // Force wrangler to max power state. If system is in dark wake |
| 7963 | // this alone won't raise the wrangler's power state. |
| 7964 | if (wrangler) { |
| 7965 | wrangler->changePowerStateForRootDomain(ordinal: kWranglerPowerStateMax); |
| 7966 | } |
| 7967 | |
| 7968 | // System in dark wake, always requesting full wake should |
| 7969 | // not have any bad side-effects, even if the request fails. |
| 7970 | |
| 7971 | if (!CAP_CURRENT(kIOPMSystemCapabilityGraphics)) { |
| 7972 | setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeNotification); |
| 7973 | requestFullWake( reason: kFullWakeReasonDisplayOn ); |
| 7974 | } |
| 7975 | } else { |
| 7976 | // Relenquish desire to power up display. |
| 7977 | // Must first transition to state 1 since wrangler doesn't |
| 7978 | // power off the displays at state 0. At state 0 the root |
| 7979 | // domain is removed from the wrangler's power client list. |
| 7980 | if (wrangler) { |
| 7981 | wrangler->changePowerStateForRootDomain(ordinal: kWranglerPowerStateMin + 1); |
| 7982 | wrangler->changePowerStateForRootDomain(ordinal: kWranglerPowerStateMin); |
| 7983 | } |
| 7984 | } |
| 7985 | } |
| 7986 | |
| 7987 | //****************************************************************************** |
| 7988 | // dispatchPowerEvent |
| 7989 | // |
| 7990 | // IOPMPowerStateQueue callback function. Running on PM work loop thread. |
| 7991 | //****************************************************************************** |
| 7992 | |
| 7993 | void |
| 7994 | IOPMrootDomain::dispatchPowerEvent( |
| 7995 | uint32_t event, void * arg0, uint64_t arg1 ) |
| 7996 | { |
| 7997 | ASSERT_GATED(); |
| 7998 | |
| 7999 | switch (event) { |
| 8000 | case kPowerEventFeatureChanged: |
| 8001 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8002 | messageClients(kIOPMMessageFeatureChange, argument: this); |
| 8003 | break; |
| 8004 | |
| 8005 | case kPowerEventReceivedPowerNotification: |
| 8006 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8007 | handlePowerNotification(msg: (UInt32)(uintptr_t) arg0 ); |
| 8008 | break; |
| 8009 | |
| 8010 | case kPowerEventSystemBootCompleted: |
| 8011 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8012 | if (systemBooting) { |
| 8013 | systemBooting = false; |
| 8014 | |
| 8015 | if (PE_get_default(property_name: "sleep-disabled" , property_ptr: &gSleepDisabledFlag, max_property: sizeof(gSleepDisabledFlag))) { |
| 8016 | DLOG("Setting gSleepDisabledFlag to %u from device tree\n" , gSleepDisabledFlag); |
| 8017 | } |
| 8018 | if (lowBatteryCondition || thermalEmergencyState) { |
| 8019 | if (lowBatteryCondition) { |
| 8020 | privateSleepSystem(sleepReason: kIOPMSleepReasonLowPower); |
| 8021 | } else { |
| 8022 | privateSleepSystem(sleepReason: kIOPMSleepReasonThermalEmergency); |
| 8023 | } |
| 8024 | // The rest is unnecessary since the system is expected |
| 8025 | // to sleep immediately. The following wake will update |
| 8026 | // everything. |
| 8027 | break; |
| 8028 | } |
| 8029 | |
| 8030 | sleepWakeDebugMemAlloc(); |
| 8031 | saveFailureData2File(); |
| 8032 | |
| 8033 | // If lid is closed, re-send lid closed notification |
| 8034 | // now that booting is complete. |
| 8035 | if (clamshellClosed) { |
| 8036 | handlePowerNotification(kLocalEvalClamshellCommand); |
| 8037 | } |
| 8038 | evaluatePolicy( stimulus: kStimulusAllowSystemSleepChanged ); |
| 8039 | } |
| 8040 | break; |
| 8041 | |
| 8042 | case kPowerEventSystemShutdown: |
| 8043 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8044 | if (kOSBooleanTrue == (OSBoolean *) arg0) { |
| 8045 | /* We set systemShutdown = true during shutdown |
| 8046 | * to prevent sleep at unexpected times while loginwindow is trying |
| 8047 | * to shutdown apps and while the OS is trying to transition to |
| 8048 | * complete power of. |
| 8049 | * |
| 8050 | * Set to true during shutdown, as soon as loginwindow shows |
| 8051 | * the "shutdown countdown dialog", through individual app |
| 8052 | * termination, and through black screen kernel shutdown. |
| 8053 | */ |
| 8054 | systemShutdown = true; |
| 8055 | } else { |
| 8056 | /* |
| 8057 | * A shutdown was initiated, but then the shutdown |
| 8058 | * was cancelled, clearing systemShutdown to false here. |
| 8059 | */ |
| 8060 | systemShutdown = false; |
| 8061 | } |
| 8062 | break; |
| 8063 | |
| 8064 | case kPowerEventUserDisabledSleep: |
| 8065 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8066 | userDisabledAllSleep = (kOSBooleanTrue == (OSBoolean *) arg0); |
| 8067 | break; |
| 8068 | |
| 8069 | case kPowerEventRegisterSystemCapabilityClient: |
| 8070 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8071 | |
| 8072 | // reset() handles the arg0 == nullptr case for us |
| 8073 | systemCapabilityNotifier.reset(p: (IONotifier *) arg0, OSRetain); |
| 8074 | /* intentional fall-through */ |
| 8075 | [[clang::fallthrough]]; |
| 8076 | |
| 8077 | case kPowerEventRegisterKernelCapabilityClient: |
| 8078 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8079 | if (!_joinedCapabilityClients) { |
| 8080 | _joinedCapabilityClients = OSSet::withCapacity(capacity: 8); |
| 8081 | } |
| 8082 | if (arg0) { |
| 8083 | OSSharedPtr<IONotifier> notify((IONotifier *) arg0, OSNoRetain); |
| 8084 | if (_joinedCapabilityClients) { |
| 8085 | _joinedCapabilityClients->setObject(notify.get()); |
| 8086 | synchronizePowerTree( kIOPMSyncNoChildNotify ); |
| 8087 | } |
| 8088 | } |
| 8089 | break; |
| 8090 | |
| 8091 | case kPowerEventPolicyStimulus: |
| 8092 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8093 | if (arg0) { |
| 8094 | int stimulus = (int)(uintptr_t) arg0; |
| 8095 | evaluatePolicy(stimulus, arg: (uint32_t) arg1); |
| 8096 | } |
| 8097 | break; |
| 8098 | |
| 8099 | case kPowerEventAssertionCreate: |
| 8100 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8101 | if (pmAssertions) { |
| 8102 | pmAssertions->handleCreateAssertion((OSValueObject<PMAssertStruct> *)arg0); |
| 8103 | } |
| 8104 | break; |
| 8105 | |
| 8106 | |
| 8107 | case kPowerEventAssertionRelease: |
| 8108 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8109 | if (pmAssertions) { |
| 8110 | pmAssertions->handleReleaseAssertion(arg1); |
| 8111 | } |
| 8112 | break; |
| 8113 | |
| 8114 | case kPowerEventAssertionSetLevel: |
| 8115 | DMSG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8116 | if (pmAssertions) { |
| 8117 | pmAssertions->handleSetAssertionLevel(arg1, (IOPMDriverAssertionLevel)(uintptr_t)arg0); |
| 8118 | } |
| 8119 | break; |
| 8120 | |
| 8121 | case kPowerEventQueueSleepWakeUUID: |
| 8122 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8123 | handleQueueSleepWakeUUID(obj: (OSObject *)arg0); |
| 8124 | break; |
| 8125 | case kPowerEventPublishSleepWakeUUID: |
| 8126 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8127 | handlePublishSleepWakeUUID(shouldPublish: (bool)arg0); |
| 8128 | break; |
| 8129 | |
| 8130 | case kPowerEventSetDisplayPowerOn: |
| 8131 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8132 | if (arg1 != 0) { |
| 8133 | displayPowerOnRequested = true; |
| 8134 | } else { |
| 8135 | displayPowerOnRequested = false; |
| 8136 | } |
| 8137 | handleSetDisplayPowerOn(powerOn: displayPowerOnRequested); |
| 8138 | break; |
| 8139 | |
| 8140 | case kPowerEventPublishWakeType: |
| 8141 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8142 | |
| 8143 | // Don't replace wake type property if already set |
| 8144 | if ((arg0 == gIOPMWakeTypeUserKey) || |
| 8145 | !propertyExists(kIOPMRootDomainWakeTypeKey)) { |
| 8146 | const char * wakeType = NULL; |
| 8147 | |
| 8148 | if (arg0 == gIOPMWakeTypeUserKey) { |
| 8149 | requestUserActive(device: this, reason: "WakeTypeUser" ); |
| 8150 | wakeType = kIOPMRootDomainWakeTypeUser; |
| 8151 | } else if (arg0 == gIOPMSettingDebugWakeRelativeKey) { |
| 8152 | if (!(gDarkWakeFlags & kDarkWakeFlagAlarmIsDark)) { |
| 8153 | requestUserActive(device: this, reason: "WakeTypeAlarm" ); |
| 8154 | } |
| 8155 | wakeType = kIOPMRootDomainWakeTypeAlarm; |
| 8156 | } else if (arg0 == gIOPMSettingSleepServiceWakeCalendarKey) { |
| 8157 | darkWakeSleepService = true; |
| 8158 | wakeType = kIOPMRootDomainWakeTypeSleepService; |
| 8159 | } else if (arg0 == gIOPMSettingMaintenanceWakeCalendarKey) { |
| 8160 | wakeType = kIOPMRootDomainWakeTypeMaintenance; |
| 8161 | } |
| 8162 | |
| 8163 | if (wakeType) { |
| 8164 | setProperty(kIOPMRootDomainWakeTypeKey, aString: wakeType); |
| 8165 | } |
| 8166 | } |
| 8167 | break; |
| 8168 | |
| 8169 | case kPowerEventAOTEvaluate: |
| 8170 | DLOG("power event %u args %p 0x%llx\n" , event, OBFUSCATE(arg0), arg1); |
| 8171 | if (_aotReadyToFullWake) { |
| 8172 | aotEvaluate(NULL); |
| 8173 | } |
| 8174 | break; |
| 8175 | } |
| 8176 | } |
| 8177 | |
| 8178 | //****************************************************************************** |
| 8179 | // systemPowerEventOccurred |
| 8180 | // |
| 8181 | // The power controller is notifying us of a hardware-related power management |
| 8182 | // event that we must handle. |
| 8183 | // |
| 8184 | // systemPowerEventOccurred covers the same functionality that |
| 8185 | // receivePowerNotification does; it simply provides a richer API for conveying |
| 8186 | // more information. |
| 8187 | //****************************************************************************** |
| 8188 | |
| 8189 | IOReturn |
| 8190 | IOPMrootDomain::systemPowerEventOccurred( |
| 8191 | const OSSymbol *event, |
| 8192 | uint32_t intValue) |
| 8193 | { |
| 8194 | IOReturn attempt = kIOReturnSuccess; |
| 8195 | OSSharedPtr<OSNumber> newNumber; |
| 8196 | |
| 8197 | if (!event) { |
| 8198 | return kIOReturnBadArgument; |
| 8199 | } |
| 8200 | |
| 8201 | newNumber = OSNumber::withNumber(value: intValue, numberOfBits: 8 * sizeof(intValue)); |
| 8202 | if (!newNumber) { |
| 8203 | return kIOReturnInternalError; |
| 8204 | } |
| 8205 | |
| 8206 | attempt = systemPowerEventOccurred(event, value: static_cast<OSObject *>(newNumber.get())); |
| 8207 | |
| 8208 | return attempt; |
| 8209 | } |
| 8210 | |
| 8211 | void |
| 8212 | IOPMrootDomain::setThermalState(OSObject *value) |
| 8213 | { |
| 8214 | OSNumber * num; |
| 8215 | |
| 8216 | if (gIOPMWorkLoop->inGate() == false) { |
| 8217 | gIOPMWorkLoop->runAction( |
| 8218 | OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setThermalState), |
| 8219 | target: (OSObject *)this, |
| 8220 | arg0: (void *)value); |
| 8221 | |
| 8222 | return; |
| 8223 | } |
| 8224 | if (value && (num = OSDynamicCast(OSNumber, value))) { |
| 8225 | thermalWarningState = ((num->unsigned32BitValue() == kIOPMThermalLevelWarning) || |
| 8226 | (num->unsigned32BitValue() == kIOPMThermalLevelTrap)) ? 1 : 0; |
| 8227 | } |
| 8228 | } |
| 8229 | |
| 8230 | IOReturn |
| 8231 | IOPMrootDomain::systemPowerEventOccurred( |
| 8232 | const OSSymbol *event, |
| 8233 | OSObject *value) |
| 8234 | { |
| 8235 | OSSharedPtr<OSDictionary> thermalsDict; |
| 8236 | bool shouldUpdate = true; |
| 8237 | |
| 8238 | if (!event || !value) { |
| 8239 | return kIOReturnBadArgument; |
| 8240 | } |
| 8241 | |
| 8242 | // LOCK |
| 8243 | // We reuse featuresDict Lock because it already exists and guards |
| 8244 | // the very infrequently used publish/remove feature mechanism; so there's zero rsk |
| 8245 | // of stepping on that lock. |
| 8246 | if (featuresDictLock) { |
| 8247 | IOLockLock(featuresDictLock); |
| 8248 | } |
| 8249 | |
| 8250 | OSSharedPtr<OSObject> origThermalsProp = copyProperty(kIOPMRootDomainPowerStatusKey); |
| 8251 | OSDictionary * origThermalsDict = OSDynamicCast(OSDictionary, origThermalsProp.get()); |
| 8252 | |
| 8253 | if (origThermalsDict) { |
| 8254 | thermalsDict = OSDictionary::withDictionary(dict: origThermalsDict); |
| 8255 | } else { |
| 8256 | thermalsDict = OSDictionary::withCapacity(capacity: 1); |
| 8257 | } |
| 8258 | |
| 8259 | if (!thermalsDict) { |
| 8260 | shouldUpdate = false; |
| 8261 | goto exit; |
| 8262 | } |
| 8263 | |
| 8264 | thermalsDict->setObject(aKey: event, anObject: value); |
| 8265 | |
| 8266 | setProperty(kIOPMRootDomainPowerStatusKey, anObject: thermalsDict.get()); |
| 8267 | |
| 8268 | exit: |
| 8269 | // UNLOCK |
| 8270 | if (featuresDictLock) { |
| 8271 | IOLockUnlock(featuresDictLock); |
| 8272 | } |
| 8273 | |
| 8274 | if (shouldUpdate) { |
| 8275 | if (event && |
| 8276 | event->isEqualTo(kIOPMThermalLevelWarningKey)) { |
| 8277 | setThermalState(value); |
| 8278 | } |
| 8279 | messageClients(kIOPMMessageSystemPowerEventOccurred, argument: (void *)NULL); |
| 8280 | } |
| 8281 | |
| 8282 | return kIOReturnSuccess; |
| 8283 | } |
| 8284 | |
| 8285 | //****************************************************************************** |
| 8286 | // receivePowerNotification |
| 8287 | // |
| 8288 | // The power controller is notifying us of a hardware-related power management |
| 8289 | // event that we must handle. This may be a result of an 'environment' interrupt |
| 8290 | // from the power mgt micro. |
| 8291 | //****************************************************************************** |
| 8292 | |
| 8293 | IOReturn |
| 8294 | IOPMrootDomain::receivePowerNotification( UInt32 msg ) |
| 8295 | { |
| 8296 | if (msg & kIOPMPowerButton) { |
| 8297 | uint32_t currentPhase = pmTracer->getTracePhase(); |
| 8298 | if (currentPhase != kIOPMTracePointSystemUp && currentPhase > kIOPMTracePointSystemSleep) { |
| 8299 | DEBUG_LOG("power button pressed during wake. phase = %u\n" , currentPhase); |
| 8300 | swd_flags |= SWD_PWR_BTN_STACKSHOT; |
| 8301 | thread_call_enter(call: powerButtonDown); |
| 8302 | } else { |
| 8303 | DEBUG_LOG("power button pressed when system is up\n" ); |
| 8304 | } |
| 8305 | } else if (msg & kIOPMPowerButtonUp) { |
| 8306 | if (swd_flags & SWD_PWR_BTN_STACKSHOT) { |
| 8307 | swd_flags &= ~SWD_PWR_BTN_STACKSHOT; |
| 8308 | thread_call_enter(call: powerButtonUp); |
| 8309 | } |
| 8310 | } else { |
| 8311 | pmPowerStateQueue->submitPowerEvent( |
| 8312 | eventType: kPowerEventReceivedPowerNotification, arg0: (void *)(uintptr_t) msg ); |
| 8313 | } |
| 8314 | return kIOReturnSuccess; |
| 8315 | } |
| 8316 | |
| 8317 | void |
| 8318 | IOPMrootDomain::handlePowerNotification( UInt32 msg ) |
| 8319 | { |
| 8320 | bool eval_clamshell = false; |
| 8321 | bool eval_clamshell_alarm = false; |
| 8322 | |
| 8323 | ASSERT_GATED(); |
| 8324 | |
| 8325 | /* |
| 8326 | * Local (IOPMrootDomain only) eval clamshell command |
| 8327 | */ |
| 8328 | if (msg & kLocalEvalClamshellCommand) { |
| 8329 | if ((gClamshellFlags & kClamshell_WAR_47715679) && isRTCAlarmWake) { |
| 8330 | eval_clamshell_alarm = true; |
| 8331 | |
| 8332 | // reset isRTCAlarmWake. This evaluation should happen only once |
| 8333 | // on RTC/Alarm wake. Any clamshell events after wake should follow |
| 8334 | // the regular evaluation |
| 8335 | isRTCAlarmWake = false; |
| 8336 | } else { |
| 8337 | eval_clamshell = true; |
| 8338 | } |
| 8339 | } |
| 8340 | |
| 8341 | /* |
| 8342 | * Overtemp |
| 8343 | */ |
| 8344 | if (msg & kIOPMOverTemp) { |
| 8345 | DLOG("Thermal overtemp message received!\n" ); |
| 8346 | thermalEmergencyState = true; |
| 8347 | privateSleepSystem(sleepReason: kIOPMSleepReasonThermalEmergency); |
| 8348 | } |
| 8349 | |
| 8350 | /* |
| 8351 | * Forward DW thermal notification to client, if system is not going to sleep |
| 8352 | */ |
| 8353 | if ((msg & kIOPMDWOverTemp) && (_systemTransitionType != kSystemTransitionSleep)) { |
| 8354 | DLOG("DarkWake thermal limits message received!\n" ); |
| 8355 | messageClients(kIOPMMessageDarkWakeThermalEmergency); |
| 8356 | } |
| 8357 | |
| 8358 | /* |
| 8359 | * Sleep Now! |
| 8360 | */ |
| 8361 | if (msg & kIOPMSleepNow) { |
| 8362 | privateSleepSystem(sleepReason: kIOPMSleepReasonSoftware); |
| 8363 | } |
| 8364 | |
| 8365 | /* |
| 8366 | * Power Emergency |
| 8367 | */ |
| 8368 | if (msg & kIOPMPowerEmergency) { |
| 8369 | DLOG("Received kIOPMPowerEmergency" ); |
| 8370 | lowBatteryCondition = true; |
| 8371 | privateSleepSystem(sleepReason: kIOPMSleepReasonLowPower); |
| 8372 | } |
| 8373 | |
| 8374 | /* |
| 8375 | * Clamshell OPEN |
| 8376 | */ |
| 8377 | if (msg & kIOPMClamshellOpened) { |
| 8378 | DLOG("Clamshell opened\n" ); |
| 8379 | // Received clamshel open message from clamshell controlling driver |
| 8380 | // Update our internal state and tell general interest clients |
| 8381 | clamshellClosed = false; |
| 8382 | clamshellExists = true; |
| 8383 | |
| 8384 | // Don't issue a hid tickle when lid is open and polled on wake |
| 8385 | if (msg & kIOPMSetValue) { |
| 8386 | setProperty(kIOPMRootDomainWakeTypeKey, aString: "Lid Open" ); |
| 8387 | reportUserInput(); |
| 8388 | } |
| 8389 | |
| 8390 | // Tell PMCPU |
| 8391 | informCPUStateChange(type: kInformLid, value: 0); |
| 8392 | |
| 8393 | // Tell general interest clients |
| 8394 | sendClientClamshellNotification(); |
| 8395 | |
| 8396 | bool aborting = ((lastSleepReason == kIOPMSleepReasonClamshell) |
| 8397 | || (lastSleepReason == kIOPMSleepReasonIdle) |
| 8398 | || (lastSleepReason == kIOPMSleepReasonMaintenance)); |
| 8399 | if (aborting) { |
| 8400 | userActivityCount++; |
| 8401 | } |
| 8402 | DLOG("clamshell tickled %d lastSleepReason %d\n" , userActivityCount, lastSleepReason); |
| 8403 | } |
| 8404 | |
| 8405 | /* |
| 8406 | * Clamshell CLOSED |
| 8407 | * Send the clamshell interest notification since the lid is closing. |
| 8408 | */ |
| 8409 | if (msg & kIOPMClamshellClosed) { |
| 8410 | if ((clamshellIgnoreClose || (gClamshellFlags & kClamshell_WAR_38378787)) && |
| 8411 | clamshellClosed && clamshellExists) { |
| 8412 | DLOG("Ignoring redundant Clamshell close event\n" ); |
| 8413 | } else { |
| 8414 | DLOG("Clamshell closed\n" ); |
| 8415 | // Received clamshel open message from clamshell controlling driver |
| 8416 | // Update our internal state and tell general interest clients |
| 8417 | clamshellClosed = true; |
| 8418 | clamshellExists = true; |
| 8419 | |
| 8420 | // Ignore all following clamshell close events until the clamshell |
| 8421 | // is opened or the system sleeps. When a clamshell close triggers |
| 8422 | // a system wake, the lid driver may send us two clamshell close |
| 8423 | // events, one for the clamshell close event itself, and a second |
| 8424 | // close event when the driver polls the lid state on wake. |
| 8425 | clamshellIgnoreClose = true; |
| 8426 | |
| 8427 | // Tell PMCPU |
| 8428 | informCPUStateChange(type: kInformLid, value: 1); |
| 8429 | |
| 8430 | // Tell general interest clients |
| 8431 | sendClientClamshellNotification(); |
| 8432 | |
| 8433 | // And set eval_clamshell = so we can attempt |
| 8434 | eval_clamshell = true; |
| 8435 | } |
| 8436 | } |
| 8437 | |
| 8438 | /* |
| 8439 | * Set Desktop mode (sent from graphics) |
| 8440 | * |
| 8441 | * -> reevaluate lid state |
| 8442 | */ |
| 8443 | if (msg & kIOPMSetDesktopMode) { |
| 8444 | desktopMode = (0 != (msg & kIOPMSetValue)); |
| 8445 | msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue); |
| 8446 | DLOG("Desktop mode %d\n" , desktopMode); |
| 8447 | |
| 8448 | sendClientClamshellNotification(); |
| 8449 | |
| 8450 | // Re-evaluate the lid state |
| 8451 | eval_clamshell = true; |
| 8452 | } |
| 8453 | |
| 8454 | /* |
| 8455 | * AC Adaptor connected |
| 8456 | * |
| 8457 | * -> reevaluate lid state |
| 8458 | */ |
| 8459 | if (msg & kIOPMSetACAdaptorConnected) { |
| 8460 | acAdaptorConnected = (0 != (msg & kIOPMSetValue)); |
| 8461 | msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue); |
| 8462 | |
| 8463 | // Tell CPU PM |
| 8464 | informCPUStateChange(type: kInformAC, value: !acAdaptorConnected); |
| 8465 | |
| 8466 | // Tell BSD if AC is connected |
| 8467 | // 0 == external power source; 1 == on battery |
| 8468 | post_sys_powersource(acAdaptorConnected ? 0:1); |
| 8469 | |
| 8470 | sendClientClamshellNotification(); |
| 8471 | |
| 8472 | IOUserServer::powerSourceChanged(acAttached: acAdaptorConnected); |
| 8473 | |
| 8474 | // Re-evaluate the lid state |
| 8475 | eval_clamshell = true; |
| 8476 | |
| 8477 | // Lack of AC may have latched a display wrangler tickle. |
| 8478 | // This mirrors the hardware's USB wake event latch, where a latched |
| 8479 | // USB wake event followed by an AC attach will trigger a full wake. |
| 8480 | latchDisplayWranglerTickle( latch: false ); |
| 8481 | |
| 8482 | #if HIBERNATION |
| 8483 | // AC presence will reset the standy timer delay adjustment. |
| 8484 | _standbyTimerResetSeconds = 0; |
| 8485 | #endif |
| 8486 | if (!userIsActive) { |
| 8487 | // Reset userActivityTime when power supply is changed(rdr 13789330) |
| 8488 | clock_get_uptime(result: &userActivityTime); |
| 8489 | } |
| 8490 | } |
| 8491 | |
| 8492 | /* |
| 8493 | * Enable Clamshell (external display disappear) |
| 8494 | * |
| 8495 | * -> reevaluate lid state |
| 8496 | */ |
| 8497 | if (msg & kIOPMEnableClamshell) { |
| 8498 | DLOG("Clamshell enabled\n" ); |
| 8499 | |
| 8500 | // Re-evaluate the lid state |
| 8501 | // System should sleep on external display disappearance |
| 8502 | // in lid closed operation. |
| 8503 | if (true == clamshellDisabled) { |
| 8504 | eval_clamshell = true; |
| 8505 | |
| 8506 | #if DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY |
| 8507 | // Also clear kClamshellSleepDisableInternal when graphics enables |
| 8508 | // the clamshell during a full wake. When graphics is behaving as |
| 8509 | // expected, this will allow clamshell close to be honored earlier |
| 8510 | // rather than waiting for the delayed evaluation. |
| 8511 | if ((clamshellSleepDisableMask & kClamshellSleepDisableInternal) && |
| 8512 | (CAP_PENDING(kIOPMSystemCapabilityGraphics) || |
| 8513 | CAP_CURRENT(kIOPMSystemCapabilityGraphics))) { |
| 8514 | setClamShellSleepDisable(false, kClamshellSleepDisableInternal); |
| 8515 | |
| 8516 | // Cancel the TC to avoid an extra kLocalEvalClamshellCommand |
| 8517 | // when timer expires which is harmless but useless. |
| 8518 | thread_call_cancel(fullWakeThreadCall); |
| 8519 | } |
| 8520 | #endif |
| 8521 | } |
| 8522 | |
| 8523 | clamshellDisabled = false; |
| 8524 | sendClientClamshellNotification(); |
| 8525 | } |
| 8526 | |
| 8527 | /* |
| 8528 | * Disable Clamshell (external display appeared) |
| 8529 | * We don't bother re-evaluating clamshell state. If the system is awake, |
| 8530 | * the lid is probably open. |
| 8531 | */ |
| 8532 | if (msg & kIOPMDisableClamshell) { |
| 8533 | DLOG("Clamshell disabled\n" ); |
| 8534 | clamshellDisabled = true; |
| 8535 | sendClientClamshellNotification(); |
| 8536 | } |
| 8537 | |
| 8538 | /* |
| 8539 | * Evaluate clamshell and SLEEP if appropriate |
| 8540 | */ |
| 8541 | if (eval_clamshell_alarm && clamshellClosed) { |
| 8542 | if (shouldSleepOnRTCAlarmWake()) { |
| 8543 | privateSleepSystem(sleepReason: kIOPMSleepReasonClamshell); |
| 8544 | } |
| 8545 | } else if (eval_clamshell && clamshellClosed) { |
| 8546 | if (shouldSleepOnClamshellClosed()) { |
| 8547 | privateSleepSystem(sleepReason: kIOPMSleepReasonClamshell); |
| 8548 | } else { |
| 8549 | evaluatePolicy( stimulus: kStimulusDarkWakeEvaluate ); |
| 8550 | } |
| 8551 | } |
| 8552 | |
| 8553 | if (msg & kIOPMProModeEngaged) { |
| 8554 | int newState = 1; |
| 8555 | DLOG("ProModeEngaged\n" ); |
| 8556 | messageClient(kIOPMMessageProModeStateChange, client: systemCapabilityNotifier.get(), messageArgument: &newState, argSize: sizeof(newState)); |
| 8557 | } |
| 8558 | |
| 8559 | if (msg & kIOPMProModeDisengaged) { |
| 8560 | int newState = 0; |
| 8561 | DLOG("ProModeDisengaged\n" ); |
| 8562 | messageClient(kIOPMMessageProModeStateChange, client: systemCapabilityNotifier.get(), messageArgument: &newState, argSize: sizeof(newState)); |
| 8563 | } |
| 8564 | } |
| 8565 | |
| 8566 | //****************************************************************************** |
| 8567 | // evaluatePolicy |
| 8568 | // |
| 8569 | // Evaluate root-domain policy in response to external changes. |
| 8570 | //****************************************************************************** |
| 8571 | |
| 8572 | void |
| 8573 | IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg ) |
| 8574 | { |
| 8575 | union { |
| 8576 | struct { |
| 8577 | int idleSleepEnabled : 1; |
| 8578 | int idleSleepDisabled : 1; |
| 8579 | int displaySleep : 1; |
| 8580 | int sleepDelayChanged : 1; |
| 8581 | int evaluateDarkWake : 1; |
| 8582 | int adjustPowerState : 1; |
| 8583 | int userBecameInactive : 1; |
| 8584 | int displaySleepEntry : 1; |
| 8585 | } bit; |
| 8586 | uint32_t u32; |
| 8587 | } flags; |
| 8588 | |
| 8589 | |
| 8590 | ASSERT_GATED(); |
| 8591 | flags.u32 = 0; |
| 8592 | |
| 8593 | switch (stimulus) { |
| 8594 | case kStimulusDisplayWranglerSleep: |
| 8595 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8596 | if (!wranglerPowerOff) { |
| 8597 | // wrangler is in sleep state or lower |
| 8598 | flags.bit.displaySleep = true; |
| 8599 | } |
| 8600 | if (!wranglerAsleep) { |
| 8601 | // transition from wrangler wake to wrangler sleep |
| 8602 | flags.bit.displaySleepEntry = true; |
| 8603 | wranglerAsleep = true; |
| 8604 | } |
| 8605 | break; |
| 8606 | |
| 8607 | case kStimulusDisplayWranglerWake: |
| 8608 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8609 | displayIdleForDemandSleep = false; |
| 8610 | wranglerPowerOff = false; |
| 8611 | wranglerAsleep = false; |
| 8612 | break; |
| 8613 | |
| 8614 | case kStimulusEnterUserActiveState: |
| 8615 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8616 | if (_preventUserActive) { |
| 8617 | DLOG("user active dropped\n" ); |
| 8618 | break; |
| 8619 | } |
| 8620 | if (!userIsActive) { |
| 8621 | userIsActive = true; |
| 8622 | userWasActive = true; |
| 8623 | clock_get_uptime(result: &gUserActiveAbsTime); |
| 8624 | |
| 8625 | // Stay awake after dropping demand for display power on |
| 8626 | if (kFullWakeReasonDisplayOn == fullWakeReason) { |
| 8627 | fullWakeReason = fFullWakeReasonDisplayOnAndLocalUser; |
| 8628 | DLOG("User activity while in notification wake\n" ); |
| 8629 | changePowerStateWithOverrideTo( ordinal: getRUN_STATE(), reason: 0); |
| 8630 | } |
| 8631 | |
| 8632 | kdebugTrace(event: kPMLogUserActiveState, regId: 0, param1: 1, param2: 0); |
| 8633 | setProperty(aKey: gIOPMUserIsActiveKey.get(), anObject: kOSBooleanTrue); |
| 8634 | messageClients(kIOPMMessageUserIsActiveChanged); |
| 8635 | } |
| 8636 | flags.bit.idleSleepDisabled = true; |
| 8637 | break; |
| 8638 | |
| 8639 | case kStimulusLeaveUserActiveState: |
| 8640 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8641 | if (userIsActive) { |
| 8642 | clock_get_uptime(result: &gUserInactiveAbsTime); |
| 8643 | userIsActive = false; |
| 8644 | clock_get_uptime(result: &userBecameInactiveTime); |
| 8645 | flags.bit.userBecameInactive = true; |
| 8646 | |
| 8647 | kdebugTrace(event: kPMLogUserActiveState, regId: 0, param1: 0, param2: 0); |
| 8648 | setProperty(aKey: gIOPMUserIsActiveKey.get(), anObject: kOSBooleanFalse); |
| 8649 | messageClients(kIOPMMessageUserIsActiveChanged); |
| 8650 | } |
| 8651 | break; |
| 8652 | |
| 8653 | case kStimulusAggressivenessChanged: |
| 8654 | { |
| 8655 | DMSG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8656 | unsigned long aggressiveValue; |
| 8657 | uint32_t minutesToIdleSleep = 0; |
| 8658 | uint32_t minutesToDisplayDim = 0; |
| 8659 | uint32_t minutesDelta = 0; |
| 8660 | |
| 8661 | // Fetch latest display and system sleep slider values. |
| 8662 | aggressiveValue = 0; |
| 8663 | getAggressiveness(type: kPMMinutesToSleep, outLevel: &aggressiveValue); |
| 8664 | minutesToIdleSleep = (uint32_t) aggressiveValue; |
| 8665 | |
| 8666 | aggressiveValue = 0; |
| 8667 | getAggressiveness(type: kPMMinutesToDim, outLevel: &aggressiveValue); |
| 8668 | minutesToDisplayDim = (uint32_t) aggressiveValue; |
| 8669 | DLOG("aggressiveness changed: system %u->%u, display %u\n" , |
| 8670 | sleepSlider, minutesToIdleSleep, minutesToDisplayDim); |
| 8671 | |
| 8672 | DLOG("idle time -> %d ms (ena %d)\n" , |
| 8673 | idleMilliSeconds, (minutesToIdleSleep != 0)); |
| 8674 | |
| 8675 | // How long to wait before sleeping the system once |
| 8676 | // the displays turns off is indicated by 'extraSleepDelay'. |
| 8677 | |
| 8678 | if (minutesToIdleSleep > minutesToDisplayDim) { |
| 8679 | minutesDelta = minutesToIdleSleep - minutesToDisplayDim; |
| 8680 | } else if (minutesToIdleSleep == minutesToDisplayDim) { |
| 8681 | minutesDelta = 1; |
| 8682 | } |
| 8683 | |
| 8684 | if ((!idleSleepEnabled) && (minutesToIdleSleep != 0)) { |
| 8685 | idleSleepEnabled = flags.bit.idleSleepEnabled = true; |
| 8686 | } |
| 8687 | |
| 8688 | if ((idleSleepEnabled) && (minutesToIdleSleep == 0)) { |
| 8689 | flags.bit.idleSleepDisabled = true; |
| 8690 | idleSleepEnabled = false; |
| 8691 | } |
| 8692 | #if !defined(XNU_TARGET_OS_OSX) |
| 8693 | if (0x7fffffff == minutesToIdleSleep) { |
| 8694 | minutesToIdleSleep = idleMilliSeconds / 1000; |
| 8695 | } |
| 8696 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
| 8697 | |
| 8698 | if (((minutesDelta != extraSleepDelay) || |
| 8699 | (userActivityTime != userActivityTime_prev)) && |
| 8700 | !flags.bit.idleSleepEnabled && !flags.bit.idleSleepDisabled) { |
| 8701 | flags.bit.sleepDelayChanged = true; |
| 8702 | } |
| 8703 | |
| 8704 | if (systemDarkWake && !darkWakeToSleepASAP && |
| 8705 | (flags.bit.idleSleepEnabled || flags.bit.idleSleepDisabled)) { |
| 8706 | // Reconsider decision to remain in dark wake |
| 8707 | flags.bit.evaluateDarkWake = true; |
| 8708 | } |
| 8709 | |
| 8710 | sleepSlider = minutesToIdleSleep; |
| 8711 | extraSleepDelay = minutesDelta; |
| 8712 | userActivityTime_prev = userActivityTime; |
| 8713 | } break; |
| 8714 | |
| 8715 | case kStimulusDemandSystemSleep: |
| 8716 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8717 | displayIdleForDemandSleep = true; |
| 8718 | if (wrangler && wranglerIdleSettings) { |
| 8719 | // Request wrangler idle only when demand sleep is triggered |
| 8720 | // from full wake. |
| 8721 | if (CAP_CURRENT(kIOPMSystemCapabilityGraphics)) { |
| 8722 | wrangler->setProperties(wranglerIdleSettings.get()); |
| 8723 | DLOG("Requested wrangler idle\n" ); |
| 8724 | } |
| 8725 | } |
| 8726 | // arg = sleepReason |
| 8727 | changePowerStateWithOverrideTo( ordinal: SLEEP_STATE, reason: arg ); |
| 8728 | break; |
| 8729 | |
| 8730 | case kStimulusAllowSystemSleepChanged: |
| 8731 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8732 | flags.bit.adjustPowerState = true; |
| 8733 | break; |
| 8734 | |
| 8735 | case kStimulusDarkWakeActivityTickle: |
| 8736 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8737 | // arg == true implies real and not self generated wrangler tickle. |
| 8738 | // Update wake type on PM work loop instead of the tickle thread to |
| 8739 | // eliminate the possibility of an early tickle clobbering the wake |
| 8740 | // type set by the platform driver. |
| 8741 | if (arg == true) { |
| 8742 | setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeHIDActivity); |
| 8743 | } |
| 8744 | |
| 8745 | if (!darkWakeExit) { |
| 8746 | if (latchDisplayWranglerTickle(latch: true)) { |
| 8747 | DLOG("latched tickle\n" ); |
| 8748 | break; |
| 8749 | } |
| 8750 | |
| 8751 | darkWakeExit = true; |
| 8752 | DLOG("Requesting full wake due to dark wake activity tickle\n" ); |
| 8753 | requestFullWake( reason: kFullWakeReasonLocalUser ); |
| 8754 | } |
| 8755 | break; |
| 8756 | |
| 8757 | case kStimulusDarkWakeEntry: |
| 8758 | case kStimulusDarkWakeReentry: |
| 8759 | DLOG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8760 | // Any system transitions since the last dark wake transition |
| 8761 | // will invalid the stimulus. |
| 8762 | |
| 8763 | if (arg == _systemStateGeneration) { |
| 8764 | DLOG("dark wake entry\n" ); |
| 8765 | systemDarkWake = true; |
| 8766 | |
| 8767 | // Keep wranglerPowerOff an invariant when wrangler is absent |
| 8768 | if (wrangler) { |
| 8769 | wranglerPowerOff = true; |
| 8770 | } |
| 8771 | |
| 8772 | if (kStimulusDarkWakeEntry == stimulus) { |
| 8773 | clock_get_uptime(result: &userBecameInactiveTime); |
| 8774 | flags.bit.evaluateDarkWake = true; |
| 8775 | if (activitySinceSleep()) { |
| 8776 | DLOG("User activity recorded while going to darkwake\n" ); |
| 8777 | reportUserInput(); |
| 8778 | } |
| 8779 | } |
| 8780 | |
| 8781 | // Always accelerate disk spindown while in dark wake, |
| 8782 | // even if system does not support/allow sleep. |
| 8783 | |
| 8784 | cancelIdleSleepTimer(); |
| 8785 | setQuickSpinDownTimeout(); |
| 8786 | } |
| 8787 | break; |
| 8788 | |
| 8789 | case kStimulusDarkWakeEvaluate: |
| 8790 | DMSG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8791 | if (systemDarkWake) { |
| 8792 | flags.bit.evaluateDarkWake = true; |
| 8793 | } |
| 8794 | break; |
| 8795 | |
| 8796 | case kStimulusNoIdleSleepPreventers: |
| 8797 | DMSG("evaluatePolicy( %d, 0x%x )\n" , stimulus, arg); |
| 8798 | flags.bit.adjustPowerState = true; |
| 8799 | break; |
| 8800 | } /* switch(stimulus) */ |
| 8801 | |
| 8802 | if (flags.bit.evaluateDarkWake && (kFullWakeReasonNone == fullWakeReason)) { |
| 8803 | DLOG("DarkWake: sleepASAP %d, clamshell closed %d, disabled %d/%x, desktopMode %d, ac %d\n" , |
| 8804 | darkWakeToSleepASAP, clamshellClosed, clamshellDisabled, clamshellSleepDisableMask, desktopMode, acAdaptorConnected); |
| 8805 | if (darkWakeToSleepASAP || |
| 8806 | (clamshellClosed && !(desktopMode && acAdaptorConnected))) { |
| 8807 | uint32_t newSleepReason; |
| 8808 | |
| 8809 | if (CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) { |
| 8810 | // System was previously in full wake. Sleep reason from |
| 8811 | // full to dark already recorded in fullToDarkReason. |
| 8812 | |
| 8813 | if (lowBatteryCondition) { |
| 8814 | newSleepReason = kIOPMSleepReasonLowPower; |
| 8815 | } else if (thermalEmergencyState) { |
| 8816 | newSleepReason = kIOPMSleepReasonThermalEmergency; |
| 8817 | } else { |
| 8818 | newSleepReason = fullToDarkReason; |
| 8819 | } |
| 8820 | } else { |
| 8821 | // In dark wake from system sleep. |
| 8822 | |
| 8823 | if (darkWakeSleepService) { |
| 8824 | newSleepReason = kIOPMSleepReasonSleepServiceExit; |
| 8825 | } else { |
| 8826 | newSleepReason = kIOPMSleepReasonMaintenance; |
| 8827 | } |
| 8828 | } |
| 8829 | |
| 8830 | if (checkSystemCanSleep(sleepReason: newSleepReason)) { |
| 8831 | privateSleepSystem(sleepReason: newSleepReason); |
| 8832 | } |
| 8833 | } else { // non-maintenance (network) dark wake |
| 8834 | if (checkSystemCanSleep(sleepReason: kIOPMSleepReasonIdle)) { |
| 8835 | // Release power clamp, and wait for children idle. |
| 8836 | adjustPowerState(sleepASAP: true); |
| 8837 | } else { |
| 8838 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonDarkWakeCannotSleep); |
| 8839 | } |
| 8840 | } |
| 8841 | } |
| 8842 | |
| 8843 | if (systemDarkWake) { |
| 8844 | // The rest are irrelevant while system is in dark wake. |
| 8845 | flags.u32 = 0; |
| 8846 | } |
| 8847 | |
| 8848 | if ((flags.bit.displaySleepEntry) && |
| 8849 | (kFullWakeReasonDisplayOn == fullWakeReason)) { |
| 8850 | // kIOPMSleepReasonNotificationWakeExit |
| 8851 | DLOG("Display sleep while in notification wake\n" ); |
| 8852 | changePowerStateWithOverrideTo(ordinal: SLEEP_STATE, reason: kIOPMSleepReasonNotificationWakeExit); |
| 8853 | } |
| 8854 | |
| 8855 | if (flags.bit.userBecameInactive || flags.bit.sleepDelayChanged) { |
| 8856 | bool cancelQuickSpindown = false; |
| 8857 | |
| 8858 | if (flags.bit.sleepDelayChanged) { |
| 8859 | // Cancel existing idle sleep timer and quick disk spindown. |
| 8860 | // New settings will be applied by the idleSleepEnabled flag |
| 8861 | // handler below if idle sleep is enabled. |
| 8862 | |
| 8863 | DLOG("extra sleep timer changed\n" ); |
| 8864 | cancelIdleSleepTimer(); |
| 8865 | cancelQuickSpindown = true; |
| 8866 | } else { |
| 8867 | DLOG("user inactive\n" ); |
| 8868 | } |
| 8869 | |
| 8870 | if (!userIsActive && idleSleepEnabled) { |
| 8871 | startIdleSleepTimer(inMilliSeconds: getTimeToIdleSleep()); |
| 8872 | } |
| 8873 | |
| 8874 | if (cancelQuickSpindown) { |
| 8875 | restoreUserSpinDownTimeout(); |
| 8876 | } |
| 8877 | } |
| 8878 | |
| 8879 | if (flags.bit.idleSleepEnabled) { |
| 8880 | DLOG("idle sleep timer enabled\n" ); |
| 8881 | if (!wrangler) { |
| 8882 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 8883 | startIdleSleepTimer(inMilliSeconds: getTimeToIdleSleep()); |
| 8884 | #else |
| 8885 | changePowerStateWithTagToPriv(getRUN_STATE(), kCPSReasonIdleSleepEnabled); |
| 8886 | startIdleSleepTimer( idleMilliSeconds ); |
| 8887 | #endif |
| 8888 | } else { |
| 8889 | // Start idle timer if prefs now allow system sleep |
| 8890 | // and user is already inactive. Disk spindown is |
| 8891 | // accelerated upon timer expiration. |
| 8892 | |
| 8893 | if (!userIsActive) { |
| 8894 | startIdleSleepTimer(inMilliSeconds: getTimeToIdleSleep()); |
| 8895 | } |
| 8896 | } |
| 8897 | } |
| 8898 | |
| 8899 | if (flags.bit.idleSleepDisabled) { |
| 8900 | DLOG("idle sleep timer disabled\n" ); |
| 8901 | cancelIdleSleepTimer(); |
| 8902 | restoreUserSpinDownTimeout(); |
| 8903 | adjustPowerState(); |
| 8904 | } |
| 8905 | |
| 8906 | if (flags.bit.adjustPowerState) { |
| 8907 | bool sleepASAP = false; |
| 8908 | |
| 8909 | if (!systemBooting && (0 == idleSleepPreventersCount())) { |
| 8910 | if (!wrangler) { |
| 8911 | changePowerStateWithTagToPriv(ordinal: getRUN_STATE(), reason: kCPSReasonEvaluatePolicy); |
| 8912 | if (idleSleepEnabled) { |
| 8913 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 8914 | if (!extraSleepDelay && !idleSleepTimerPending && !gNoIdleFlag) { |
| 8915 | sleepASAP = true; |
| 8916 | } |
| 8917 | #else |
| 8918 | // stay awake for at least idleMilliSeconds |
| 8919 | startIdleSleepTimer(idleMilliSeconds); |
| 8920 | #endif |
| 8921 | } |
| 8922 | } else if (!extraSleepDelay && !idleSleepTimerPending && !systemDarkWake && !gNoIdleFlag) { |
| 8923 | sleepASAP = true; |
| 8924 | } |
| 8925 | } |
| 8926 | |
| 8927 | adjustPowerState(sleepASAP); |
| 8928 | } |
| 8929 | } |
| 8930 | |
| 8931 | //****************************************************************************** |
| 8932 | |
| 8933 | unsigned int |
| 8934 | IOPMrootDomain::idleSleepPreventersCount() |
| 8935 | { |
| 8936 | if (_aotMode) { |
| 8937 | unsigned int count __block; |
| 8938 | count = 0; |
| 8939 | preventIdleSleepList->iterateObjects(block: ^bool (OSObject * obj) |
| 8940 | { |
| 8941 | count += (NULL == obj->metaCast(toMeta: "AppleARMBacklight" )); |
| 8942 | return false; |
| 8943 | }); |
| 8944 | return count; |
| 8945 | } |
| 8946 | |
| 8947 | return preventIdleSleepList->getCount(); |
| 8948 | } |
| 8949 | |
| 8950 | |
| 8951 | //****************************************************************************** |
| 8952 | // requestFullWake |
| 8953 | // |
| 8954 | // Request transition from dark wake to full wake |
| 8955 | //****************************************************************************** |
| 8956 | |
| 8957 | void |
| 8958 | IOPMrootDomain::requestFullWake( FullWakeReason reason ) |
| 8959 | { |
| 8960 | uint32_t options = 0; |
| 8961 | IOService * pciRoot = NULL; |
| 8962 | bool promotion = false; |
| 8963 | |
| 8964 | // System must be in dark wake and a valid reason for entering full wake |
| 8965 | if ((kFullWakeReasonNone == reason) || |
| 8966 | (kFullWakeReasonNone != fullWakeReason) || |
| 8967 | (CAP_CURRENT(kIOPMSystemCapabilityGraphics))) { |
| 8968 | return; |
| 8969 | } |
| 8970 | |
| 8971 | // Will clear reason upon exit from full wake |
| 8972 | fullWakeReason = reason; |
| 8973 | |
| 8974 | _desiredCapability |= (kIOPMSystemCapabilityGraphics | |
| 8975 | kIOPMSystemCapabilityAudio); |
| 8976 | |
| 8977 | if ((kSystemTransitionWake == _systemTransitionType) && |
| 8978 | !(_pendingCapability & kIOPMSystemCapabilityGraphics) && |
| 8979 | !darkWakePowerClamped) { |
| 8980 | // Promote to full wake while waking up to dark wake due to tickle. |
| 8981 | // PM will hold off notifying the graphics subsystem about system wake |
| 8982 | // as late as possible, so if a HID tickle does arrive, graphics can |
| 8983 | // power up from this same wake transition. Otherwise, the latency to |
| 8984 | // power up graphics on the following transition can be huge on certain |
| 8985 | // systems. However, once any power clamping has taken effect, it is |
| 8986 | // too late to promote the current dark wake transition to a full wake. |
| 8987 | _pendingCapability |= (kIOPMSystemCapabilityGraphics | |
| 8988 | kIOPMSystemCapabilityAudio); |
| 8989 | |
| 8990 | // Tell the PCI parent of audio and graphics drivers to stop |
| 8991 | // delaying the child notifications. Same for root domain. |
| 8992 | pciRoot = pciHostBridgeDriver.get(); |
| 8993 | willEnterFullWake(); |
| 8994 | promotion = true; |
| 8995 | } |
| 8996 | |
| 8997 | // Unsafe to cancel once graphics was powered. |
| 8998 | // If system woke from dark wake, the return to sleep can |
| 8999 | // be cancelled. "awake -> dark -> sleep" transition |
| 9000 | // can be cancelled also, during the "dark -> sleep" phase |
| 9001 | // *prior* to driver power down. |
| 9002 | if (!CAP_HIGHEST(kIOPMSystemCapabilityGraphics) || |
| 9003 | _pendingCapability == 0) { |
| 9004 | options |= kIOPMSyncCancelPowerDown; |
| 9005 | } |
| 9006 | |
| 9007 | synchronizePowerTree(options, notifyRoot: pciRoot); |
| 9008 | |
| 9009 | if (kFullWakeReasonLocalUser == fullWakeReason) { |
| 9010 | // IOGraphics doesn't light the display even though graphics is |
| 9011 | // enabled in kIOMessageSystemCapabilityChange message(radar 9502104) |
| 9012 | // So, do an explicit activity tickle |
| 9013 | if (wrangler) { |
| 9014 | wrangler->activityTickle(type: 0, stateNumber: 0); |
| 9015 | } |
| 9016 | } |
| 9017 | |
| 9018 | // Log a timestamp for the initial full wake request. |
| 9019 | // System may not always honor this full wake request. |
| 9020 | if (!CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) { |
| 9021 | AbsoluteTime now; |
| 9022 | uint64_t nsec; |
| 9023 | |
| 9024 | clock_get_uptime(result: &now); |
| 9025 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 9026 | absolutetime_to_nanoseconds(abstime: now, result: &nsec); |
| 9027 | MSG("full wake %s (reason %u) %u ms\n" , |
| 9028 | promotion ? "promotion" : "request" , |
| 9029 | fullWakeReason, ((int)((nsec) / NSEC_PER_MSEC))); |
| 9030 | } |
| 9031 | } |
| 9032 | |
| 9033 | //****************************************************************************** |
| 9034 | // willEnterFullWake |
| 9035 | // |
| 9036 | // System will enter full wake from sleep, from dark wake, or from dark |
| 9037 | // wake promotion. This function aggregate things that are in common to |
| 9038 | // all three full wake transitions. |
| 9039 | // |
| 9040 | // Assumptions: fullWakeReason was updated |
| 9041 | //****************************************************************************** |
| 9042 | |
| 9043 | void |
| 9044 | IOPMrootDomain::willEnterFullWake( void ) |
| 9045 | { |
| 9046 | hibernateRetry = false; |
| 9047 | sleepToStandby = false; |
| 9048 | standbyNixed = false; |
| 9049 | resetTimers = false; |
| 9050 | sleepTimerMaintenance = false; |
| 9051 | |
| 9052 | assert(!CAP_CURRENT(kIOPMSystemCapabilityGraphics)); |
| 9053 | |
| 9054 | _systemMessageClientMask = kSystemMessageClientPowerd | |
| 9055 | kSystemMessageClientLegacyApp; |
| 9056 | |
| 9057 | if ((_highestCapability & kIOPMSystemCapabilityGraphics) == 0) { |
| 9058 | // First time to attain full wake capability since the last wake |
| 9059 | _systemMessageClientMask |= kSystemMessageClientKernel; |
| 9060 | |
| 9061 | // Set kIOPMUserTriggeredFullWakeKey before full wake for IOGraphics |
| 9062 | setProperty(aKey: gIOPMUserTriggeredFullWakeKey.get(), |
| 9063 | anObject: (kFullWakeReasonLocalUser == fullWakeReason) ? |
| 9064 | kOSBooleanTrue : kOSBooleanFalse); |
| 9065 | } |
| 9066 | #if HIBERNATION |
| 9067 | IOHibernateSetWakeCapabilities(_pendingCapability); |
| 9068 | #endif |
| 9069 | |
| 9070 | IOService::setAdvisoryTickleEnable( true ); |
| 9071 | tellClients(kIOMessageSystemWillPowerOn); |
| 9072 | preventTransitionToUserActive(prevent: false); |
| 9073 | } |
| 9074 | |
| 9075 | //****************************************************************************** |
| 9076 | // fullWakeDelayedWork |
| 9077 | // |
| 9078 | // System has already entered full wake. Invoked by a delayed thread call. |
| 9079 | //****************************************************************************** |
| 9080 | |
| 9081 | void |
| 9082 | IOPMrootDomain::fullWakeDelayedWork( void ) |
| 9083 | { |
| 9084 | #if DARK_TO_FULL_EVALUATE_CLAMSHELL_DELAY |
| 9085 | if (!gIOPMWorkLoop->inGate()) { |
| 9086 | gIOPMWorkLoop->runAction( |
| 9087 | OSMemberFunctionCast(IOWorkLoop::Action, this, |
| 9088 | &IOPMrootDomain::fullWakeDelayedWork), this); |
| 9089 | return; |
| 9090 | } |
| 9091 | |
| 9092 | DLOG("fullWakeDelayedWork cap cur %x pend %x high %x, clamshell disable %x/%x\n" , |
| 9093 | _currentCapability, _pendingCapability, _highestCapability, |
| 9094 | clamshellDisabled, clamshellSleepDisableMask); |
| 9095 | |
| 9096 | if (clamshellExists && |
| 9097 | CAP_CURRENT(kIOPMSystemCapabilityGraphics) && |
| 9098 | !CAP_CHANGE(kIOPMSystemCapabilityGraphics)) { |
| 9099 | if (clamshellSleepDisableMask & kClamshellSleepDisableInternal) { |
| 9100 | setClamShellSleepDisable(false, kClamshellSleepDisableInternal); |
| 9101 | } else { |
| 9102 | // Not the initial full wake after waking from sleep. |
| 9103 | // Evaluate the clamshell for rdar://problem/9157444. |
| 9104 | receivePowerNotification(kLocalEvalClamshellCommand); |
| 9105 | } |
| 9106 | } |
| 9107 | #endif |
| 9108 | } |
| 9109 | |
| 9110 | //****************************************************************************** |
| 9111 | // evaluateAssertions |
| 9112 | // |
| 9113 | //****************************************************************************** |
| 9114 | |
| 9115 | // Bitmask of all kernel assertions that prevent system idle sleep. |
| 9116 | // kIOPMDriverAssertionReservedBit7 is reserved for IOMediaBSDClient. |
| 9117 | #define NO_IDLE_SLEEP_ASSERTIONS_MASK \ |
| 9118 | (kIOPMDriverAssertionReservedBit7 | \ |
| 9119 | kIOPMDriverAssertionPreventSystemIdleSleepBit) |
| 9120 | |
| 9121 | void |
| 9122 | IOPMrootDomain::evaluateAssertions(IOPMDriverAssertionType newAssertions, IOPMDriverAssertionType oldAssertions) |
| 9123 | { |
| 9124 | IOPMDriverAssertionType changedBits = newAssertions ^ oldAssertions; |
| 9125 | |
| 9126 | messageClients(kIOPMMessageDriverAssertionsChanged); |
| 9127 | |
| 9128 | if (changedBits & kIOPMDriverAssertionPreventDisplaySleepBit) { |
| 9129 | if (wrangler) { |
| 9130 | bool value = (newAssertions & kIOPMDriverAssertionPreventDisplaySleepBit) ? true : false; |
| 9131 | |
| 9132 | DLOG("wrangler->setIgnoreIdleTimer\(%d)\n" , value); |
| 9133 | wrangler->setIgnoreIdleTimer( value ); |
| 9134 | } |
| 9135 | } |
| 9136 | |
| 9137 | if (changedBits & kIOPMDriverAssertionCPUBit) { |
| 9138 | if (_aotNow) { |
| 9139 | IOLog(format: "CPU assertions %d\n" , (0 != (kIOPMDriverAssertionCPUBit & newAssertions))); |
| 9140 | } |
| 9141 | evaluatePolicy(stimulus: _aotNow ? kStimulusNoIdleSleepPreventers : kStimulusDarkWakeEvaluate); |
| 9142 | if (!assertOnWakeSecs && gIOLastWakeAbsTime) { |
| 9143 | AbsoluteTime now; |
| 9144 | clock_usec_t microsecs; |
| 9145 | clock_get_uptime(result: &now); |
| 9146 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 9147 | absolutetime_to_microtime(abstime: now, secs: &assertOnWakeSecs, microsecs: µsecs); |
| 9148 | if (assertOnWakeReport) { |
| 9149 | HISTREPORT_TALLYVALUE(assertOnWakeReport, (int64_t)assertOnWakeSecs); |
| 9150 | DLOG("Updated assertOnWake %lu\n" , (unsigned long)assertOnWakeSecs); |
| 9151 | } |
| 9152 | } |
| 9153 | } |
| 9154 | |
| 9155 | if (changedBits & NO_IDLE_SLEEP_ASSERTIONS_MASK) { |
| 9156 | if ((newAssertions & NO_IDLE_SLEEP_ASSERTIONS_MASK) != 0) { |
| 9157 | if ((oldAssertions & NO_IDLE_SLEEP_ASSERTIONS_MASK) == 0) { |
| 9158 | DLOG("PreventIdleSleep driver assertion raised\n" ); |
| 9159 | bool ok = updatePreventIdleSleepList(service: this, addNotRemove: true); |
| 9160 | if (ok && (changedBits & kIOPMDriverAssertionPreventSystemIdleSleepBit)) { |
| 9161 | // Cancel idle sleep if there is one in progress |
| 9162 | cancelIdlePowerDown(service: this); |
| 9163 | } |
| 9164 | } |
| 9165 | } else { |
| 9166 | DLOG("PreventIdleSleep driver assertion dropped\n" ); |
| 9167 | updatePreventIdleSleepList(service: this, addNotRemove: false); |
| 9168 | } |
| 9169 | } |
| 9170 | } |
| 9171 | |
| 9172 | // MARK: - |
| 9173 | // MARK: Statistics |
| 9174 | |
| 9175 | //****************************************************************************** |
| 9176 | // pmStats |
| 9177 | // |
| 9178 | //****************************************************************************** |
| 9179 | |
| 9180 | void |
| 9181 | IOPMrootDomain::pmStatsRecordEvent( |
| 9182 | int eventIndex, |
| 9183 | AbsoluteTime timestamp) |
| 9184 | { |
| 9185 | bool starting = eventIndex & kIOPMStatsEventStartFlag ? true:false; |
| 9186 | bool stopping = eventIndex & kIOPMStatsEventStopFlag ? true:false; |
| 9187 | uint64_t delta; |
| 9188 | uint64_t nsec; |
| 9189 | OSSharedPtr<OSData> publishPMStats; |
| 9190 | |
| 9191 | eventIndex &= ~(kIOPMStatsEventStartFlag | kIOPMStatsEventStopFlag); |
| 9192 | |
| 9193 | absolutetime_to_nanoseconds(abstime: timestamp, result: &nsec); |
| 9194 | |
| 9195 | switch (eventIndex) { |
| 9196 | case kIOPMStatsHibernateImageWrite: |
| 9197 | if (starting) { |
| 9198 | gPMStats.hibWrite.start = nsec; |
| 9199 | } else if (stopping) { |
| 9200 | gPMStats.hibWrite.stop = nsec; |
| 9201 | } |
| 9202 | |
| 9203 | if (stopping) { |
| 9204 | delta = gPMStats.hibWrite.stop - gPMStats.hibWrite.start; |
| 9205 | IOLog(format: "PMStats: Hibernate write took %qd ms\n" , delta / NSEC_PER_MSEC); |
| 9206 | } |
| 9207 | break; |
| 9208 | case kIOPMStatsHibernateImageRead: |
| 9209 | if (starting) { |
| 9210 | gPMStats.hibRead.start = nsec; |
| 9211 | } else if (stopping) { |
| 9212 | gPMStats.hibRead.stop = nsec; |
| 9213 | } |
| 9214 | |
| 9215 | if (stopping) { |
| 9216 | delta = gPMStats.hibRead.stop - gPMStats.hibRead.start; |
| 9217 | IOLog(format: "PMStats: Hibernate read took %qd ms\n" , delta / NSEC_PER_MSEC); |
| 9218 | |
| 9219 | publishPMStats = OSData::withValue(value: gPMStats); |
| 9220 | setProperty(kIOPMSleepStatisticsKey, anObject: publishPMStats.get()); |
| 9221 | bzero(s: &gPMStats, n: sizeof(gPMStats)); |
| 9222 | } |
| 9223 | break; |
| 9224 | } |
| 9225 | } |
| 9226 | |
| 9227 | /* |
| 9228 | * Appends a record of the application response to |
| 9229 | * IOPMrootDomain::pmStatsAppResponses |
| 9230 | */ |
| 9231 | void |
| 9232 | IOPMrootDomain::pmStatsRecordApplicationResponse( |
| 9233 | const OSSymbol *response, |
| 9234 | const char *name, |
| 9235 | int messageType, |
| 9236 | uint32_t delay_ms, |
| 9237 | uint64_t id, |
| 9238 | OSObject *object, |
| 9239 | IOPMPowerStateIndex powerState, |
| 9240 | bool async) |
| 9241 | { |
| 9242 | OSSharedPtr<OSDictionary> responseDescription; |
| 9243 | OSSharedPtr<OSNumber> delayNum; |
| 9244 | OSSharedPtr<OSNumber> powerCaps; |
| 9245 | OSSharedPtr<OSNumber> pidNum; |
| 9246 | OSSharedPtr<OSNumber> msgNum; |
| 9247 | OSSharedPtr<const OSSymbol> appname; |
| 9248 | OSSharedPtr<const OSSymbol> sleep; |
| 9249 | OSSharedPtr<const OSSymbol> wake; |
| 9250 | IOPMServiceInterestNotifier *notify = NULL; |
| 9251 | |
| 9252 | if (object && (notify = OSDynamicCast(IOPMServiceInterestNotifier, object))) { |
| 9253 | if (response->isEqualTo(aSymbol: gIOPMStatsResponseTimedOut.get())) { |
| 9254 | notify->ackTimeoutCnt++; |
| 9255 | } else { |
| 9256 | notify->ackTimeoutCnt = 0; |
| 9257 | } |
| 9258 | } |
| 9259 | |
| 9260 | if (response->isEqualTo(aSymbol: gIOPMStatsResponsePrompt.get()) || |
| 9261 | (_systemTransitionType == kSystemTransitionNone) || (_systemTransitionType == kSystemTransitionNewCapClient)) { |
| 9262 | return; |
| 9263 | } |
| 9264 | |
| 9265 | |
| 9266 | if (response->isEqualTo(aSymbol: gIOPMStatsDriverPSChangeSlow.get())) { |
| 9267 | kdebugTrace(event: kPMLogDrvPSChangeDelay, regId: id, param1: messageType, param2: delay_ms); |
| 9268 | } else if (notify) { |
| 9269 | // User space app or kernel capability client |
| 9270 | if (id) { |
| 9271 | kdebugTrace(event: kPMLogAppResponseDelay, regId: id, param1: notify->msgType, param2: delay_ms); |
| 9272 | } else { |
| 9273 | kdebugTrace(event: kPMLogDrvResponseDelay, regId: notify->uuid0, param1: messageType, param2: delay_ms); |
| 9274 | } |
| 9275 | notify->msgType = 0; |
| 9276 | } |
| 9277 | |
| 9278 | responseDescription = OSDictionary::withCapacity(capacity: 5); |
| 9279 | if (responseDescription) { |
| 9280 | if (response) { |
| 9281 | responseDescription->setObject(aKey: _statsResponseTypeKey.get(), anObject: response); |
| 9282 | } |
| 9283 | |
| 9284 | msgNum = OSNumber::withNumber(value: messageType, numberOfBits: 32); |
| 9285 | if (msgNum) { |
| 9286 | responseDescription->setObject(aKey: _statsMessageTypeKey.get(), anObject: msgNum.get()); |
| 9287 | } |
| 9288 | |
| 9289 | if (!name && notify && notify->identifier) { |
| 9290 | name = notify->identifier->getCStringNoCopy(); |
| 9291 | } |
| 9292 | |
| 9293 | if (name && (strlen(s: name) > 0)) { |
| 9294 | appname = OSSymbol::withCString(cString: name); |
| 9295 | if (appname) { |
| 9296 | responseDescription->setObject(aKey: _statsNameKey.get(), anObject: appname.get()); |
| 9297 | } |
| 9298 | } |
| 9299 | |
| 9300 | if (!id && notify) { |
| 9301 | id = notify->uuid0; |
| 9302 | } |
| 9303 | pidNum = OSNumber::withNumber(value: id, numberOfBits: 64); |
| 9304 | if (pidNum) { |
| 9305 | responseDescription->setObject(aKey: _statsPIDKey.get(), anObject: pidNum.get()); |
| 9306 | } |
| 9307 | |
| 9308 | delayNum = OSNumber::withNumber(value: delay_ms, numberOfBits: 32); |
| 9309 | if (delayNum) { |
| 9310 | responseDescription->setObject(aKey: _statsTimeMSKey.get(), anObject: delayNum.get()); |
| 9311 | } |
| 9312 | |
| 9313 | if (response->isEqualTo(aSymbol: gIOPMStatsDriverPSChangeSlow.get())) { |
| 9314 | powerCaps = OSNumber::withNumber(value: powerState, numberOfBits: 32); |
| 9315 | |
| 9316 | #if !defined(__i386__) && !defined(__x86_64__) && (DEVELOPMENT || DEBUG) |
| 9317 | static const char * driverCallTypes[] = { |
| 9318 | [kDriverCallInformPreChange] = "powerStateWillChangeTo" , |
| 9319 | [kDriverCallInformPostChange] = "powerStateDidChangeTo" , |
| 9320 | [kDriverCallSetPowerState] = "setPowerState" |
| 9321 | }; |
| 9322 | |
| 9323 | if (messageType < (sizeof(driverCallTypes) / sizeof(driverCallTypes[0]))) { |
| 9324 | DLOG("%s[0x%qx]::%s(%u) %stook %d ms\n" , |
| 9325 | name, id, driverCallTypes[messageType], (uint32_t) powerState, |
| 9326 | async ? "async " : "" , delay_ms); |
| 9327 | } |
| 9328 | #endif |
| 9329 | } else { |
| 9330 | powerCaps = OSNumber::withNumber(value: _pendingCapability, numberOfBits: 32); |
| 9331 | } |
| 9332 | if (powerCaps) { |
| 9333 | responseDescription->setObject(aKey: _statsPowerCapsKey.get(), anObject: powerCaps.get()); |
| 9334 | } |
| 9335 | |
| 9336 | sleep = OSSymbol::withCString(cString: "Sleep" ); |
| 9337 | wake = OSSymbol::withCString(cString: "Wake" ); |
| 9338 | if (_systemTransitionType == kSystemTransitionSleep) { |
| 9339 | responseDescription->setObject(kIOPMStatsSystemTransitionKey, anObject: sleep.get()); |
| 9340 | } else if (_systemTransitionType == kSystemTransitionWake) { |
| 9341 | responseDescription->setObject(kIOPMStatsSystemTransitionKey, anObject: wake.get()); |
| 9342 | } else if (_systemTransitionType == kSystemTransitionCapability) { |
| 9343 | if (CAP_LOSS(kIOPMSystemCapabilityGraphics)) { |
| 9344 | responseDescription->setObject(kIOPMStatsSystemTransitionKey, anObject: sleep.get()); |
| 9345 | } else if (CAP_GAIN(kIOPMSystemCapabilityGraphics)) { |
| 9346 | responseDescription->setObject(kIOPMStatsSystemTransitionKey, anObject: wake.get()); |
| 9347 | } |
| 9348 | } |
| 9349 | |
| 9350 | IOLockLock(pmStatsLock); |
| 9351 | if (pmStatsAppResponses && pmStatsAppResponses->getCount() < 50) { |
| 9352 | pmStatsAppResponses->setObject(responseDescription.get()); |
| 9353 | } |
| 9354 | IOLockUnlock(pmStatsLock); |
| 9355 | } |
| 9356 | |
| 9357 | return; |
| 9358 | } |
| 9359 | |
| 9360 | // MARK: - |
| 9361 | // MARK: PMTraceWorker |
| 9362 | |
| 9363 | //****************************************************************************** |
| 9364 | // TracePoint support |
| 9365 | // |
| 9366 | //****************************************************************************** |
| 9367 | |
| 9368 | #define kIOPMRegisterNVRAMTracePointHandlerKey \ |
| 9369 | "IOPMRegisterNVRAMTracePointHandler" |
| 9370 | |
| 9371 | IOReturn |
| 9372 | IOPMrootDomain::callPlatformFunction( |
| 9373 | const OSSymbol * functionName, |
| 9374 | bool waitForFunction, |
| 9375 | void * param1, void * param2, |
| 9376 | void * param3, void * param4 ) |
| 9377 | { |
| 9378 | if (pmTracer && functionName && |
| 9379 | functionName->isEqualTo(kIOPMRegisterNVRAMTracePointHandlerKey) && |
| 9380 | !pmTracer->tracePointHandler && !pmTracer->tracePointTarget) { |
| 9381 | uint32_t tracePointPhases, tracePointPCI; |
| 9382 | uint64_t statusCode; |
| 9383 | |
| 9384 | pmTracer->tracePointHandler = (IOPMTracePointHandler) param1; |
| 9385 | pmTracer->tracePointTarget = (void *) param2; |
| 9386 | tracePointPCI = (uint32_t)(uintptr_t) param3; |
| 9387 | tracePointPhases = (uint32_t)(uintptr_t) param4; |
| 9388 | if ((tracePointPhases & 0xff) == kIOPMTracePointSystemSleep) { |
| 9389 | OSSharedPtr<IORegistryEntry> node = IORegistryEntry::fromPath( path: "/chosen" , plane: gIODTPlane ); |
| 9390 | if (node) { |
| 9391 | OSSharedPtr<OSObject> bootRomFailureProp; |
| 9392 | bootRomFailureProp = node->copyProperty(kIOEFIBootRomFailureKey); |
| 9393 | OSData *data = OSDynamicCast(OSData, bootRomFailureProp.get()); |
| 9394 | uint32_t bootFailureCode; |
| 9395 | if (data && data->getLength() == sizeof(bootFailureCode)) { |
| 9396 | // Failure code from EFI/BootRom is a four byte structure |
| 9397 | memcpy(dst: &bootFailureCode, src: data->getBytesNoCopy(), n: sizeof(bootFailureCode)); |
| 9398 | tracePointPCI = OSSwapBigToHostInt32(bootFailureCode); |
| 9399 | } |
| 9400 | } |
| 9401 | } |
| 9402 | statusCode = (((uint64_t)tracePointPCI) << 32) | tracePointPhases; |
| 9403 | if ((tracePointPhases & 0xff) != kIOPMTracePointSystemUp) { |
| 9404 | MSG("Sleep failure code 0x%08x 0x%08x\n" , |
| 9405 | tracePointPCI, tracePointPhases); |
| 9406 | } |
| 9407 | setProperty(kIOPMSleepWakeFailureCodeKey, aValue: statusCode, aNumberOfBits: 64); |
| 9408 | pmTracer->tracePointHandler( pmTracer->tracePointTarget, 0, 0 ); |
| 9409 | |
| 9410 | return kIOReturnSuccess; |
| 9411 | } |
| 9412 | #if HIBERNATION |
| 9413 | else if (functionName && |
| 9414 | functionName->isEqualTo(kIOPMInstallSystemSleepPolicyHandlerKey)) { |
| 9415 | if (gSleepPolicyHandler) { |
| 9416 | return kIOReturnExclusiveAccess; |
| 9417 | } |
| 9418 | if (!param1) { |
| 9419 | return kIOReturnBadArgument; |
| 9420 | } |
| 9421 | gSleepPolicyHandler = (IOPMSystemSleepPolicyHandler) param1; |
| 9422 | gSleepPolicyTarget = (void *) param2; |
| 9423 | setProperty("IOPMSystemSleepPolicyHandler" , kOSBooleanTrue); |
| 9424 | return kIOReturnSuccess; |
| 9425 | } |
| 9426 | #endif |
| 9427 | |
| 9428 | return super::callPlatformFunction( |
| 9429 | functionName, waitForFunction, param1, param2, param3, param4); |
| 9430 | } |
| 9431 | |
| 9432 | void |
| 9433 | IOPMrootDomain::kdebugTrace(uint32_t event, uint64_t id, |
| 9434 | uintptr_t param1, uintptr_t param2, uintptr_t param3) |
| 9435 | { |
| 9436 | uint32_t code = IODBG_POWER(event); |
| 9437 | uint64_t regId = id; |
| 9438 | if (regId == 0) { |
| 9439 | regId = getRegistryEntryID(); |
| 9440 | } |
| 9441 | KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, code, (uintptr_t) regId, param1, param2, param3, 0); |
| 9442 | } |
| 9443 | |
| 9444 | void |
| 9445 | IOPMrootDomain::tracePoint( uint8_t point ) |
| 9446 | { |
| 9447 | if (systemBooting) { |
| 9448 | return; |
| 9449 | } |
| 9450 | |
| 9451 | if (kIOPMTracePointWakeCapabilityClients == point) { |
| 9452 | acceptSystemWakeEvents(control: kAcceptSystemWakeEvents_Disable); |
| 9453 | } |
| 9454 | |
| 9455 | kdebugTrace(event: kPMLogSleepWakeTracePoint, id: 0, param1: point, param2: 0); |
| 9456 | pmTracer->tracePoint(phase: point); |
| 9457 | } |
| 9458 | |
| 9459 | static void |
| 9460 | kext_log_putc(char c) |
| 9461 | { |
| 9462 | if (gKextNameEnd || gKextNamePos >= (sizeof(gKextNameBuf) - 1)) { |
| 9463 | return; |
| 9464 | } |
| 9465 | if (c == '(' || c == '[' || c == ' ') { |
| 9466 | c = 0; |
| 9467 | gKextNameEnd = true; |
| 9468 | } |
| 9469 | |
| 9470 | gKextNameBuf[gKextNamePos++] = c; |
| 9471 | } |
| 9472 | |
| 9473 | static int |
| 9474 | kext_log(const char *fmt, ...) |
| 9475 | { |
| 9476 | va_list listp; |
| 9477 | |
| 9478 | va_start(listp, fmt); |
| 9479 | _doprnt(format: fmt, arg: &listp, lputc: &kext_log_putc, radix: 16); |
| 9480 | va_end(listp); |
| 9481 | |
| 9482 | return 0; |
| 9483 | } |
| 9484 | |
| 9485 | static OSPtr<const OSSymbol> |
| 9486 | copyKextIdentifierWithAddress(vm_address_t address) |
| 9487 | { |
| 9488 | OSSharedPtr<const OSSymbol> identifer; |
| 9489 | |
| 9490 | IOLockLock(gHaltLogLock); |
| 9491 | |
| 9492 | gKextNameEnd = false; |
| 9493 | gKextNamePos = 0; |
| 9494 | gKextNameBuf[0] = 0; |
| 9495 | |
| 9496 | OSKext::printKextsInBacktrace(addr: &address, cnt: 1, printf_func: kext_log, flags: OSKext::kPrintKextsLock | OSKext::kPrintKextsTerse); |
| 9497 | gKextNameBuf[sizeof(gKextNameBuf) - 1] = 0; |
| 9498 | identifer = OSSymbol::withCString(cString: (gKextNameBuf[0] != 0) ? gKextNameBuf : kOSKextKernelIdentifier); |
| 9499 | |
| 9500 | IOLockUnlock(gHaltLogLock); |
| 9501 | |
| 9502 | return identifer; |
| 9503 | } |
| 9504 | |
| 9505 | // Caller serialized using PM workloop |
| 9506 | const char * |
| 9507 | IOPMrootDomain::getNotificationClientName(OSObject *object) |
| 9508 | { |
| 9509 | IOPMServiceInterestNotifier *notifier = (typeof(notifier))object; |
| 9510 | const char *clientName = "UNKNOWN" ; |
| 9511 | |
| 9512 | if (!notifier->clientName) { |
| 9513 | // Check for user client |
| 9514 | if (systemCapabilityNotifier && (((IOPMServiceInterestNotifier *) systemCapabilityNotifier.get())->handler == notifier->handler)) { |
| 9515 | OSNumber *clientID = NULL; |
| 9516 | messageClient(kIOMessageCopyClientID, client: object, messageArgument: &clientID); |
| 9517 | if (clientID) { |
| 9518 | OSSharedPtr<OSString> string(IOCopyLogNameForPID(pid: clientID->unsigned32BitValue()), OSNoRetain); |
| 9519 | if (string) { |
| 9520 | notifier->clientName = OSSymbol::withString(aString: string.get()); |
| 9521 | } |
| 9522 | clientID->release(); |
| 9523 | } |
| 9524 | } else if (notifier->identifier) { |
| 9525 | notifier->clientName.reset(p: notifier->identifier.get(), OSRetain); |
| 9526 | } |
| 9527 | } |
| 9528 | |
| 9529 | if (notifier->clientName) { |
| 9530 | clientName = notifier->clientName->getCStringNoCopy(); |
| 9531 | } |
| 9532 | |
| 9533 | return clientName; |
| 9534 | } |
| 9535 | |
| 9536 | void |
| 9537 | IOPMrootDomain::traceNotification(OSObject *object, bool start, uint64_t timestamp, uint32_t msgIndex) |
| 9538 | { |
| 9539 | IOPMServiceInterestNotifier *notifier; |
| 9540 | |
| 9541 | if (systemBooting) { |
| 9542 | return; |
| 9543 | } |
| 9544 | notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); |
| 9545 | if (!notifier) { |
| 9546 | return; |
| 9547 | } |
| 9548 | |
| 9549 | if (start) { |
| 9550 | pmTracer->traceDetail(detail: notifier->uuid0 >> 32); |
| 9551 | kdebugTrace(event: kPMLogSleepWakeMessage, id: pmTracer->getTracePhase(), |
| 9552 | param1: (uintptr_t) notifier->msgType, param2: (uintptr_t) notifier->uuid0, param3: (uintptr_t) notifier->uuid1); |
| 9553 | |
| 9554 | // Update notifier state used for response/ack logging |
| 9555 | notifier->msgIndex = msgIndex; |
| 9556 | notifier->msgAbsTime = timestamp; |
| 9557 | |
| 9558 | if (msgIndex != UINT_MAX) { |
| 9559 | DLOG("%s[%u] to %s\n" , getIOMessageString(notifier->msgType), msgIndex, getNotificationClientName(notifier)); |
| 9560 | } else { |
| 9561 | DLOG("%s to %s\n" , getIOMessageString(notifier->msgType), getNotificationClientName(notifier)); |
| 9562 | } |
| 9563 | |
| 9564 | assert(notifierObject == NULL); |
| 9565 | notifierThread = current_thread(); |
| 9566 | notifierObject.reset(p: notifier, OSRetain); |
| 9567 | } else { |
| 9568 | uint64_t nsec; |
| 9569 | uint32_t delayMS; |
| 9570 | |
| 9571 | SUB_ABSOLUTETIME(×tamp, ¬ifier->msgAbsTime); |
| 9572 | absolutetime_to_nanoseconds(abstime: timestamp, result: &nsec); |
| 9573 | delayMS = (uint32_t)(nsec / 1000000ULL); |
| 9574 | if (delayMS > notifier->maxMsgDelayMS) { |
| 9575 | notifier->maxMsgDelayMS = delayMS; |
| 9576 | } |
| 9577 | |
| 9578 | assert(notifierObject == notifier); |
| 9579 | notifierObject.reset(); |
| 9580 | notifierThread = NULL; |
| 9581 | } |
| 9582 | } |
| 9583 | |
| 9584 | void |
| 9585 | IOPMrootDomain::traceNotificationAck(OSObject *object, uint32_t delay_ms) |
| 9586 | { |
| 9587 | if (systemBooting) { |
| 9588 | return; |
| 9589 | } |
| 9590 | IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); |
| 9591 | if (!notifier) { |
| 9592 | return; |
| 9593 | } |
| 9594 | |
| 9595 | kdebugTrace(event: kPMLogDrvResponseDelay, id: notifier->uuid0, |
| 9596 | param1: (uintptr_t) notifier->uuid1, param2: (uintptr_t) 0, param3: (uintptr_t) delay_ms); |
| 9597 | |
| 9598 | DLOG("%s[%u] ack from %s took %d ms\n" , |
| 9599 | getIOMessageString(notifier->msgType), notifier->msgIndex, getNotificationClientName(notifier), delay_ms); |
| 9600 | if (delay_ms > notifier->maxAckDelayMS) { |
| 9601 | notifier->maxAckDelayMS = delay_ms; |
| 9602 | } |
| 9603 | } |
| 9604 | |
| 9605 | void |
| 9606 | IOPMrootDomain::traceNotificationResponse(OSObject *object, uint32_t delay_ms, uint32_t ack_time_us) |
| 9607 | { |
| 9608 | if (systemBooting) { |
| 9609 | return; |
| 9610 | } |
| 9611 | IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); |
| 9612 | if (!notifier) { |
| 9613 | return; |
| 9614 | } |
| 9615 | |
| 9616 | kdebugTrace(event: kPMLogDrvResponseDelay, id: notifier->uuid0, |
| 9617 | param1: (uintptr_t) notifier->uuid1, param2: (uintptr_t)(ack_time_us / 1000), param3: (uintptr_t) delay_ms); |
| 9618 | |
| 9619 | if (ack_time_us == 0) { |
| 9620 | // Client work is done and ack will not be forthcoming |
| 9621 | DLOG("%s[%u] response from %s took %d ms\n" , |
| 9622 | getIOMessageString(notifier->msgType), notifier->msgIndex, getNotificationClientName(notifier), delay_ms); |
| 9623 | } else { |
| 9624 | // Client needs more time and it must ack within ack_time_us |
| 9625 | DLOG("%s[%u] response from %s took %d ms (ack in %d us)\n" , |
| 9626 | getIOMessageString(notifier->msgType), notifier->msgIndex, getNotificationClientName(notifier), delay_ms, ack_time_us); |
| 9627 | } |
| 9628 | } |
| 9629 | |
| 9630 | void |
| 9631 | IOPMrootDomain::traceFilteredNotification(OSObject *object) |
| 9632 | { |
| 9633 | if ((kIOLogDebugPower & gIOKitDebug) == 0) { |
| 9634 | return; |
| 9635 | } |
| 9636 | if (systemBooting) { |
| 9637 | return; |
| 9638 | } |
| 9639 | IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, object); |
| 9640 | if (!notifier) { |
| 9641 | return; |
| 9642 | } |
| 9643 | |
| 9644 | DLOG("%s to %s dropped\n" , getIOMessageString(notifier->msgType), getNotificationClientName(notifier)); |
| 9645 | } |
| 9646 | |
| 9647 | void |
| 9648 | IOPMrootDomain::traceDetail(uint32_t msgType, uint32_t msgIndex, uint32_t delay) |
| 9649 | { |
| 9650 | if (!systemBooting) { |
| 9651 | uint32_t detail = ((msgType & 0xffff) << 16) | (delay & 0xffff); |
| 9652 | pmTracer->traceDetail( detail ); |
| 9653 | kdebugTrace(event: kPMLogSleepWakeTracePoint, id: pmTracer->getTracePhase(), param1: msgType, param2: delay); |
| 9654 | DLOG("trace point 0x%02x msgType 0x%x detail 0x%08x\n" , pmTracer->getTracePhase(), msgType, delay); |
| 9655 | } |
| 9656 | } |
| 9657 | |
| 9658 | void |
| 9659 | IOPMrootDomain::configureReportGated(uint64_t channel_id, uint64_t action, void *result) |
| 9660 | { |
| 9661 | size_t reportSize; |
| 9662 | void **report = NULL; |
| 9663 | uint32_t bktCnt; |
| 9664 | uint32_t bktSize; |
| 9665 | uint32_t *clientCnt; |
| 9666 | |
| 9667 | ASSERT_GATED(); |
| 9668 | |
| 9669 | report = NULL; |
| 9670 | if (channel_id == kAssertDelayChID) { |
| 9671 | report = &assertOnWakeReport; |
| 9672 | bktCnt = kAssertDelayBcktCnt; |
| 9673 | bktSize = kAssertDelayBcktSize; |
| 9674 | clientCnt = &assertOnWakeClientCnt; |
| 9675 | } else if (channel_id == kSleepDelaysChID) { |
| 9676 | report = &sleepDelaysReport; |
| 9677 | bktCnt = kSleepDelaysBcktCnt; |
| 9678 | bktSize = kSleepDelaysBcktSize; |
| 9679 | clientCnt = &sleepDelaysClientCnt; |
| 9680 | } else { |
| 9681 | assert(false); |
| 9682 | return; |
| 9683 | } |
| 9684 | |
| 9685 | switch (action) { |
| 9686 | case kIOReportEnable: |
| 9687 | |
| 9688 | if (*report) { |
| 9689 | (*clientCnt)++; |
| 9690 | break; |
| 9691 | } |
| 9692 | |
| 9693 | reportSize = HISTREPORT_BUFSIZE(bktCnt); |
| 9694 | *report = IOMallocZeroData(reportSize); |
| 9695 | if (*report == NULL) { |
| 9696 | break; |
| 9697 | } |
| 9698 | HISTREPORT_INIT((uint16_t)bktCnt, bktSize, *report, reportSize, |
| 9699 | getRegistryEntryID(), channel_id, kIOReportCategoryPower); |
| 9700 | |
| 9701 | if (channel_id == kAssertDelayChID) { |
| 9702 | assertOnWakeSecs = 0; |
| 9703 | } |
| 9704 | |
| 9705 | break; |
| 9706 | |
| 9707 | case kIOReportDisable: |
| 9708 | if (*clientCnt == 0) { |
| 9709 | break; |
| 9710 | } |
| 9711 | if (*clientCnt == 1) { |
| 9712 | IOFreeData(address: *report, HISTREPORT_BUFSIZE(bktCnt)); |
| 9713 | *report = NULL; |
| 9714 | } |
| 9715 | (*clientCnt)--; |
| 9716 | |
| 9717 | if (channel_id == kAssertDelayChID) { |
| 9718 | assertOnWakeSecs = -1; // Invalid value to prevent updates |
| 9719 | } |
| 9720 | break; |
| 9721 | |
| 9722 | case kIOReportGetDimensions: |
| 9723 | if (*report) { |
| 9724 | HISTREPORT_UPDATERES(*report, kIOReportGetDimensions, result); |
| 9725 | } |
| 9726 | break; |
| 9727 | } |
| 9728 | |
| 9729 | return; |
| 9730 | } |
| 9731 | |
| 9732 | IOReturn |
| 9733 | IOPMrootDomain::configureReport(IOReportChannelList *channelList, |
| 9734 | IOReportConfigureAction action, |
| 9735 | void *result, |
| 9736 | void *destination) |
| 9737 | { |
| 9738 | unsigned cnt; |
| 9739 | uint64_t configAction = (uint64_t)action; |
| 9740 | |
| 9741 | for (cnt = 0; cnt < channelList->nchannels; cnt++) { |
| 9742 | if ((channelList->channels[cnt].channel_id == kSleepCntChID) || |
| 9743 | (channelList->channels[cnt].channel_id == kDarkWkCntChID) || |
| 9744 | (channelList->channels[cnt].channel_id == kUserWkCntChID)) { |
| 9745 | if (action != kIOReportGetDimensions) { |
| 9746 | continue; |
| 9747 | } |
| 9748 | SIMPLEREPORT_UPDATERES(kIOReportGetDimensions, result); |
| 9749 | } else if ((channelList->channels[cnt].channel_id == kAssertDelayChID) || |
| 9750 | (channelList->channels[cnt].channel_id == kSleepDelaysChID)) { |
| 9751 | gIOPMWorkLoop->runAction( |
| 9752 | OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::configureReportGated), |
| 9753 | target: (OSObject *)this, arg0: (void *)channelList->channels[cnt].channel_id, |
| 9754 | arg1: (void *)configAction, arg2: (void *)result); |
| 9755 | } |
| 9756 | } |
| 9757 | |
| 9758 | return super::configureReport(channels: channelList, action, result, destination); |
| 9759 | } |
| 9760 | |
| 9761 | IOReturn |
| 9762 | IOPMrootDomain::updateReportGated(uint64_t ch_id, void *result, IOBufferMemoryDescriptor *dest) |
| 9763 | { |
| 9764 | uint32_t size2cpy; |
| 9765 | void *data2cpy; |
| 9766 | void **report; |
| 9767 | |
| 9768 | ASSERT_GATED(); |
| 9769 | |
| 9770 | report = NULL; |
| 9771 | if (ch_id == kAssertDelayChID) { |
| 9772 | report = &assertOnWakeReport; |
| 9773 | } else if (ch_id == kSleepDelaysChID) { |
| 9774 | report = &sleepDelaysReport; |
| 9775 | } else { |
| 9776 | assert(false); |
| 9777 | return kIOReturnBadArgument; |
| 9778 | } |
| 9779 | |
| 9780 | if (*report == NULL) { |
| 9781 | return kIOReturnNotOpen; |
| 9782 | } |
| 9783 | |
| 9784 | HISTREPORT_UPDATEPREP(*report, data2cpy, size2cpy); |
| 9785 | if (size2cpy > (dest->getCapacity() - dest->getLength())) { |
| 9786 | return kIOReturnOverrun; |
| 9787 | } |
| 9788 | |
| 9789 | HISTREPORT_UPDATERES(*report, kIOReportCopyChannelData, result); |
| 9790 | dest->appendBytes(bytes: data2cpy, withLength: size2cpy); |
| 9791 | |
| 9792 | return kIOReturnSuccess; |
| 9793 | } |
| 9794 | |
| 9795 | IOReturn |
| 9796 | IOPMrootDomain::updateReport(IOReportChannelList *channelList, |
| 9797 | IOReportUpdateAction action, |
| 9798 | void *result, |
| 9799 | void *destination) |
| 9800 | { |
| 9801 | uint32_t size2cpy; |
| 9802 | void *data2cpy; |
| 9803 | uint8_t buf[SIMPLEREPORT_BUFSIZE]; |
| 9804 | IOBufferMemoryDescriptor *dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination); |
| 9805 | unsigned cnt; |
| 9806 | uint64_t ch_id; |
| 9807 | |
| 9808 | if (action != kIOReportCopyChannelData) { |
| 9809 | goto exit; |
| 9810 | } |
| 9811 | |
| 9812 | for (cnt = 0; cnt < channelList->nchannels; cnt++) { |
| 9813 | ch_id = channelList->channels[cnt].channel_id; |
| 9814 | |
| 9815 | if ((ch_id == kAssertDelayChID) || (ch_id == kSleepDelaysChID)) { |
| 9816 | gIOPMWorkLoop->runAction( |
| 9817 | OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::updateReportGated), |
| 9818 | target: (OSObject *)this, arg0: (void *)ch_id, |
| 9819 | arg1: (void *)result, arg2: (void *)dest); |
| 9820 | continue; |
| 9821 | } else if ((ch_id == kSleepCntChID) || |
| 9822 | (ch_id == kDarkWkCntChID) || (ch_id == kUserWkCntChID)) { |
| 9823 | SIMPLEREPORT_INIT(buf, sizeof(buf), getRegistryEntryID(), ch_id, kIOReportCategoryPower); |
| 9824 | } else { |
| 9825 | continue; |
| 9826 | } |
| 9827 | |
| 9828 | if (ch_id == kSleepCntChID) { |
| 9829 | SIMPLEREPORT_SETVALUE(buf, sleepCnt); |
| 9830 | } else if (ch_id == kDarkWkCntChID) { |
| 9831 | SIMPLEREPORT_SETVALUE(buf, darkWakeCnt); |
| 9832 | } else if (ch_id == kUserWkCntChID) { |
| 9833 | SIMPLEREPORT_SETVALUE(buf, displayWakeCnt); |
| 9834 | } |
| 9835 | |
| 9836 | SIMPLEREPORT_UPDATEPREP(buf, data2cpy, size2cpy); |
| 9837 | SIMPLEREPORT_UPDATERES(kIOReportCopyChannelData, result); |
| 9838 | dest->appendBytes(bytes: data2cpy, withLength: size2cpy); |
| 9839 | } |
| 9840 | |
| 9841 | exit: |
| 9842 | return super::updateReport(channels: channelList, action, result, destination); |
| 9843 | } |
| 9844 | |
| 9845 | |
| 9846 | //****************************************************************************** |
| 9847 | // PMTraceWorker Class |
| 9848 | // |
| 9849 | //****************************************************************************** |
| 9850 | |
| 9851 | #undef super |
| 9852 | #define super OSObject |
| 9853 | OSDefineMetaClassAndStructors(PMTraceWorker, OSObject) |
| 9854 | |
| 9855 | #define kPMBestGuessPCIDevicesCount 25 |
| 9856 | #define kPMMaxRTCBitfieldSize 32 |
| 9857 | |
| 9858 | OSPtr<PMTraceWorker> |
| 9859 | PMTraceWorker::tracer(IOPMrootDomain * owner) |
| 9860 | { |
| 9861 | OSSharedPtr<PMTraceWorker> me = OSMakeShared<PMTraceWorker>(); |
| 9862 | if (!me || !me->init()) { |
| 9863 | return NULL; |
| 9864 | } |
| 9865 | |
| 9866 | DLOG("PMTraceWorker %p\n" , OBFUSCATE(me.get())); |
| 9867 | |
| 9868 | // Note that we cannot instantiate the PCI device -> bit mappings here, since |
| 9869 | // the IODeviceTree has not yet been created by IOPlatformExpert. We create |
| 9870 | // this dictionary lazily. |
| 9871 | me->owner = owner; |
| 9872 | me->pciDeviceBitMappings = NULL; |
| 9873 | me->pmTraceWorkerLock = IOLockAlloc(); |
| 9874 | me->tracePhase = kIOPMTracePointSystemUp; |
| 9875 | me->traceData32 = 0; |
| 9876 | me->loginWindowData = 0; |
| 9877 | me->coreDisplayData = 0; |
| 9878 | me->coreGraphicsData = 0; |
| 9879 | return me; |
| 9880 | } |
| 9881 | |
| 9882 | void |
| 9883 | PMTraceWorker::RTC_TRACE(void) |
| 9884 | { |
| 9885 | if (tracePointHandler && tracePointTarget) { |
| 9886 | uint32_t wordA; |
| 9887 | |
| 9888 | IOLockLock(pmTraceWorkerLock); |
| 9889 | wordA = (loginWindowData << 24) | (coreDisplayData << 16) | |
| 9890 | (coreGraphicsData << 8) | tracePhase; |
| 9891 | IOLockUnlock(pmTraceWorkerLock); |
| 9892 | |
| 9893 | tracePointHandler( tracePointTarget, traceData32, wordA ); |
| 9894 | _LOG("RTC_TRACE wrote 0x%08x 0x%08x\n" , traceData32, wordA); |
| 9895 | } |
| 9896 | #if DEVELOPMENT || DEBUG |
| 9897 | if ((swd_panic_phase != 0) && (swd_panic_phase == tracePhase)) { |
| 9898 | DEBUG_LOG("Causing sleep wake failure in phase 0x%08x\n" , tracePhase); |
| 9899 | IOLock *l = IOLockAlloc(); |
| 9900 | IOLockLock(l); |
| 9901 | IOLockLock(l); |
| 9902 | } |
| 9903 | #endif /* DEVELOPMENT || DEBUG */ |
| 9904 | } |
| 9905 | |
| 9906 | int |
| 9907 | PMTraceWorker::recordTopLevelPCIDevice(IOService * pciDevice) |
| 9908 | { |
| 9909 | OSSharedPtr<const OSSymbol> deviceName; |
| 9910 | int index = -1; |
| 9911 | |
| 9912 | IOLockLock(pmTraceWorkerLock); |
| 9913 | |
| 9914 | if (!pciDeviceBitMappings) { |
| 9915 | pciDeviceBitMappings = OSArray::withCapacity(kPMBestGuessPCIDevicesCount); |
| 9916 | if (!pciDeviceBitMappings) { |
| 9917 | goto exit; |
| 9918 | } |
| 9919 | } |
| 9920 | |
| 9921 | // Check for bitmask overflow. |
| 9922 | if (pciDeviceBitMappings->getCount() >= kPMMaxRTCBitfieldSize) { |
| 9923 | goto exit; |
| 9924 | } |
| 9925 | |
| 9926 | if ((deviceName = pciDevice->copyName()) && |
| 9927 | (pciDeviceBitMappings->getNextIndexOfObject(anObject: deviceName.get(), index: 0) == (unsigned int)-1) && |
| 9928 | pciDeviceBitMappings->setObject(deviceName.get())) { |
| 9929 | index = pciDeviceBitMappings->getCount() - 1; |
| 9930 | _LOG("PMTrace PCI array: set object %s => %d\n" , |
| 9931 | deviceName->getCStringNoCopy(), index); |
| 9932 | } |
| 9933 | |
| 9934 | if (!addedToRegistry && (index >= 0)) { |
| 9935 | addedToRegistry = owner->setProperty(aKey: "PCITopLevel" , anObject: this); |
| 9936 | } |
| 9937 | |
| 9938 | exit: |
| 9939 | IOLockUnlock(pmTraceWorkerLock); |
| 9940 | return index; |
| 9941 | } |
| 9942 | |
| 9943 | bool |
| 9944 | PMTraceWorker::serialize(OSSerialize *s) const |
| 9945 | { |
| 9946 | bool ok = false; |
| 9947 | if (pciDeviceBitMappings) { |
| 9948 | IOLockLock(pmTraceWorkerLock); |
| 9949 | ok = pciDeviceBitMappings->serialize(serializer: s); |
| 9950 | IOLockUnlock(pmTraceWorkerLock); |
| 9951 | } |
| 9952 | return ok; |
| 9953 | } |
| 9954 | |
| 9955 | void |
| 9956 | PMTraceWorker::tracePoint(uint8_t phase) |
| 9957 | { |
| 9958 | // clear trace detail when phase begins |
| 9959 | if (tracePhase != phase) { |
| 9960 | traceData32 = 0; |
| 9961 | } |
| 9962 | |
| 9963 | tracePhase = phase; |
| 9964 | |
| 9965 | DLOG("trace point 0x%02x\n" , tracePhase); |
| 9966 | RTC_TRACE(); |
| 9967 | } |
| 9968 | |
| 9969 | void |
| 9970 | PMTraceWorker::traceDetail(uint32_t detail) |
| 9971 | { |
| 9972 | if (detail == traceData32) { |
| 9973 | return; |
| 9974 | } |
| 9975 | traceData32 = detail; |
| 9976 | RTC_TRACE(); |
| 9977 | } |
| 9978 | |
| 9979 | void |
| 9980 | PMTraceWorker::traceComponentWakeProgress(uint32_t component, uint32_t data) |
| 9981 | { |
| 9982 | switch (component) { |
| 9983 | case kIOPMLoginWindowProgress: |
| 9984 | loginWindowData = data & kIOPMLoginWindowProgressMask; |
| 9985 | break; |
| 9986 | case kIOPMCoreDisplayProgress: |
| 9987 | coreDisplayData = data & kIOPMCoreDisplayProgressMask; |
| 9988 | break; |
| 9989 | case kIOPMCoreGraphicsProgress: |
| 9990 | coreGraphicsData = data & kIOPMCoreGraphicsProgressMask; |
| 9991 | break; |
| 9992 | default: |
| 9993 | return; |
| 9994 | } |
| 9995 | |
| 9996 | DLOG("component trace point 0x%02x data 0x%08x\n" , component, data); |
| 9997 | RTC_TRACE(); |
| 9998 | } |
| 9999 | |
| 10000 | void |
| 10001 | PMTraceWorker::tracePCIPowerChange( |
| 10002 | change_t type, IOService *service, uint32_t changeFlags, uint32_t bitNum) |
| 10003 | { |
| 10004 | uint32_t bitMask; |
| 10005 | uint32_t expectedFlag; |
| 10006 | |
| 10007 | // Ignore PCI changes outside of system sleep/wake. |
| 10008 | if ((kIOPMTracePointSleepPowerPlaneDrivers != tracePhase) && |
| 10009 | (kIOPMTracePointWakePowerPlaneDrivers != tracePhase)) { |
| 10010 | return; |
| 10011 | } |
| 10012 | |
| 10013 | // Only record the WillChange transition when going to sleep, |
| 10014 | // and the DidChange on the way up. |
| 10015 | changeFlags &= (kIOPMDomainWillChange | kIOPMDomainDidChange); |
| 10016 | expectedFlag = (kIOPMTracePointSleepPowerPlaneDrivers == tracePhase) ? |
| 10017 | kIOPMDomainWillChange : kIOPMDomainDidChange; |
| 10018 | if (changeFlags != expectedFlag) { |
| 10019 | return; |
| 10020 | } |
| 10021 | |
| 10022 | // Mark this device off in our bitfield |
| 10023 | if (bitNum < kPMMaxRTCBitfieldSize) { |
| 10024 | bitMask = (1 << bitNum); |
| 10025 | |
| 10026 | if (kPowerChangeStart == type) { |
| 10027 | traceData32 |= bitMask; |
| 10028 | _LOG("PMTrace: Device %s started - bit %2d mask 0x%08x => 0x%08x\n" , |
| 10029 | service->getName(), bitNum, bitMask, traceData32); |
| 10030 | owner->kdebugTrace(event: kPMLogPCIDevChangeStart, id: service->getRegistryEntryID(), param1: traceData32, param2: 0); |
| 10031 | } else { |
| 10032 | traceData32 &= ~bitMask; |
| 10033 | _LOG("PMTrace: Device %s finished - bit %2d mask 0x%08x => 0x%08x\n" , |
| 10034 | service->getName(), bitNum, bitMask, traceData32); |
| 10035 | owner->kdebugTrace(event: kPMLogPCIDevChangeDone, id: service->getRegistryEntryID(), param1: traceData32, param2: 0); |
| 10036 | } |
| 10037 | |
| 10038 | DLOG("trace point 0x%02x detail 0x%08x\n" , tracePhase, traceData32); |
| 10039 | RTC_TRACE(); |
| 10040 | } |
| 10041 | } |
| 10042 | |
| 10043 | uint64_t |
| 10044 | PMTraceWorker::getPMStatusCode() |
| 10045 | { |
| 10046 | return ((uint64_t)traceData32 << 32) | ((uint64_t)tracePhase); |
| 10047 | } |
| 10048 | |
| 10049 | uint8_t |
| 10050 | PMTraceWorker::getTracePhase() |
| 10051 | { |
| 10052 | return tracePhase; |
| 10053 | } |
| 10054 | |
| 10055 | uint32_t |
| 10056 | PMTraceWorker::getTraceData() |
| 10057 | { |
| 10058 | return traceData32; |
| 10059 | } |
| 10060 | |
| 10061 | // MARK: - |
| 10062 | // MARK: PMHaltWorker |
| 10063 | |
| 10064 | //****************************************************************************** |
| 10065 | // PMHaltWorker Class |
| 10066 | // |
| 10067 | //****************************************************************************** |
| 10068 | |
| 10069 | PMHaltWorker * |
| 10070 | PMHaltWorker::worker( void ) |
| 10071 | { |
| 10072 | PMHaltWorker * me; |
| 10073 | IOThread thread; |
| 10074 | |
| 10075 | do { |
| 10076 | me = OSTypeAlloc( PMHaltWorker ); |
| 10077 | if (!me || !me->init()) { |
| 10078 | break; |
| 10079 | } |
| 10080 | |
| 10081 | me->lock = IOLockAlloc(); |
| 10082 | if (!me->lock) { |
| 10083 | break; |
| 10084 | } |
| 10085 | |
| 10086 | DLOG("PMHaltWorker %p\n" , OBFUSCATE(me)); |
| 10087 | me->retain(); // thread holds extra retain |
| 10088 | if (KERN_SUCCESS != kernel_thread_start(continuation: &PMHaltWorker::main, parameter: (void *) me, new_thread: &thread)) { |
| 10089 | me->release(); |
| 10090 | break; |
| 10091 | } |
| 10092 | thread_deallocate(thread); |
| 10093 | return me; |
| 10094 | } while (false); |
| 10095 | |
| 10096 | if (me) { |
| 10097 | me->release(); |
| 10098 | } |
| 10099 | return NULL; |
| 10100 | } |
| 10101 | |
| 10102 | void |
| 10103 | PMHaltWorker::free( void ) |
| 10104 | { |
| 10105 | DLOG("PMHaltWorker free %p\n" , OBFUSCATE(this)); |
| 10106 | if (lock) { |
| 10107 | IOLockFree(lock); |
| 10108 | lock = NULL; |
| 10109 | } |
| 10110 | return OSObject::free(); |
| 10111 | } |
| 10112 | |
| 10113 | void |
| 10114 | PMHaltWorker::main( void * arg, wait_result_t waitResult ) |
| 10115 | { |
| 10116 | PMHaltWorker * me = (PMHaltWorker *) arg; |
| 10117 | |
| 10118 | IOLockLock( gPMHaltLock ); |
| 10119 | gPMHaltBusyCount++; |
| 10120 | me->depth = gPMHaltDepth; |
| 10121 | IOLockUnlock( gPMHaltLock ); |
| 10122 | |
| 10123 | while (me->depth >= 0) { |
| 10124 | PMHaltWorker::work( me ); |
| 10125 | |
| 10126 | IOLockLock( gPMHaltLock ); |
| 10127 | if (++gPMHaltIdleCount >= gPMHaltBusyCount) { |
| 10128 | // This is the last thread to finish work on this level, |
| 10129 | // inform everyone to start working on next lower level. |
| 10130 | gPMHaltDepth--; |
| 10131 | me->depth = gPMHaltDepth; |
| 10132 | gPMHaltIdleCount = 0; |
| 10133 | thread_wakeup((event_t) &gPMHaltIdleCount); |
| 10134 | } else { |
| 10135 | // One or more threads are still working on this level, |
| 10136 | // this thread must wait. |
| 10137 | me->depth = gPMHaltDepth - 1; |
| 10138 | do { |
| 10139 | IOLockSleep(lock: gPMHaltLock, event: &gPMHaltIdleCount, THREAD_UNINT); |
| 10140 | } while (me->depth != gPMHaltDepth); |
| 10141 | } |
| 10142 | IOLockUnlock( gPMHaltLock ); |
| 10143 | } |
| 10144 | |
| 10145 | // No more work to do, terminate thread |
| 10146 | DLOG("All done for worker: %p (visits = %u)\n" , OBFUSCATE(me), me->visits); |
| 10147 | thread_wakeup( &gPMHaltDepth ); |
| 10148 | me->release(); |
| 10149 | } |
| 10150 | |
| 10151 | void |
| 10152 | PMHaltWorker::work( PMHaltWorker * me ) |
| 10153 | { |
| 10154 | OSSharedPtr<IOService> service; |
| 10155 | OSSet * inner; |
| 10156 | AbsoluteTime startTime, elapsedTime; |
| 10157 | UInt32 deltaTime; |
| 10158 | bool timeout; |
| 10159 | |
| 10160 | while (true) { |
| 10161 | timeout = false; |
| 10162 | |
| 10163 | // Claim an unit of work from the shared pool |
| 10164 | IOLockLock( gPMHaltLock ); |
| 10165 | inner = (OSSet *)gPMHaltArray->getObject(index: me->depth); |
| 10166 | if (inner) { |
| 10167 | service.reset(OSDynamicCast(IOService, inner->getAnyObject()), OSRetain); |
| 10168 | if (service) { |
| 10169 | inner->removeObject(anObject: service.get()); |
| 10170 | } |
| 10171 | } |
| 10172 | IOLockUnlock( gPMHaltLock ); |
| 10173 | if (!service) { |
| 10174 | break; // no more work at this depth |
| 10175 | } |
| 10176 | clock_get_uptime(result: &startTime); |
| 10177 | |
| 10178 | if (!service->isInactive() && |
| 10179 | service->setProperty(aKey: gPMHaltClientAcknowledgeKey.get(), anObject: me)) { |
| 10180 | IOLockLock(me->lock); |
| 10181 | me->startTime = startTime; |
| 10182 | me->service = service.get(); |
| 10183 | me->timeout = false; |
| 10184 | IOLockUnlock(me->lock); |
| 10185 | |
| 10186 | service->systemWillShutdown( specifier: gPMHaltMessageType); |
| 10187 | |
| 10188 | // Wait for driver acknowledgement |
| 10189 | IOLockLock(me->lock); |
| 10190 | while (service->propertyExists(aKey: gPMHaltClientAcknowledgeKey.get())) { |
| 10191 | IOLockSleep(lock: me->lock, event: me, THREAD_UNINT); |
| 10192 | } |
| 10193 | me->service = NULL; |
| 10194 | timeout = me->timeout; |
| 10195 | IOLockUnlock(me->lock); |
| 10196 | } |
| 10197 | |
| 10198 | deltaTime = computeDeltaTimeMS(startTime: &startTime, elapsedTime: &elapsedTime); |
| 10199 | if ((deltaTime > kPMHaltTimeoutMS) || timeout) { |
| 10200 | LOG("%s driver %s (0x%llx) took %u ms\n" , |
| 10201 | (gPMHaltMessageType == kIOMessageSystemWillPowerOff) ? |
| 10202 | "PowerOff" : "Restart" , |
| 10203 | service->getName(), service->getRegistryEntryID(), |
| 10204 | (uint32_t) deltaTime ); |
| 10205 | halt_log_enter(what: "PowerOff/Restart handler completed" , |
| 10206 | OSMemberFunctionCast(const void *, service.get(), &IOService::systemWillShutdown), |
| 10207 | time: elapsedTime); |
| 10208 | } |
| 10209 | |
| 10210 | me->visits++; |
| 10211 | } |
| 10212 | } |
| 10213 | |
| 10214 | void |
| 10215 | PMHaltWorker::checkTimeout( PMHaltWorker * me, AbsoluteTime * now ) |
| 10216 | { |
| 10217 | UInt64 nano; |
| 10218 | AbsoluteTime startTime; |
| 10219 | AbsoluteTime endTime; |
| 10220 | |
| 10221 | endTime = *now; |
| 10222 | |
| 10223 | IOLockLock(me->lock); |
| 10224 | if (me->service && !me->timeout) { |
| 10225 | startTime = me->startTime; |
| 10226 | nano = 0; |
| 10227 | if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0) { |
| 10228 | SUB_ABSOLUTETIME(&endTime, &startTime); |
| 10229 | absolutetime_to_nanoseconds(abstime: endTime, result: &nano); |
| 10230 | } |
| 10231 | if (nano > 3000000000ULL) { |
| 10232 | me->timeout = true; |
| 10233 | |
| 10234 | halt_log_enter(what: "PowerOff/Restart still waiting on handler" , |
| 10235 | OSMemberFunctionCast(const void *, me->service, &IOService::systemWillShutdown), |
| 10236 | time: endTime); |
| 10237 | MSG("%s still waiting on %s\n" , |
| 10238 | (gPMHaltMessageType == kIOMessageSystemWillPowerOff) ? "PowerOff" : "Restart" , |
| 10239 | me->service->getName()); |
| 10240 | } |
| 10241 | } |
| 10242 | IOLockUnlock(me->lock); |
| 10243 | } |
| 10244 | |
| 10245 | //****************************************************************************** |
| 10246 | // acknowledgeSystemWillShutdown |
| 10247 | // |
| 10248 | // Acknowledgement from drivers that they have prepared for shutdown/restart. |
| 10249 | //****************************************************************************** |
| 10250 | |
| 10251 | void |
| 10252 | IOPMrootDomain::acknowledgeSystemWillShutdown( IOService * from ) |
| 10253 | { |
| 10254 | PMHaltWorker * worker; |
| 10255 | OSSharedPtr<OSObject> prop; |
| 10256 | |
| 10257 | if (!from) { |
| 10258 | return; |
| 10259 | } |
| 10260 | |
| 10261 | //DLOG("%s acknowledged\n", from->getName()); |
| 10262 | prop = from->copyProperty( aKey: gPMHaltClientAcknowledgeKey.get()); |
| 10263 | if (prop) { |
| 10264 | worker = (PMHaltWorker *) prop.get(); |
| 10265 | IOLockLock(worker->lock); |
| 10266 | from->removeProperty( aKey: gPMHaltClientAcknowledgeKey.get()); |
| 10267 | thread_wakeup((event_t) worker); |
| 10268 | IOLockUnlock(worker->lock); |
| 10269 | } else { |
| 10270 | DLOG("%s acknowledged without worker property\n" , |
| 10271 | from->getName()); |
| 10272 | } |
| 10273 | } |
| 10274 | |
| 10275 | |
| 10276 | //****************************************************************************** |
| 10277 | // notifySystemShutdown |
| 10278 | // |
| 10279 | // Notify all objects in PM tree that system will shutdown or restart |
| 10280 | //****************************************************************************** |
| 10281 | |
| 10282 | static void |
| 10283 | notifySystemShutdown( IOService * root, uint32_t messageType ) |
| 10284 | { |
| 10285 | #define PLACEHOLDER ((OSSet *)gPMHaltArray.get()) |
| 10286 | OSSharedPtr<IORegistryIterator> iter; |
| 10287 | IORegistryEntry * entry; |
| 10288 | IOService * node; |
| 10289 | OSSet * inner; |
| 10290 | OSSharedPtr<OSSet> newInner; |
| 10291 | PMHaltWorker * workers[kPMHaltMaxWorkers]; |
| 10292 | AbsoluteTime deadline; |
| 10293 | unsigned int totalNodes = 0; |
| 10294 | unsigned int depth; |
| 10295 | unsigned int rootDepth; |
| 10296 | unsigned int numWorkers; |
| 10297 | unsigned int count; |
| 10298 | int waitResult; |
| 10299 | void * baseFunc; |
| 10300 | bool ok; |
| 10301 | |
| 10302 | DLOG("%s msgType = 0x%x\n" , __FUNCTION__, messageType); |
| 10303 | |
| 10304 | baseFunc = OSMemberFunctionCast(void *, root, &IOService::systemWillShutdown); |
| 10305 | |
| 10306 | // Iterate the entire PM tree starting from root |
| 10307 | |
| 10308 | rootDepth = root->getDepth( plane: gIOPowerPlane ); |
| 10309 | if (!rootDepth) { |
| 10310 | goto done; |
| 10311 | } |
| 10312 | |
| 10313 | // debug - for repeated test runs |
| 10314 | while (PMHaltWorker::metaClass->getInstanceCount()) { |
| 10315 | IOSleep(milliseconds: 1); |
| 10316 | } |
| 10317 | |
| 10318 | if (!gPMHaltArray) { |
| 10319 | gPMHaltArray = OSArray::withCapacity(capacity: 40); |
| 10320 | if (!gPMHaltArray) { |
| 10321 | goto done; |
| 10322 | } |
| 10323 | } else { // debug |
| 10324 | gPMHaltArray->flushCollection(); |
| 10325 | } |
| 10326 | |
| 10327 | if (!gPMHaltLock) { |
| 10328 | gPMHaltLock = IOLockAlloc(); |
| 10329 | if (!gPMHaltLock) { |
| 10330 | goto done; |
| 10331 | } |
| 10332 | } |
| 10333 | |
| 10334 | if (!gPMHaltClientAcknowledgeKey) { |
| 10335 | gPMHaltClientAcknowledgeKey = |
| 10336 | OSSymbol::withCStringNoCopy(cString: "PMShutdown" ); |
| 10337 | if (!gPMHaltClientAcknowledgeKey) { |
| 10338 | goto done; |
| 10339 | } |
| 10340 | } |
| 10341 | |
| 10342 | gPMHaltMessageType = messageType; |
| 10343 | |
| 10344 | // Depth-first walk of PM plane |
| 10345 | |
| 10346 | iter = IORegistryIterator::iterateOver( |
| 10347 | start: root, plane: gIOPowerPlane, options: kIORegistryIterateRecursively); |
| 10348 | |
| 10349 | if (iter) { |
| 10350 | while ((entry = iter->getNextObject())) { |
| 10351 | node = OSDynamicCast(IOService, entry); |
| 10352 | if (!node) { |
| 10353 | continue; |
| 10354 | } |
| 10355 | |
| 10356 | if (baseFunc == |
| 10357 | OSMemberFunctionCast(void *, node, &IOService::systemWillShutdown)) { |
| 10358 | continue; |
| 10359 | } |
| 10360 | |
| 10361 | depth = node->getDepth( plane: gIOPowerPlane ); |
| 10362 | if (depth <= rootDepth) { |
| 10363 | continue; |
| 10364 | } |
| 10365 | |
| 10366 | ok = false; |
| 10367 | |
| 10368 | // adjust to zero based depth |
| 10369 | depth -= (rootDepth + 1); |
| 10370 | |
| 10371 | // gPMHaltArray is an array of containers, each container |
| 10372 | // refers to nodes with the same depth. |
| 10373 | |
| 10374 | count = gPMHaltArray->getCount(); |
| 10375 | while (depth >= count) { |
| 10376 | // expand array and insert placeholders |
| 10377 | gPMHaltArray->setObject(PLACEHOLDER); |
| 10378 | count++; |
| 10379 | } |
| 10380 | count = gPMHaltArray->getCount(); |
| 10381 | if (depth < count) { |
| 10382 | inner = (OSSet *)gPMHaltArray->getObject(index: depth); |
| 10383 | if (inner == PLACEHOLDER) { |
| 10384 | newInner = OSSet::withCapacity(capacity: 40); |
| 10385 | if (newInner) { |
| 10386 | gPMHaltArray->replaceObject(index: depth, anObject: newInner.get()); |
| 10387 | inner = newInner.get(); |
| 10388 | } |
| 10389 | } |
| 10390 | |
| 10391 | // PM nodes that appear more than once in the tree will have |
| 10392 | // the same depth, OSSet will refuse to add the node twice. |
| 10393 | if (inner) { |
| 10394 | ok = inner->setObject(node); |
| 10395 | } |
| 10396 | } |
| 10397 | if (!ok) { |
| 10398 | DLOG("Skipped PM node %s\n" , node->getName()); |
| 10399 | } |
| 10400 | } |
| 10401 | } |
| 10402 | |
| 10403 | // debug only |
| 10404 | for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(index: i)); i++) { |
| 10405 | count = 0; |
| 10406 | if (inner != PLACEHOLDER) { |
| 10407 | count = inner->getCount(); |
| 10408 | } |
| 10409 | DLOG("Nodes at depth %u = %u\n" , i, count); |
| 10410 | } |
| 10411 | |
| 10412 | // strip placeholders (not all depths are populated) |
| 10413 | numWorkers = 0; |
| 10414 | for (int i = 0; (inner = (OSSet *)gPMHaltArray->getObject(index: i));) { |
| 10415 | if (inner == PLACEHOLDER) { |
| 10416 | gPMHaltArray->removeObject(index: i); |
| 10417 | continue; |
| 10418 | } |
| 10419 | count = inner->getCount(); |
| 10420 | if (count > numWorkers) { |
| 10421 | numWorkers = count; |
| 10422 | } |
| 10423 | totalNodes += count; |
| 10424 | i++; |
| 10425 | } |
| 10426 | |
| 10427 | if (gPMHaltArray->getCount() == 0 || !numWorkers) { |
| 10428 | goto done; |
| 10429 | } |
| 10430 | |
| 10431 | gPMHaltBusyCount = 0; |
| 10432 | gPMHaltIdleCount = 0; |
| 10433 | gPMHaltDepth = gPMHaltArray->getCount() - 1; |
| 10434 | |
| 10435 | // Create multiple workers (and threads) |
| 10436 | |
| 10437 | if (numWorkers > kPMHaltMaxWorkers) { |
| 10438 | numWorkers = kPMHaltMaxWorkers; |
| 10439 | } |
| 10440 | |
| 10441 | DLOG("PM nodes %u, maxDepth %u, workers %u\n" , |
| 10442 | totalNodes, gPMHaltArray->getCount(), numWorkers); |
| 10443 | |
| 10444 | for (unsigned int i = 0; i < numWorkers; i++) { |
| 10445 | workers[i] = PMHaltWorker::worker(); |
| 10446 | } |
| 10447 | |
| 10448 | // Wait for workers to exhaust all available work |
| 10449 | |
| 10450 | IOLockLock(gPMHaltLock); |
| 10451 | while (gPMHaltDepth >= 0) { |
| 10452 | clock_interval_to_deadline(interval: 1000, scale_factor: kMillisecondScale, result: &deadline); |
| 10453 | |
| 10454 | waitResult = IOLockSleepDeadline( |
| 10455 | lock: gPMHaltLock, event: &gPMHaltDepth, deadline, THREAD_UNINT); |
| 10456 | if (THREAD_TIMED_OUT == waitResult) { |
| 10457 | AbsoluteTime now; |
| 10458 | clock_get_uptime(result: &now); |
| 10459 | |
| 10460 | IOLockUnlock(gPMHaltLock); |
| 10461 | for (unsigned int i = 0; i < numWorkers; i++) { |
| 10462 | if (workers[i]) { |
| 10463 | PMHaltWorker::checkTimeout(me: workers[i], now: &now); |
| 10464 | } |
| 10465 | } |
| 10466 | IOLockLock(gPMHaltLock); |
| 10467 | } |
| 10468 | } |
| 10469 | IOLockUnlock(gPMHaltLock); |
| 10470 | |
| 10471 | // Release all workers |
| 10472 | |
| 10473 | for (unsigned int i = 0; i < numWorkers; i++) { |
| 10474 | if (workers[i]) { |
| 10475 | workers[i]->release(); |
| 10476 | } |
| 10477 | // worker also retained by it's own thread |
| 10478 | } |
| 10479 | |
| 10480 | done: |
| 10481 | DLOG("%s done\n" , __FUNCTION__); |
| 10482 | return; |
| 10483 | } |
| 10484 | |
| 10485 | // MARK: - |
| 10486 | // MARK: Kernel Assertion |
| 10487 | |
| 10488 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 10489 | |
| 10490 | IOPMDriverAssertionID |
| 10491 | IOPMrootDomain::createPMAssertion( |
| 10492 | IOPMDriverAssertionType whichAssertionBits, |
| 10493 | IOPMDriverAssertionLevel assertionLevel, |
| 10494 | IOService *ownerService, |
| 10495 | const char *ownerDescription) |
| 10496 | { |
| 10497 | IOReturn ret; |
| 10498 | IOPMDriverAssertionID newAssertion; |
| 10499 | |
| 10500 | if (!pmAssertions) { |
| 10501 | return 0; |
| 10502 | } |
| 10503 | |
| 10504 | ret = pmAssertions->createAssertion(whichAssertionBits, assertionLevel, ownerService, ownerDescription, &newAssertion); |
| 10505 | |
| 10506 | if (kIOReturnSuccess == ret) { |
| 10507 | #if (DEVELOPMENT || DEBUG) |
| 10508 | if (_aotNow || (kIOLogPMRootDomain & gIOKitDebug)) { |
| 10509 | OSReportWithBacktrace("PMRD: createPMAssertion(0x%qx) %s (%s)" , newAssertion, ownerService->getName(), ownerDescription); |
| 10510 | } |
| 10511 | #endif /* (DEVELOPMENT || DEBUG) */ |
| 10512 | return newAssertion; |
| 10513 | } else { |
| 10514 | return 0; |
| 10515 | } |
| 10516 | } |
| 10517 | |
| 10518 | IOReturn |
| 10519 | IOPMrootDomain::releasePMAssertion(IOPMDriverAssertionID releaseAssertion) |
| 10520 | { |
| 10521 | #if (DEVELOPMENT || DEBUG) |
| 10522 | if (_aotNow || (kIOLogPMRootDomain & gIOKitDebug)) { |
| 10523 | PMAssertStruct *details = pmAssertions->detailsForID(releaseAssertion, NULL); |
| 10524 | if (details && details->ownerService && details->ownerString) { |
| 10525 | OSReportWithBacktrace("PMRD: releasePMAssertion(0x%qx) %s (%s)" , releaseAssertion, |
| 10526 | details->ownerService->getName(), details->ownerString->getCStringNoCopy()); |
| 10527 | } else { |
| 10528 | OSReportWithBacktrace("PMRD: releasePMAssertion(0x%qx)" , releaseAssertion); |
| 10529 | } |
| 10530 | } |
| 10531 | #endif /* (DEVELOPMENT || DEBUG) */ |
| 10532 | if (!pmAssertions) { |
| 10533 | return kIOReturnInternalError; |
| 10534 | } |
| 10535 | return pmAssertions->releaseAssertion(releaseAssertion); |
| 10536 | } |
| 10537 | |
| 10538 | |
| 10539 | IOReturn |
| 10540 | IOPMrootDomain::setPMAssertionLevel( |
| 10541 | IOPMDriverAssertionID assertionID, |
| 10542 | IOPMDriverAssertionLevel assertionLevel) |
| 10543 | { |
| 10544 | return pmAssertions->setAssertionLevel(assertionID, assertionLevel); |
| 10545 | } |
| 10546 | |
| 10547 | IOPMDriverAssertionLevel |
| 10548 | IOPMrootDomain::getPMAssertionLevel(IOPMDriverAssertionType whichAssertion) |
| 10549 | { |
| 10550 | IOPMDriverAssertionType sysLevels; |
| 10551 | |
| 10552 | if (!pmAssertions || whichAssertion == 0) { |
| 10553 | return kIOPMDriverAssertionLevelOff; |
| 10554 | } |
| 10555 | |
| 10556 | sysLevels = pmAssertions->getActivatedAssertions(); |
| 10557 | |
| 10558 | // Check that every bit set in argument 'whichAssertion' is asserted |
| 10559 | // in the aggregate bits. |
| 10560 | if ((sysLevels & whichAssertion) == whichAssertion) { |
| 10561 | return kIOPMDriverAssertionLevelOn; |
| 10562 | } else { |
| 10563 | return kIOPMDriverAssertionLevelOff; |
| 10564 | } |
| 10565 | } |
| 10566 | |
| 10567 | IOReturn |
| 10568 | IOPMrootDomain::setPMAssertionUserLevels(IOPMDriverAssertionType inLevels) |
| 10569 | { |
| 10570 | if (!pmAssertions) { |
| 10571 | return kIOReturnNotFound; |
| 10572 | } |
| 10573 | |
| 10574 | return pmAssertions->setUserAssertionLevels(inLevels); |
| 10575 | } |
| 10576 | |
| 10577 | IOReturn |
| 10578 | IOPMrootDomain::acquireDriverKitMatchingAssertion() |
| 10579 | { |
| 10580 | return gIOPMWorkLoop->runActionBlock(action: ^{ |
| 10581 | if (_driverKitMatchingAssertionCount != 0) { |
| 10582 | _driverKitMatchingAssertionCount++; |
| 10583 | return kIOReturnSuccess; |
| 10584 | } else { |
| 10585 | if (kSystemTransitionSleep == _systemTransitionType) { |
| 10586 | // system going to sleep |
| 10587 | return kIOReturnBusy; |
| 10588 | } else { |
| 10589 | // createPMAssertion is asynchronous. |
| 10590 | // we must also set _driverKitMatchingAssertionCount under the PM workloop lock so that we can cancel sleep immediately |
| 10591 | // The assertion is used so that on release, we reevaluate all assertions |
| 10592 | _driverKitMatchingAssertion = createPMAssertion(whichAssertionBits: kIOPMDriverAssertionCPUBit, kIOPMDriverAssertionLevelOn, ownerService: this, ownerDescription: "DK matching" ); |
| 10593 | if (_driverKitMatchingAssertion != kIOPMUndefinedDriverAssertionID) { |
| 10594 | _driverKitMatchingAssertionCount = 1; |
| 10595 | return kIOReturnSuccess; |
| 10596 | } else { |
| 10597 | return kIOReturnBusy; |
| 10598 | } |
| 10599 | } |
| 10600 | } |
| 10601 | }); |
| 10602 | } |
| 10603 | |
| 10604 | void |
| 10605 | IOPMrootDomain::releaseDriverKitMatchingAssertion() |
| 10606 | { |
| 10607 | gIOPMWorkLoop->runActionBlock(action: ^{ |
| 10608 | if (_driverKitMatchingAssertionCount != 0) { |
| 10609 | _driverKitMatchingAssertionCount--; |
| 10610 | if (_driverKitMatchingAssertionCount == 0) { |
| 10611 | releasePMAssertion(releaseAssertion: _driverKitMatchingAssertion); |
| 10612 | _driverKitMatchingAssertion = kIOPMUndefinedDriverAssertionID; |
| 10613 | } |
| 10614 | } else { |
| 10615 | panic("Over-release of driverkit matching assertion" ); |
| 10616 | } |
| 10617 | return kIOReturnSuccess; |
| 10618 | }); |
| 10619 | } |
| 10620 | |
| 10621 | bool |
| 10622 | IOPMrootDomain::serializeProperties( OSSerialize * s ) const |
| 10623 | { |
| 10624 | if (pmAssertions) { |
| 10625 | pmAssertions->publishProperties(); |
| 10626 | } |
| 10627 | return IOService::serializeProperties(s); |
| 10628 | } |
| 10629 | |
| 10630 | OSSharedPtr<OSObject> |
| 10631 | IOPMrootDomain::copyProperty( const char * aKey) const |
| 10632 | { |
| 10633 | OSSharedPtr<OSObject> obj; |
| 10634 | obj = IOService::copyProperty(aKey); |
| 10635 | |
| 10636 | if (obj) { |
| 10637 | return obj; |
| 10638 | } |
| 10639 | |
| 10640 | if (!strncmp(s1: aKey, kIOPMSleepWakeWdogRebootKey, |
| 10641 | n: sizeof(kIOPMSleepWakeWdogRebootKey))) { |
| 10642 | if (swd_flags & SWD_BOOT_BY_SW_WDOG) { |
| 10643 | return OSSharedPtr<OSBoolean>(kOSBooleanTrue, OSNoRetain); |
| 10644 | } else { |
| 10645 | return OSSharedPtr<OSBoolean>(kOSBooleanFalse, OSNoRetain); |
| 10646 | } |
| 10647 | } |
| 10648 | |
| 10649 | if (!strncmp(s1: aKey, kIOPMSleepWakeWdogLogsValidKey, |
| 10650 | n: sizeof(kIOPMSleepWakeWdogLogsValidKey))) { |
| 10651 | if (swd_flags & SWD_VALID_LOGS) { |
| 10652 | return OSSharedPtr<OSBoolean>(kOSBooleanTrue, OSNoRetain); |
| 10653 | } else { |
| 10654 | return OSSharedPtr<OSBoolean>(kOSBooleanFalse, OSNoRetain); |
| 10655 | } |
| 10656 | } |
| 10657 | |
| 10658 | /* |
| 10659 | * XXX: We should get rid of "DesktopMode" property when 'kAppleClamshellCausesSleepKey' |
| 10660 | * is set properly in darwake from sleep. For that, kIOPMEnableClamshell msg has to be |
| 10661 | * issued by DisplayWrangler on darkwake. |
| 10662 | */ |
| 10663 | if (!strcmp(s1: aKey, s2: "DesktopMode" )) { |
| 10664 | if (desktopMode) { |
| 10665 | return OSSharedPtr<OSBoolean>(kOSBooleanTrue, OSNoRetain); |
| 10666 | } else { |
| 10667 | return OSSharedPtr<OSBoolean>(kOSBooleanFalse, OSNoRetain); |
| 10668 | } |
| 10669 | } |
| 10670 | if (!strcmp(s1: aKey, s2: "DisplayIdleForDemandSleep" )) { |
| 10671 | if (displayIdleForDemandSleep) { |
| 10672 | return OSSharedPtr<OSBoolean>(kOSBooleanTrue, OSNoRetain); |
| 10673 | } else { |
| 10674 | return OSSharedPtr<OSBoolean>(kOSBooleanFalse, OSNoRetain); |
| 10675 | } |
| 10676 | } |
| 10677 | |
| 10678 | if (!strcmp(s1: aKey, kIOPMDriverWakeEventsKey)) { |
| 10679 | OSSharedPtr<OSArray> array; |
| 10680 | WAKEEVENT_LOCK(); |
| 10681 | if (_systemWakeEventsArray && _systemWakeEventsArray->getCount()) { |
| 10682 | OSSharedPtr<OSCollection> collection = _systemWakeEventsArray->copyCollection(); |
| 10683 | if (collection) { |
| 10684 | array = OSDynamicPtrCast<OSArray>(source: collection); |
| 10685 | } |
| 10686 | } |
| 10687 | WAKEEVENT_UNLOCK(); |
| 10688 | return os::move(t&: array); |
| 10689 | } |
| 10690 | |
| 10691 | if (!strcmp(s1: aKey, kIOPMSleepStatisticsAppsKey)) { |
| 10692 | OSSharedPtr<OSArray> array; |
| 10693 | IOLockLock(pmStatsLock); |
| 10694 | if (pmStatsAppResponses && pmStatsAppResponses->getCount()) { |
| 10695 | OSSharedPtr<OSCollection> collection = pmStatsAppResponses->copyCollection(); |
| 10696 | if (collection) { |
| 10697 | array = OSDynamicPtrCast<OSArray>(source: collection); |
| 10698 | } |
| 10699 | } |
| 10700 | IOLockUnlock(pmStatsLock); |
| 10701 | return os::move(t&: array); |
| 10702 | } |
| 10703 | |
| 10704 | if (!strcmp(s1: aKey, kIOPMIdleSleepPreventersKey)) { |
| 10705 | OSArray *idleSleepList = NULL; |
| 10706 | gRootDomain->copySleepPreventersList(idleSleepList: &idleSleepList, NULL); |
| 10707 | return OSSharedPtr<OSArray>(idleSleepList, OSNoRetain); |
| 10708 | } |
| 10709 | |
| 10710 | if (!strcmp(s1: aKey, kIOPMSystemSleepPreventersKey)) { |
| 10711 | OSArray *systemSleepList = NULL; |
| 10712 | gRootDomain->copySleepPreventersList(NULL, systemSleepList: &systemSleepList); |
| 10713 | return OSSharedPtr<OSArray>(systemSleepList, OSNoRetain); |
| 10714 | } |
| 10715 | |
| 10716 | if (!strcmp(s1: aKey, kIOPMIdleSleepPreventersWithIDKey)) { |
| 10717 | OSArray *idleSleepList = NULL; |
| 10718 | gRootDomain->copySleepPreventersListWithID(idleSleepList: &idleSleepList, NULL); |
| 10719 | return OSSharedPtr<OSArray>(idleSleepList, OSNoRetain); |
| 10720 | } |
| 10721 | |
| 10722 | if (!strcmp(s1: aKey, kIOPMSystemSleepPreventersWithIDKey)) { |
| 10723 | OSArray *systemSleepList = NULL; |
| 10724 | gRootDomain->copySleepPreventersListWithID(NULL, systemSleepList: &systemSleepList); |
| 10725 | return OSSharedPtr<OSArray>(systemSleepList, OSNoRetain); |
| 10726 | } |
| 10727 | return NULL; |
| 10728 | } |
| 10729 | |
| 10730 | // MARK: - |
| 10731 | // MARK: Wake Event Reporting |
| 10732 | |
| 10733 | void |
| 10734 | IOPMrootDomain::copyWakeReasonString( char * outBuf, size_t bufSize ) |
| 10735 | { |
| 10736 | WAKEEVENT_LOCK(); |
| 10737 | strlcpy(dst: outBuf, src: gWakeReasonString, n: bufSize); |
| 10738 | WAKEEVENT_UNLOCK(); |
| 10739 | } |
| 10740 | |
| 10741 | void |
| 10742 | IOPMrootDomain::copyShutdownReasonString( char * outBuf, size_t bufSize ) |
| 10743 | { |
| 10744 | WAKEEVENT_LOCK(); |
| 10745 | strlcpy(dst: outBuf, src: gShutdownReasonString, n: bufSize); |
| 10746 | WAKEEVENT_UNLOCK(); |
| 10747 | } |
| 10748 | |
| 10749 | //****************************************************************************** |
| 10750 | // acceptSystemWakeEvents |
| 10751 | // |
| 10752 | // Private control for the acceptance of driver wake event claims. |
| 10753 | //****************************************************************************** |
| 10754 | |
| 10755 | void |
| 10756 | IOPMrootDomain::acceptSystemWakeEvents( uint32_t control ) |
| 10757 | { |
| 10758 | bool logWakeReason = false; |
| 10759 | |
| 10760 | WAKEEVENT_LOCK(); |
| 10761 | switch (control) { |
| 10762 | case kAcceptSystemWakeEvents_Enable: |
| 10763 | assert(_acceptSystemWakeEvents == false); |
| 10764 | if (!_systemWakeEventsArray) { |
| 10765 | _systemWakeEventsArray = OSArray::withCapacity(capacity: 4); |
| 10766 | } |
| 10767 | _acceptSystemWakeEvents = (_systemWakeEventsArray != NULL); |
| 10768 | if (!(_aotNow && (kIOPMWakeEventAOTExitFlags & _aotPendingFlags))) { |
| 10769 | gWakeReasonString[0] = '\0'; |
| 10770 | if (_systemWakeEventsArray) { |
| 10771 | _systemWakeEventsArray->flushCollection(); |
| 10772 | } |
| 10773 | } |
| 10774 | |
| 10775 | // Remove stale WakeType property before system sleep |
| 10776 | removeProperty(kIOPMRootDomainWakeTypeKey); |
| 10777 | removeProperty(kIOPMRootDomainWakeReasonKey); |
| 10778 | break; |
| 10779 | |
| 10780 | case kAcceptSystemWakeEvents_Disable: |
| 10781 | _acceptSystemWakeEvents = false; |
| 10782 | #if defined(XNU_TARGET_OS_OSX) |
| 10783 | logWakeReason = (gWakeReasonString[0] != '\0'); |
| 10784 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
| 10785 | logWakeReason = gWakeReasonSysctlRegistered; |
| 10786 | #if DEVELOPMENT |
| 10787 | static int panic_allowed = -1; |
| 10788 | |
| 10789 | if ((panic_allowed == -1) && |
| 10790 | (PE_parse_boot_argn("swd_wakereason_panic" , &panic_allowed, sizeof(panic_allowed)) == false)) { |
| 10791 | panic_allowed = 0; |
| 10792 | } |
| 10793 | |
| 10794 | if (panic_allowed) { |
| 10795 | size_t i = 0; |
| 10796 | // Panic if wake reason is null or empty |
| 10797 | for (i = 0; (i < strlen(gWakeReasonString)); i++) { |
| 10798 | if ((gWakeReasonString[i] != ' ') && (gWakeReasonString[i] != '\t')) { |
| 10799 | break; |
| 10800 | } |
| 10801 | } |
| 10802 | if (i >= strlen(gWakeReasonString)) { |
| 10803 | panic("Wake reason is empty" ); |
| 10804 | } |
| 10805 | } |
| 10806 | #endif /* DEVELOPMENT */ |
| 10807 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
| 10808 | |
| 10809 | // publish kIOPMRootDomainWakeReasonKey if not already set |
| 10810 | if (!propertyExists(kIOPMRootDomainWakeReasonKey)) { |
| 10811 | setProperty(kIOPMRootDomainWakeReasonKey, aString: gWakeReasonString); |
| 10812 | } |
| 10813 | break; |
| 10814 | |
| 10815 | case kAcceptSystemWakeEvents_Reenable: |
| 10816 | assert(_acceptSystemWakeEvents == false); |
| 10817 | _acceptSystemWakeEvents = (_systemWakeEventsArray != NULL); |
| 10818 | removeProperty(kIOPMRootDomainWakeReasonKey); |
| 10819 | break; |
| 10820 | } |
| 10821 | WAKEEVENT_UNLOCK(); |
| 10822 | |
| 10823 | if (logWakeReason) { |
| 10824 | MSG("system wake events: %s\n" , gWakeReasonString); |
| 10825 | } |
| 10826 | } |
| 10827 | |
| 10828 | //****************************************************************************** |
| 10829 | // claimSystemWakeEvent |
| 10830 | // |
| 10831 | // For a driver to claim a device is the source/conduit of a system wake event. |
| 10832 | //****************************************************************************** |
| 10833 | |
| 10834 | void |
| 10835 | IOPMrootDomain::claimSystemWakeEvent( |
| 10836 | IOService * device, |
| 10837 | IOOptionBits flags, |
| 10838 | const char * reason, |
| 10839 | OSObject * details ) |
| 10840 | { |
| 10841 | OSSharedPtr<const OSSymbol> deviceName; |
| 10842 | OSSharedPtr<OSNumber> deviceRegId; |
| 10843 | OSSharedPtr<OSNumber> claimTime; |
| 10844 | OSSharedPtr<OSData> flagsData; |
| 10845 | OSSharedPtr<OSString> reasonString; |
| 10846 | OSSharedPtr<OSDictionary> dict; |
| 10847 | uint64_t timestamp; |
| 10848 | bool addWakeReason; |
| 10849 | |
| 10850 | if (!device || !reason) { |
| 10851 | return; |
| 10852 | } |
| 10853 | |
| 10854 | pmEventTimeStamp(recordTS: ×tamp); |
| 10855 | |
| 10856 | IOOptionBits aotFlags = 0; |
| 10857 | bool needAOTEvaluate = FALSE; |
| 10858 | |
| 10859 | if (kIOPMAOTModeAddEventFlags & _aotMode) { |
| 10860 | if (!strcmp(s1: "hold" , s2: reason) |
| 10861 | || !strcmp(s1: "help" , s2: reason) |
| 10862 | || !strcmp(s1: "menu" , s2: reason) |
| 10863 | || !strcmp(s1: "stockholm" , s2: reason) |
| 10864 | || !strcmp(s1: "ringer" , s2: reason) |
| 10865 | || !strcmp(s1: "ringerab" , s2: reason) |
| 10866 | || !strcmp(s1: "smc0" , s2: reason) |
| 10867 | || !strcmp(s1: "AOP.RTPWakeupAP" , s2: reason) |
| 10868 | || !strcmp(s1: "AOP.RTP_AP_IRQ" , s2: reason) |
| 10869 | || !strcmp(s1: "BT.OutboxNotEmpty" , s2: reason) |
| 10870 | || !strcmp(s1: "WL.OutboxNotEmpty" , s2: reason)) { |
| 10871 | flags |= kIOPMWakeEventAOTExit; |
| 10872 | } |
| 10873 | } |
| 10874 | |
| 10875 | #if DEVELOPMENT || DEBUG |
| 10876 | if (_aotLingerTime && !strcmp("rtc" , reason)) { |
| 10877 | flags |= kIOPMWakeEventAOTPossibleExit; |
| 10878 | } |
| 10879 | #endif /* DEVELOPMENT || DEBUG */ |
| 10880 | |
| 10881 | #if defined(XNU_TARGET_OS_OSX) && !DISPLAY_WRANGLER_PRESENT |
| 10882 | // Publishing the WakeType is serialized by the PM work loop |
| 10883 | if (!strcmp(s1: "rtc" , s2: reason) && (_nextScheduledAlarmType != NULL)) { |
| 10884 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventPublishWakeType, |
| 10885 | arg0: (void *) _nextScheduledAlarmType.get()); |
| 10886 | } |
| 10887 | |
| 10888 | // Workaround for the missing wake HID event |
| 10889 | if (gDarkWakeFlags & kDarkWakeFlagUserWakeWorkaround) { |
| 10890 | if (!strcmp(s1: "trackpadkeyboard" , s2: reason)) { |
| 10891 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventPublishWakeType, |
| 10892 | arg0: (void *) gIOPMWakeTypeUserKey.get()); |
| 10893 | } |
| 10894 | } |
| 10895 | #endif |
| 10896 | |
| 10897 | deviceName = device->copyName(plane: gIOServicePlane); |
| 10898 | deviceRegId = OSNumber::withNumber(value: device->getRegistryEntryID(), numberOfBits: 64); |
| 10899 | claimTime = OSNumber::withNumber(value: timestamp, numberOfBits: 64); |
| 10900 | flagsData = OSData::withValue(value: flags); |
| 10901 | reasonString = OSString::withCString(cString: reason); |
| 10902 | dict = OSDictionary::withCapacity(capacity: 5 + (details ? 1 : 0)); |
| 10903 | if (!dict || !deviceName || !deviceRegId || !claimTime || !flagsData || !reasonString) { |
| 10904 | goto done; |
| 10905 | } |
| 10906 | |
| 10907 | dict->setObject(aKey: gIONameKey, anObject: deviceName.get()); |
| 10908 | dict->setObject(aKey: gIORegistryEntryIDKey, anObject: deviceRegId.get()); |
| 10909 | dict->setObject(kIOPMWakeEventTimeKey, anObject: claimTime.get()); |
| 10910 | dict->setObject(kIOPMWakeEventFlagsKey, anObject: flagsData.get()); |
| 10911 | dict->setObject(kIOPMWakeEventReasonKey, anObject: reasonString.get()); |
| 10912 | if (details) { |
| 10913 | dict->setObject(kIOPMWakeEventDetailsKey, anObject: details); |
| 10914 | } |
| 10915 | |
| 10916 | WAKEEVENT_LOCK(); |
| 10917 | addWakeReason = _acceptSystemWakeEvents; |
| 10918 | if (_aotMode) { |
| 10919 | IOLog(format: "claimSystemWakeEvent(%s, %s, 0x%x) 0x%x %d\n" , reason, deviceName->getCStringNoCopy(), (int)flags, _aotPendingFlags, _aotReadyToFullWake); |
| 10920 | } |
| 10921 | aotFlags = (kIOPMWakeEventAOTFlags & flags); |
| 10922 | aotFlags = (aotFlags & ~_aotPendingFlags); |
| 10923 | needAOTEvaluate = false; |
| 10924 | if (_aotNow && aotFlags) { |
| 10925 | if (kIOPMWakeEventAOTPossibleExit & flags) { |
| 10926 | _aotMetrics->possibleCount++; |
| 10927 | } |
| 10928 | if (kIOPMWakeEventAOTConfirmedPossibleExit & flags) { |
| 10929 | _aotMetrics->confirmedPossibleCount++; |
| 10930 | } |
| 10931 | if (kIOPMWakeEventAOTRejectedPossibleExit & flags) { |
| 10932 | _aotMetrics->rejectedPossibleCount++; |
| 10933 | } |
| 10934 | if (kIOPMWakeEventAOTExpiredPossibleExit & flags) { |
| 10935 | _aotMetrics->expiredPossibleCount++; |
| 10936 | } |
| 10937 | |
| 10938 | _aotPendingFlags |= aotFlags; |
| 10939 | addWakeReason = _aotNow && _systemWakeEventsArray && ((kIOPMWakeEventAOTExitFlags & aotFlags)); |
| 10940 | needAOTEvaluate = _aotReadyToFullWake; |
| 10941 | } |
| 10942 | DMSG("claimSystemWakeEvent(%s, 0x%x, %s, 0x%llx) aot %d phase 0x%x add %d\n" , |
| 10943 | reason, (int)flags, deviceName->getCStringNoCopy(), device->getRegistryEntryID(), |
| 10944 | _aotNow, pmTracer->getTracePhase(), addWakeReason); |
| 10945 | |
| 10946 | #if DEVELOPMENT || DEBUG |
| 10947 | if (addWakeReason) { |
| 10948 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 10949 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 10950 | "Report System Wake Event" , |
| 10951 | "Reason: %s Flags: 0x%x Device: %s, DeviceRegEntry: 0x%llx\n" , |
| 10952 | reason, |
| 10953 | (int)flags, |
| 10954 | deviceName->getCStringNoCopy(), |
| 10955 | device->getRegistryEntryID() |
| 10956 | ); |
| 10957 | } |
| 10958 | #endif /* DEVELOPMENT || DEBUG */ |
| 10959 | |
| 10960 | if (!gWakeReasonSysctlRegistered) { |
| 10961 | // Lazy registration until the platform driver stops registering |
| 10962 | // the same name. |
| 10963 | gWakeReasonSysctlRegistered = true; |
| 10964 | } |
| 10965 | if (addWakeReason) { |
| 10966 | _systemWakeEventsArray->setObject(dict.get()); |
| 10967 | if (gWakeReasonString[0] != '\0') { |
| 10968 | strlcat(dst: gWakeReasonString, src: " " , n: sizeof(gWakeReasonString)); |
| 10969 | } |
| 10970 | strlcat(dst: gWakeReasonString, src: reason, n: sizeof(gWakeReasonString)); |
| 10971 | } |
| 10972 | |
| 10973 | WAKEEVENT_UNLOCK(); |
| 10974 | if (needAOTEvaluate) { |
| 10975 | // Call aotEvaluate() on PM work loop since it may call |
| 10976 | // aotExit() which accesses PM state. |
| 10977 | pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventAOTEvaluate); |
| 10978 | } |
| 10979 | |
| 10980 | done: |
| 10981 | return; |
| 10982 | } |
| 10983 | |
| 10984 | //****************************************************************************** |
| 10985 | // claimSystemBootEvent |
| 10986 | // |
| 10987 | // For a driver to claim a device is the source/conduit of a system boot event. |
| 10988 | //****************************************************************************** |
| 10989 | |
| 10990 | void |
| 10991 | IOPMrootDomain::claimSystemBootEvent( |
| 10992 | IOService * device, |
| 10993 | IOOptionBits flags, |
| 10994 | const char * reason, |
| 10995 | __unused OSObject * details ) |
| 10996 | { |
| 10997 | if (!device || !reason) { |
| 10998 | return; |
| 10999 | } |
| 11000 | |
| 11001 | DEBUG_LOG("claimSystemBootEvent(%s, %s, 0x%x)\n" , reason, device->getName(), (uint32_t) flags); |
| 11002 | #if DEVELOPMENT || DEBUG |
| 11003 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 11004 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 11005 | "Report System Boot Device" , |
| 11006 | "Reason: %s Flags: 0x%x Device: %s" , |
| 11007 | reason, |
| 11008 | (int)flags, |
| 11009 | device->getName() |
| 11010 | ); |
| 11011 | #endif /* DEVELOPMENT || DEBUG */ |
| 11012 | WAKEEVENT_LOCK(); |
| 11013 | if (!gBootReasonSysctlRegistered) { |
| 11014 | // Lazy sysctl registration after setting gBootReasonString |
| 11015 | strlcat(dst: gBootReasonString, src: reason, n: sizeof(gBootReasonString)); |
| 11016 | os_atomic_store(&gBootReasonSysctlRegistered, true, release); |
| 11017 | } |
| 11018 | WAKEEVENT_UNLOCK(); |
| 11019 | } |
| 11020 | |
| 11021 | //****************************************************************************** |
| 11022 | // claimSystemShutdownEvent |
| 11023 | // |
| 11024 | // For drivers to claim a system shutdown event on the ensuing boot. |
| 11025 | //****************************************************************************** |
| 11026 | |
| 11027 | void |
| 11028 | IOPMrootDomain::claimSystemShutdownEvent( |
| 11029 | IOService * device, |
| 11030 | IOOptionBits flags, |
| 11031 | const char * reason, |
| 11032 | __unused OSObject * details ) |
| 11033 | { |
| 11034 | if (!device || !reason) { |
| 11035 | return; |
| 11036 | } |
| 11037 | |
| 11038 | DEBUG_LOG("claimSystemShutdownEvent(%s, %s, 0x%x)\n" , reason, device->getName(), (uint32_t) flags); |
| 11039 | #if DEVELOPMENT || DEBUG |
| 11040 | record_system_event(SYSTEM_EVENT_TYPE_INFO, |
| 11041 | SYSTEM_EVENT_SUBSYSTEM_PMRD, |
| 11042 | "Report System Shutdown Cause From Previous Boot" , |
| 11043 | "Reason: %s Flags: 0x%x Device: %s" , |
| 11044 | reason, |
| 11045 | (int)flags, |
| 11046 | device->getName() |
| 11047 | ); |
| 11048 | #endif /* DEVELOPMENT || DEBUG */ |
| 11049 | WAKEEVENT_LOCK(); |
| 11050 | if (gShutdownReasonString[0] != '\0') { |
| 11051 | strlcat(dst: gShutdownReasonString, src: " " , n: sizeof(gShutdownReasonString)); |
| 11052 | } |
| 11053 | strlcat(dst: gShutdownReasonString, src: reason, n: sizeof(gShutdownReasonString)); |
| 11054 | |
| 11055 | gShutdownReasonSysctlRegistered = true; |
| 11056 | WAKEEVENT_UNLOCK(); |
| 11057 | } |
| 11058 | |
| 11059 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 11060 | |
| 11061 | // MARK: - |
| 11062 | // MARK: PMSettingHandle |
| 11063 | |
| 11064 | OSDefineMetaClassAndStructors( PMSettingHandle, OSObject ) |
| 11065 | |
| 11066 | void |
| 11067 | PMSettingHandle::free( void ) |
| 11068 | { |
| 11069 | if (pmso) { |
| 11070 | pmso->clientHandleFreed(); |
| 11071 | pmso->release(); |
| 11072 | pmso = NULL; |
| 11073 | } |
| 11074 | |
| 11075 | OSObject::free(); |
| 11076 | } |
| 11077 | |
| 11078 | // MARK: - |
| 11079 | // MARK: PMSettingObject |
| 11080 | |
| 11081 | #undef super |
| 11082 | #define super OSObject |
| 11083 | OSDefineMetaClassAndFinalStructors( PMSettingObject, OSObject ) |
| 11084 | |
| 11085 | /* |
| 11086 | * Static constructor/initializer for PMSettingObject |
| 11087 | */ |
| 11088 | PMSettingObject *PMSettingObject::pmSettingObject( |
| 11089 | IOPMrootDomain * parent_arg, |
| 11090 | IOPMSettingControllerCallback handler_arg, |
| 11091 | OSObject * target_arg, |
| 11092 | uintptr_t refcon_arg, |
| 11093 | uint32_t supportedPowerSources, |
| 11094 | const OSSymbol * settings[], |
| 11095 | OSObject * *handle_obj) |
| 11096 | { |
| 11097 | uint32_t settingCount = 0; |
| 11098 | PMSettingObject *pmso = NULL; |
| 11099 | PMSettingHandle *pmsh = NULL; |
| 11100 | |
| 11101 | if (!parent_arg || !handler_arg || !settings || !handle_obj) { |
| 11102 | return NULL; |
| 11103 | } |
| 11104 | |
| 11105 | // count OSSymbol entries in NULL terminated settings array |
| 11106 | while (settings[settingCount]) { |
| 11107 | settingCount++; |
| 11108 | } |
| 11109 | if (0 == settingCount) { |
| 11110 | return NULL; |
| 11111 | } |
| 11112 | |
| 11113 | pmso = new PMSettingObject; |
| 11114 | if (!pmso || !pmso->init()) { |
| 11115 | goto fail; |
| 11116 | } |
| 11117 | |
| 11118 | pmsh = new PMSettingHandle; |
| 11119 | if (!pmsh || !pmsh->init()) { |
| 11120 | goto fail; |
| 11121 | } |
| 11122 | |
| 11123 | queue_init(&pmso->calloutQueue); |
| 11124 | pmso->parent = parent_arg; |
| 11125 | pmso->func = handler_arg; |
| 11126 | pmso->target = target_arg; |
| 11127 | pmso->refcon = refcon_arg; |
| 11128 | pmso->settingCount = settingCount; |
| 11129 | |
| 11130 | pmso->retain(); // handle holds a retain on pmso |
| 11131 | pmsh->pmso = pmso; |
| 11132 | pmso->pmsh = pmsh; |
| 11133 | |
| 11134 | pmso->publishedFeatureID = OSDataAllocation<uint32_t>(settingCount, OSAllocateMemory); |
| 11135 | if (pmso->publishedFeatureID) { |
| 11136 | for (unsigned int i = 0; i < settingCount; i++) { |
| 11137 | // Since there is now at least one listener to this setting, publish |
| 11138 | // PM root domain support for it. |
| 11139 | parent_arg->publishPMSetting( feature: settings[i], |
| 11140 | where: supportedPowerSources, featureID: &pmso->publishedFeatureID[i] ); |
| 11141 | } |
| 11142 | } |
| 11143 | |
| 11144 | *handle_obj = pmsh; |
| 11145 | return pmso; |
| 11146 | |
| 11147 | fail: |
| 11148 | if (pmso) { |
| 11149 | pmso->release(); |
| 11150 | } |
| 11151 | if (pmsh) { |
| 11152 | pmsh->release(); |
| 11153 | } |
| 11154 | return NULL; |
| 11155 | } |
| 11156 | |
| 11157 | void |
| 11158 | PMSettingObject::free( void ) |
| 11159 | { |
| 11160 | if (publishedFeatureID) { |
| 11161 | for (const auto& featureID : publishedFeatureID) { |
| 11162 | if (featureID) { |
| 11163 | parent->removePublishedFeature( removeFeatureID: featureID ); |
| 11164 | } |
| 11165 | } |
| 11166 | |
| 11167 | publishedFeatureID = {}; |
| 11168 | } |
| 11169 | |
| 11170 | super::free(); |
| 11171 | } |
| 11172 | |
| 11173 | IOReturn |
| 11174 | PMSettingObject::dispatchPMSetting( const OSSymbol * type, OSObject * object ) |
| 11175 | { |
| 11176 | return (*func)(target, type, object, refcon); |
| 11177 | } |
| 11178 | |
| 11179 | void |
| 11180 | PMSettingObject::clientHandleFreed( void ) |
| 11181 | { |
| 11182 | parent->deregisterPMSettingObject(pmso: this); |
| 11183 | } |
| 11184 | |
| 11185 | // MARK: - |
| 11186 | // MARK: PMAssertionsTracker |
| 11187 | |
| 11188 | //********************************************************************************* |
| 11189 | //********************************************************************************* |
| 11190 | //********************************************************************************* |
| 11191 | // class PMAssertionsTracker Implementation |
| 11192 | |
| 11193 | #define kAssertUniqueIDStart 500 |
| 11194 | |
| 11195 | PMAssertionsTracker * |
| 11196 | PMAssertionsTracker::pmAssertionsTracker( IOPMrootDomain *rootDomain ) |
| 11197 | { |
| 11198 | PMAssertionsTracker *me; |
| 11199 | |
| 11200 | me = new PMAssertionsTracker; |
| 11201 | if (!me || !me->init()) { |
| 11202 | if (me) { |
| 11203 | me->release(); |
| 11204 | } |
| 11205 | return NULL; |
| 11206 | } |
| 11207 | |
| 11208 | me->owner = rootDomain; |
| 11209 | me->issuingUniqueID = kAssertUniqueIDStart; |
| 11210 | me->assertionsArray = OSArray::withCapacity(capacity: 5); |
| 11211 | me->assertionsKernel = 0; |
| 11212 | me->assertionsUser = 0; |
| 11213 | me->assertionsCombined = 0; |
| 11214 | me->assertionsArrayLock = IOLockAlloc(); |
| 11215 | me->tabulateProducerCount = me->tabulateConsumerCount = 0; |
| 11216 | |
| 11217 | assert(me->assertionsArray); |
| 11218 | assert(me->assertionsArrayLock); |
| 11219 | |
| 11220 | return me; |
| 11221 | } |
| 11222 | |
| 11223 | /* tabulate |
| 11224 | * - Update assertionsKernel to reflect the state of all |
| 11225 | * assertions in the kernel. |
| 11226 | * - Update assertionsCombined to reflect both kernel & user space. |
| 11227 | */ |
| 11228 | void |
| 11229 | PMAssertionsTracker::tabulate(void) |
| 11230 | { |
| 11231 | int i; |
| 11232 | int count; |
| 11233 | const PMAssertStruct *_a = nullptr; |
| 11234 | OSValueObject<PMAssertStruct> *_d = nullptr; |
| 11235 | |
| 11236 | IOPMDriverAssertionType oldKernel = assertionsKernel; |
| 11237 | IOPMDriverAssertionType oldCombined = assertionsCombined; |
| 11238 | |
| 11239 | ASSERT_GATED(); |
| 11240 | |
| 11241 | assertionsKernel = 0; |
| 11242 | assertionsCombined = 0; |
| 11243 | |
| 11244 | if (!assertionsArray) { |
| 11245 | return; |
| 11246 | } |
| 11247 | |
| 11248 | if ((count = assertionsArray->getCount())) { |
| 11249 | for (i = 0; i < count; i++) { |
| 11250 | _d = OSDynamicCast(OSValueObject<PMAssertStruct>, assertionsArray->getObject(i)); |
| 11251 | if (_d) { |
| 11252 | _a = _d->getBytesNoCopy(); |
| 11253 | if (_a && (kIOPMDriverAssertionLevelOn == _a->level)) { |
| 11254 | assertionsKernel |= _a->assertionBits; |
| 11255 | } |
| 11256 | } |
| 11257 | } |
| 11258 | } |
| 11259 | |
| 11260 | tabulateProducerCount++; |
| 11261 | assertionsCombined = assertionsKernel | assertionsUser; |
| 11262 | |
| 11263 | if ((assertionsKernel != oldKernel) || |
| 11264 | (assertionsCombined != oldCombined)) { |
| 11265 | owner->evaluateAssertions(newAssertions: assertionsCombined, oldAssertions: oldCombined); |
| 11266 | } |
| 11267 | } |
| 11268 | |
| 11269 | void |
| 11270 | PMAssertionsTracker::updateCPUBitAccounting( PMAssertStruct *assertStruct ) |
| 11271 | { |
| 11272 | AbsoluteTime now; |
| 11273 | uint64_t nsec; |
| 11274 | |
| 11275 | if (((assertStruct->assertionBits & kIOPMDriverAssertionCPUBit) == 0) || |
| 11276 | (assertStruct->assertCPUStartTime == 0)) { |
| 11277 | return; |
| 11278 | } |
| 11279 | |
| 11280 | now = mach_absolute_time(); |
| 11281 | SUB_ABSOLUTETIME(&now, &assertStruct->assertCPUStartTime); |
| 11282 | absolutetime_to_nanoseconds(abstime: now, result: &nsec); |
| 11283 | assertStruct->assertCPUDuration += nsec; |
| 11284 | assertStruct->assertCPUStartTime = 0; |
| 11285 | |
| 11286 | if (assertStruct->assertCPUDuration > maxAssertCPUDuration) { |
| 11287 | maxAssertCPUDuration = assertStruct->assertCPUDuration; |
| 11288 | maxAssertCPUEntryId = assertStruct->registryEntryID; |
| 11289 | } |
| 11290 | } |
| 11291 | |
| 11292 | void |
| 11293 | PMAssertionsTracker::reportCPUBitAccounting( void ) |
| 11294 | { |
| 11295 | const PMAssertStruct *_a = nullptr; |
| 11296 | OSValueObject<PMAssertStruct> *_d = nullptr; |
| 11297 | int i, count; |
| 11298 | AbsoluteTime now; |
| 11299 | uint64_t nsec; |
| 11300 | |
| 11301 | ASSERT_GATED(); |
| 11302 | |
| 11303 | // Account for drivers that are still holding the CPU assertion |
| 11304 | if (assertionsKernel & kIOPMDriverAssertionCPUBit) { |
| 11305 | now = mach_absolute_time(); |
| 11306 | if ((count = assertionsArray->getCount())) { |
| 11307 | for (i = 0; i < count; i++) { |
| 11308 | _d = OSDynamicCast(OSValueObject<PMAssertStruct>, assertionsArray->getObject(i)); |
| 11309 | if (_d) { |
| 11310 | _a = _d->getBytesNoCopy(); |
| 11311 | if ((_a->assertionBits & kIOPMDriverAssertionCPUBit) && |
| 11312 | (_a->level == kIOPMDriverAssertionLevelOn) && |
| 11313 | (_a->assertCPUStartTime != 0)) { |
| 11314 | // Don't modify PMAssertStruct, leave that |
| 11315 | // for updateCPUBitAccounting() |
| 11316 | SUB_ABSOLUTETIME(&now, &_a->assertCPUStartTime); |
| 11317 | absolutetime_to_nanoseconds(abstime: now, result: &nsec); |
| 11318 | nsec += _a->assertCPUDuration; |
| 11319 | if (nsec > maxAssertCPUDuration) { |
| 11320 | maxAssertCPUDuration = nsec; |
| 11321 | maxAssertCPUEntryId = _a->registryEntryID; |
| 11322 | } |
| 11323 | } |
| 11324 | } |
| 11325 | } |
| 11326 | } |
| 11327 | } |
| 11328 | |
| 11329 | if (maxAssertCPUDuration) { |
| 11330 | DLOG("cpu assertion held for %llu ms by 0x%llx\n" , |
| 11331 | (maxAssertCPUDuration / NSEC_PER_MSEC), maxAssertCPUEntryId); |
| 11332 | } |
| 11333 | |
| 11334 | maxAssertCPUDuration = 0; |
| 11335 | maxAssertCPUEntryId = 0; |
| 11336 | } |
| 11337 | |
| 11338 | void |
| 11339 | PMAssertionsTracker::publishProperties( void ) |
| 11340 | { |
| 11341 | OSSharedPtr<OSArray> assertionsSummary; |
| 11342 | |
| 11343 | if (tabulateConsumerCount != tabulateProducerCount) { |
| 11344 | IOLockLock(assertionsArrayLock); |
| 11345 | |
| 11346 | tabulateConsumerCount = tabulateProducerCount; |
| 11347 | |
| 11348 | /* Publish the IOPMrootDomain property "DriverPMAssertionsDetailed" |
| 11349 | */ |
| 11350 | assertionsSummary = copyAssertionsArray(); |
| 11351 | if (assertionsSummary) { |
| 11352 | owner->setProperty(kIOPMAssertionsDriverDetailedKey, anObject: assertionsSummary.get()); |
| 11353 | } else { |
| 11354 | owner->removeProperty(kIOPMAssertionsDriverDetailedKey); |
| 11355 | } |
| 11356 | |
| 11357 | /* Publish the IOPMrootDomain property "DriverPMAssertions" |
| 11358 | */ |
| 11359 | owner->setProperty(kIOPMAssertionsDriverKey, aValue: assertionsKernel, aNumberOfBits: 64); |
| 11360 | |
| 11361 | IOLockUnlock(assertionsArrayLock); |
| 11362 | } |
| 11363 | } |
| 11364 | |
| 11365 | PMAssertStruct * |
| 11366 | PMAssertionsTracker::detailsForID(IOPMDriverAssertionID _id, int *index) |
| 11367 | { |
| 11368 | PMAssertStruct *_a = NULL; |
| 11369 | OSValueObject<PMAssertStruct> *_d = nullptr; |
| 11370 | int found = -1; |
| 11371 | int count = 0; |
| 11372 | int i = 0; |
| 11373 | |
| 11374 | if (assertionsArray |
| 11375 | && (count = assertionsArray->getCount())) { |
| 11376 | for (i = 0; i < count; i++) { |
| 11377 | _d = OSDynamicCast(OSValueObject<PMAssertStruct>, assertionsArray->getObject(i)); |
| 11378 | if (_d) { |
| 11379 | _a = _d->getMutableBytesNoCopy(); |
| 11380 | if (_a && (_id == _a->id)) { |
| 11381 | found = i; |
| 11382 | break; |
| 11383 | } |
| 11384 | } |
| 11385 | } |
| 11386 | } |
| 11387 | |
| 11388 | if (-1 == found) { |
| 11389 | return NULL; |
| 11390 | } else { |
| 11391 | if (index) { |
| 11392 | *index = found; |
| 11393 | } |
| 11394 | return _a; |
| 11395 | } |
| 11396 | } |
| 11397 | |
| 11398 | /* PMAssertionsTracker::handleCreateAssertion |
| 11399 | * Perform assertion work on the PM workloop. Do not call directly. |
| 11400 | */ |
| 11401 | IOReturn |
| 11402 | PMAssertionsTracker::handleCreateAssertion(OSValueObject<PMAssertStruct> *newAssertion) |
| 11403 | { |
| 11404 | PMAssertStruct *assertStruct = nullptr; |
| 11405 | |
| 11406 | ASSERT_GATED(); |
| 11407 | |
| 11408 | if (newAssertion) { |
| 11409 | IOLockLock(assertionsArrayLock); |
| 11410 | assertStruct = newAssertion->getMutableBytesNoCopy(); |
| 11411 | if ((assertStruct->assertionBits & kIOPMDriverAssertionCPUBit) && |
| 11412 | (assertStruct->level == kIOPMDriverAssertionLevelOn)) { |
| 11413 | assertStruct->assertCPUStartTime = mach_absolute_time(); |
| 11414 | } |
| 11415 | assertionsArray->setObject(newAssertion); |
| 11416 | IOLockUnlock(assertionsArrayLock); |
| 11417 | newAssertion->release(); |
| 11418 | |
| 11419 | tabulate(); |
| 11420 | } |
| 11421 | return kIOReturnSuccess; |
| 11422 | } |
| 11423 | |
| 11424 | /* PMAssertionsTracker::createAssertion |
| 11425 | * createAssertion allocates memory for a new PM assertion, and affects system behavior, if |
| 11426 | * appropiate. |
| 11427 | */ |
| 11428 | IOReturn |
| 11429 | PMAssertionsTracker::createAssertion( |
| 11430 | IOPMDriverAssertionType which, |
| 11431 | IOPMDriverAssertionLevel level, |
| 11432 | IOService *serviceID, |
| 11433 | const char *whoItIs, |
| 11434 | IOPMDriverAssertionID *outID) |
| 11435 | { |
| 11436 | OSSharedPtr<OSValueObject<PMAssertStruct> > dataStore; |
| 11437 | PMAssertStruct track; |
| 11438 | |
| 11439 | // Warning: trillions and trillions of created assertions may overflow the unique ID. |
| 11440 | track.id = OSIncrementAtomic64(address: (SInt64*) &issuingUniqueID); |
| 11441 | track.level = level; |
| 11442 | track.assertionBits = which; |
| 11443 | |
| 11444 | // NB: ownerString is explicitly managed by PMAssertStruct |
| 11445 | // it will be released in `handleReleaseAssertion' below |
| 11446 | track.ownerString = whoItIs ? OSSymbol::withCString(cString: whoItIs).detach():nullptr; |
| 11447 | track.ownerService = serviceID; |
| 11448 | track.registryEntryID = serviceID ? serviceID->getRegistryEntryID():0; |
| 11449 | track.modifiedTime = 0; |
| 11450 | pmEventTimeStamp(recordTS: &track.createdTime); |
| 11451 | track.assertCPUStartTime = 0; |
| 11452 | track.assertCPUDuration = 0; |
| 11453 | |
| 11454 | dataStore = OSValueObjectWithValue(value: track); |
| 11455 | if (!dataStore) { |
| 11456 | if (track.ownerString) { |
| 11457 | track.ownerString->release(); |
| 11458 | track.ownerString = NULL; |
| 11459 | } |
| 11460 | return kIOReturnNoMemory; |
| 11461 | } |
| 11462 | |
| 11463 | *outID = track.id; |
| 11464 | |
| 11465 | if (owner && owner->pmPowerStateQueue) { |
| 11466 | // queue action is responsible for releasing dataStore |
| 11467 | owner->pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventAssertionCreate, arg0: (void *)dataStore.detach()); |
| 11468 | } |
| 11469 | |
| 11470 | return kIOReturnSuccess; |
| 11471 | } |
| 11472 | |
| 11473 | /* PMAssertionsTracker::handleReleaseAssertion |
| 11474 | * Runs in PM workloop. Do not call directly. |
| 11475 | */ |
| 11476 | IOReturn |
| 11477 | PMAssertionsTracker::handleReleaseAssertion( |
| 11478 | IOPMDriverAssertionID _id) |
| 11479 | { |
| 11480 | ASSERT_GATED(); |
| 11481 | |
| 11482 | int index; |
| 11483 | PMAssertStruct *assertStruct = detailsForID(_id, index: &index); |
| 11484 | |
| 11485 | if (!assertStruct) { |
| 11486 | return kIOReturnNotFound; |
| 11487 | } |
| 11488 | |
| 11489 | IOLockLock(assertionsArrayLock); |
| 11490 | |
| 11491 | if ((assertStruct->assertionBits & kIOPMDriverAssertionCPUBit) && |
| 11492 | (assertStruct->level == kIOPMDriverAssertionLevelOn)) { |
| 11493 | updateCPUBitAccounting(assertStruct); |
| 11494 | } |
| 11495 | |
| 11496 | if (assertStruct->ownerString) { |
| 11497 | assertStruct->ownerString->release(); |
| 11498 | assertStruct->ownerString = NULL; |
| 11499 | } |
| 11500 | |
| 11501 | assertionsArray->removeObject(index); |
| 11502 | IOLockUnlock(assertionsArrayLock); |
| 11503 | |
| 11504 | tabulate(); |
| 11505 | return kIOReturnSuccess; |
| 11506 | } |
| 11507 | |
| 11508 | /* PMAssertionsTracker::releaseAssertion |
| 11509 | * Releases an assertion and affects system behavior if appropiate. |
| 11510 | * Actual work happens on PM workloop. |
| 11511 | */ |
| 11512 | IOReturn |
| 11513 | PMAssertionsTracker::releaseAssertion( |
| 11514 | IOPMDriverAssertionID _id) |
| 11515 | { |
| 11516 | if (owner && owner->pmPowerStateQueue) { |
| 11517 | owner->pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventAssertionRelease, NULL, arg1: _id); |
| 11518 | } |
| 11519 | return kIOReturnSuccess; |
| 11520 | } |
| 11521 | |
| 11522 | /* PMAssertionsTracker::handleSetAssertionLevel |
| 11523 | * Runs in PM workloop. Do not call directly. |
| 11524 | */ |
| 11525 | IOReturn |
| 11526 | PMAssertionsTracker::handleSetAssertionLevel( |
| 11527 | IOPMDriverAssertionID _id, |
| 11528 | IOPMDriverAssertionLevel _level) |
| 11529 | { |
| 11530 | PMAssertStruct *assertStruct = detailsForID(_id, NULL); |
| 11531 | |
| 11532 | ASSERT_GATED(); |
| 11533 | |
| 11534 | if (!assertStruct) { |
| 11535 | return kIOReturnNotFound; |
| 11536 | } |
| 11537 | |
| 11538 | IOLockLock(assertionsArrayLock); |
| 11539 | pmEventTimeStamp(recordTS: &assertStruct->modifiedTime); |
| 11540 | if ((assertStruct->assertionBits & kIOPMDriverAssertionCPUBit) && |
| 11541 | (assertStruct->level != _level)) { |
| 11542 | if (_level == kIOPMDriverAssertionLevelOn) { |
| 11543 | assertStruct->assertCPUStartTime = mach_absolute_time(); |
| 11544 | } else { |
| 11545 | updateCPUBitAccounting(assertStruct); |
| 11546 | } |
| 11547 | } |
| 11548 | assertStruct->level = _level; |
| 11549 | IOLockUnlock(assertionsArrayLock); |
| 11550 | |
| 11551 | tabulate(); |
| 11552 | return kIOReturnSuccess; |
| 11553 | } |
| 11554 | |
| 11555 | /* PMAssertionsTracker::setAssertionLevel |
| 11556 | */ |
| 11557 | IOReturn |
| 11558 | PMAssertionsTracker::setAssertionLevel( |
| 11559 | IOPMDriverAssertionID _id, |
| 11560 | IOPMDriverAssertionLevel _level) |
| 11561 | { |
| 11562 | if (owner && owner->pmPowerStateQueue) { |
| 11563 | owner->pmPowerStateQueue->submitPowerEvent(eventType: kPowerEventAssertionSetLevel, |
| 11564 | arg0: (void *)(uintptr_t)_level, arg1: _id); |
| 11565 | } |
| 11566 | |
| 11567 | return kIOReturnSuccess; |
| 11568 | } |
| 11569 | |
| 11570 | IOReturn |
| 11571 | PMAssertionsTracker::handleSetUserAssertionLevels(void * arg0) |
| 11572 | { |
| 11573 | IOPMDriverAssertionType new_user_levels = *(IOPMDriverAssertionType *) arg0; |
| 11574 | |
| 11575 | ASSERT_GATED(); |
| 11576 | |
| 11577 | if (new_user_levels != assertionsUser) { |
| 11578 | DLOG("assertionsUser 0x%llx->0x%llx\n" , assertionsUser, new_user_levels); |
| 11579 | assertionsUser = new_user_levels; |
| 11580 | } |
| 11581 | |
| 11582 | tabulate(); |
| 11583 | return kIOReturnSuccess; |
| 11584 | } |
| 11585 | |
| 11586 | IOReturn |
| 11587 | PMAssertionsTracker::setUserAssertionLevels( |
| 11588 | IOPMDriverAssertionType new_user_levels) |
| 11589 | { |
| 11590 | if (gIOPMWorkLoop) { |
| 11591 | gIOPMWorkLoop->runAction( |
| 11592 | OSMemberFunctionCast( |
| 11593 | IOWorkLoop::Action, |
| 11594 | this, |
| 11595 | &PMAssertionsTracker::handleSetUserAssertionLevels), |
| 11596 | target: this, |
| 11597 | arg0: (void *) &new_user_levels, NULL, NULL, NULL); |
| 11598 | } |
| 11599 | |
| 11600 | return kIOReturnSuccess; |
| 11601 | } |
| 11602 | |
| 11603 | |
| 11604 | OSSharedPtr<OSArray> |
| 11605 | PMAssertionsTracker::copyAssertionsArray(void) |
| 11606 | { |
| 11607 | int count; |
| 11608 | int i; |
| 11609 | OSSharedPtr<OSArray> outArray = NULL; |
| 11610 | |
| 11611 | if (!assertionsArray || (0 == (count = assertionsArray->getCount()))) { |
| 11612 | goto exit; |
| 11613 | } |
| 11614 | outArray = OSArray::withCapacity(capacity: count); |
| 11615 | if (!outArray) { |
| 11616 | goto exit; |
| 11617 | } |
| 11618 | |
| 11619 | for (i = 0; i < count; i++) { |
| 11620 | const PMAssertStruct *_a = nullptr; |
| 11621 | OSValueObject<PMAssertStruct> *_d = nullptr; |
| 11622 | OSSharedPtr<OSDictionary> details; |
| 11623 | |
| 11624 | _d = OSDynamicCast(OSValueObject<PMAssertStruct>, assertionsArray->getObject(i)); |
| 11625 | if (_d && (_a = _d->getBytesNoCopy())) { |
| 11626 | OSSharedPtr<OSNumber> _n; |
| 11627 | |
| 11628 | details = OSDictionary::withCapacity(capacity: 7); |
| 11629 | if (!details) { |
| 11630 | continue; |
| 11631 | } |
| 11632 | |
| 11633 | outArray->setObject(details.get()); |
| 11634 | |
| 11635 | _n = OSNumber::withNumber(value: _a->id, numberOfBits: 64); |
| 11636 | if (_n) { |
| 11637 | details->setObject(kIOPMDriverAssertionIDKey, anObject: _n.get()); |
| 11638 | } |
| 11639 | _n = OSNumber::withNumber(value: _a->createdTime, numberOfBits: 64); |
| 11640 | if (_n) { |
| 11641 | details->setObject(kIOPMDriverAssertionCreatedTimeKey, anObject: _n.get()); |
| 11642 | } |
| 11643 | _n = OSNumber::withNumber(value: _a->modifiedTime, numberOfBits: 64); |
| 11644 | if (_n) { |
| 11645 | details->setObject(kIOPMDriverAssertionModifiedTimeKey, anObject: _n.get()); |
| 11646 | } |
| 11647 | _n = OSNumber::withNumber(value: (uintptr_t)_a->registryEntryID, numberOfBits: 64); |
| 11648 | if (_n) { |
| 11649 | details->setObject(kIOPMDriverAssertionRegistryEntryIDKey, anObject: _n.get()); |
| 11650 | } |
| 11651 | _n = OSNumber::withNumber(value: _a->level, numberOfBits: 64); |
| 11652 | if (_n) { |
| 11653 | details->setObject(kIOPMDriverAssertionLevelKey, anObject: _n.get()); |
| 11654 | } |
| 11655 | _n = OSNumber::withNumber(value: _a->assertionBits, numberOfBits: 64); |
| 11656 | if (_n) { |
| 11657 | details->setObject(kIOPMDriverAssertionAssertedKey, anObject: _n.get()); |
| 11658 | } |
| 11659 | |
| 11660 | if (_a->ownerString) { |
| 11661 | details->setObject(kIOPMDriverAssertionOwnerStringKey, anObject: _a->ownerString); |
| 11662 | } |
| 11663 | } |
| 11664 | } |
| 11665 | |
| 11666 | exit: |
| 11667 | return os::move(t&: outArray); |
| 11668 | } |
| 11669 | |
| 11670 | IOPMDriverAssertionType |
| 11671 | PMAssertionsTracker::getActivatedAssertions(void) |
| 11672 | { |
| 11673 | return assertionsCombined; |
| 11674 | } |
| 11675 | |
| 11676 | IOPMDriverAssertionLevel |
| 11677 | PMAssertionsTracker::getAssertionLevel( |
| 11678 | IOPMDriverAssertionType type) |
| 11679 | { |
| 11680 | // FIXME: unused and also wrong |
| 11681 | if (type && ((type & assertionsKernel) == assertionsKernel)) { |
| 11682 | return kIOPMDriverAssertionLevelOn; |
| 11683 | } else { |
| 11684 | return kIOPMDriverAssertionLevelOff; |
| 11685 | } |
| 11686 | } |
| 11687 | |
| 11688 | //********************************************************************************* |
| 11689 | //********************************************************************************* |
| 11690 | //********************************************************************************* |
| 11691 | |
| 11692 | |
| 11693 | static void |
| 11694 | pmEventTimeStamp(uint64_t *recordTS) |
| 11695 | { |
| 11696 | clock_sec_t tsec; |
| 11697 | clock_usec_t tusec; |
| 11698 | |
| 11699 | if (!recordTS) { |
| 11700 | return; |
| 11701 | } |
| 11702 | |
| 11703 | // We assume tsec fits into 32 bits; 32 bits holds enough |
| 11704 | // seconds for 136 years since the epoch in 1970. |
| 11705 | clock_get_calendar_microtime(secs: &tsec, microsecs: &tusec); |
| 11706 | |
| 11707 | |
| 11708 | // Pack the sec & microsec calendar time into a uint64_t, for fun. |
| 11709 | *recordTS = 0; |
| 11710 | *recordTS |= (uint32_t)tusec; |
| 11711 | *recordTS |= ((uint64_t)tsec << 32); |
| 11712 | |
| 11713 | return; |
| 11714 | } |
| 11715 | |
| 11716 | // MARK: - |
| 11717 | // MARK: IORootParent |
| 11718 | |
| 11719 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 11720 | |
| 11721 | OSDefineMetaClassAndFinalStructors(IORootParent, IOService) |
| 11722 | |
| 11723 | // The reason that root domain needs a root parent is to facilitate demand |
| 11724 | // sleep, since a power change from the root parent cannot be vetoed. |
| 11725 | // |
| 11726 | // The above statement is no longer true since root domain now performs |
| 11727 | // demand sleep using overrides. But root parent remains to avoid changing |
| 11728 | // the power tree stacking. Root parent is parked at the max power state. |
| 11729 | |
| 11730 | |
| 11731 | static IOPMPowerState patriarchPowerStates[2] = |
| 11732 | { |
| 11733 | {.version: 1, .capabilityFlags: 0, ON_POWER, .inputPowerRequirement: 0, .staticPower: 0, .stateOrder: 0, .powerToAttain: 0, .timeToAttain: 0, .settleUpTime: 0, .timeToLower: 0, .settleDownTime: 0, .powerDomainBudget: 0}, |
| 11734 | {.version: 1, .capabilityFlags: 0, ON_POWER, .inputPowerRequirement: 0, .staticPower: 0, .stateOrder: 0, .powerToAttain: 0, .timeToAttain: 0, .settleUpTime: 0, .timeToLower: 0, .settleDownTime: 0, .powerDomainBudget: 0}, |
| 11735 | }; |
| 11736 | |
| 11737 | void |
| 11738 | IORootParent::initialize( void ) |
| 11739 | { |
| 11740 | |
| 11741 | gIOPMPSExternalConnectedKey = OSSymbol::withCStringNoCopy(kIOPMPSExternalConnectedKey); |
| 11742 | gIOPMPSExternalChargeCapableKey = OSSymbol::withCStringNoCopy(kIOPMPSExternalChargeCapableKey); |
| 11743 | gIOPMPSBatteryInstalledKey = OSSymbol::withCStringNoCopy(kIOPMPSBatteryInstalledKey); |
| 11744 | gIOPMPSIsChargingKey = OSSymbol::withCStringNoCopy(kIOPMPSIsChargingKey); |
| 11745 | gIOPMPSAtWarnLevelKey = OSSymbol::withCStringNoCopy(kIOPMPSAtWarnLevelKey); |
| 11746 | gIOPMPSAtCriticalLevelKey = OSSymbol::withCStringNoCopy(kIOPMPSAtCriticalLevelKey); |
| 11747 | gIOPMPSCurrentCapacityKey = OSSymbol::withCStringNoCopy(kIOPMPSCurrentCapacityKey); |
| 11748 | gIOPMPSMaxCapacityKey = OSSymbol::withCStringNoCopy(kIOPMPSMaxCapacityKey); |
| 11749 | gIOPMPSDesignCapacityKey = OSSymbol::withCStringNoCopy(kIOPMPSDesignCapacityKey); |
| 11750 | gIOPMPSTimeRemainingKey = OSSymbol::withCStringNoCopy(kIOPMPSTimeRemainingKey); |
| 11751 | gIOPMPSAmperageKey = OSSymbol::withCStringNoCopy(kIOPMPSAmperageKey); |
| 11752 | gIOPMPSVoltageKey = OSSymbol::withCStringNoCopy(kIOPMPSVoltageKey); |
| 11753 | gIOPMPSCycleCountKey = OSSymbol::withCStringNoCopy(kIOPMPSCycleCountKey); |
| 11754 | gIOPMPSMaxErrKey = OSSymbol::withCStringNoCopy(kIOPMPSMaxErrKey); |
| 11755 | gIOPMPSAdapterInfoKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterInfoKey); |
| 11756 | gIOPMPSLocationKey = OSSymbol::withCStringNoCopy(kIOPMPSLocationKey); |
| 11757 | gIOPMPSErrorConditionKey = OSSymbol::withCStringNoCopy(kIOPMPSErrorConditionKey); |
| 11758 | gIOPMPSManufacturerKey = OSSymbol::withCStringNoCopy(kIOPMPSManufacturerKey); |
| 11759 | gIOPMPSManufactureDateKey = OSSymbol::withCStringNoCopy(kIOPMPSManufactureDateKey); |
| 11760 | gIOPMPSModelKey = OSSymbol::withCStringNoCopy(kIOPMPSModelKey); |
| 11761 | gIOPMPSSerialKey = OSSymbol::withCStringNoCopy(kIOPMPSSerialKey); |
| 11762 | gIOPMPSLegacyBatteryInfoKey = OSSymbol::withCStringNoCopy(kIOPMPSLegacyBatteryInfoKey); |
| 11763 | gIOPMPSBatteryHealthKey = OSSymbol::withCStringNoCopy(kIOPMPSBatteryHealthKey); |
| 11764 | gIOPMPSHealthConfidenceKey = OSSymbol::withCStringNoCopy(kIOPMPSHealthConfidenceKey); |
| 11765 | gIOPMPSCapacityEstimatedKey = OSSymbol::withCStringNoCopy(kIOPMPSCapacityEstimatedKey); |
| 11766 | gIOPMPSBatteryChargeStatusKey = OSSymbol::withCStringNoCopy(kIOPMPSBatteryChargeStatusKey); |
| 11767 | gIOPMPSBatteryTemperatureKey = OSSymbol::withCStringNoCopy(kIOPMPSBatteryTemperatureKey); |
| 11768 | gIOPMPSAdapterDetailsKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsKey); |
| 11769 | gIOPMPSChargerConfigurationKey = OSSymbol::withCStringNoCopy(kIOPMPSChargerConfigurationKey); |
| 11770 | gIOPMPSAdapterDetailsIDKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsIDKey); |
| 11771 | gIOPMPSAdapterDetailsWattsKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsWattsKey); |
| 11772 | gIOPMPSAdapterDetailsRevisionKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsRevisionKey); |
| 11773 | gIOPMPSAdapterDetailsSerialNumberKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsSerialNumberKey); |
| 11774 | gIOPMPSAdapterDetailsFamilyKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsFamilyKey); |
| 11775 | gIOPMPSAdapterDetailsAmperageKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsAmperageKey); |
| 11776 | gIOPMPSAdapterDetailsDescriptionKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsDescriptionKey); |
| 11777 | gIOPMPSAdapterDetailsPMUConfigurationKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsPMUConfigurationKey); |
| 11778 | gIOPMPSAdapterDetailsSourceIDKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsSourceIDKey); |
| 11779 | gIOPMPSAdapterDetailsErrorFlagsKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsErrorFlagsKey); |
| 11780 | gIOPMPSAdapterDetailsSharedSourceKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsSharedSourceKey); |
| 11781 | gIOPMPSAdapterDetailsCloakedKey = OSSymbol::withCStringNoCopy(kIOPMPSAdapterDetailsCloakedKey); |
| 11782 | gIOPMPSInvalidWakeSecondsKey = OSSymbol::withCStringNoCopy(kIOPMPSInvalidWakeSecondsKey); |
| 11783 | gIOPMPSPostChargeWaitSecondsKey = OSSymbol::withCStringNoCopy(kIOPMPSPostChargeWaitSecondsKey); |
| 11784 | gIOPMPSPostDishargeWaitSecondsKey = OSSymbol::withCStringNoCopy(kIOPMPSPostDishargeWaitSecondsKey); |
| 11785 | } |
| 11786 | |
| 11787 | bool |
| 11788 | IORootParent::start( IOService * nub ) |
| 11789 | { |
| 11790 | IOService::start(provider: nub); |
| 11791 | attachToParent( parent: getRegistryRoot(), plane: gIOPowerPlane ); |
| 11792 | PMinit(); |
| 11793 | registerPowerDriver(controllingDriver: this, powerStates: patriarchPowerStates, numberOfStates: 2); |
| 11794 | makeUsable(); |
| 11795 | return true; |
| 11796 | } |
| 11797 | |
| 11798 | void |
| 11799 | IORootParent::shutDownSystem( void ) |
| 11800 | { |
| 11801 | } |
| 11802 | |
| 11803 | void |
| 11804 | IORootParent::restartSystem( void ) |
| 11805 | { |
| 11806 | } |
| 11807 | |
| 11808 | void |
| 11809 | IORootParent::sleepSystem( void ) |
| 11810 | { |
| 11811 | } |
| 11812 | |
| 11813 | void |
| 11814 | IORootParent::dozeSystem( void ) |
| 11815 | { |
| 11816 | } |
| 11817 | |
| 11818 | void |
| 11819 | IORootParent::sleepToDoze( void ) |
| 11820 | { |
| 11821 | } |
| 11822 | |
| 11823 | void |
| 11824 | IORootParent::wakeSystem( void ) |
| 11825 | { |
| 11826 | } |
| 11827 | |
| 11828 | OSSharedPtr<OSObject> |
| 11829 | IORootParent::copyProperty( const char * aKey) const |
| 11830 | { |
| 11831 | return IOService::copyProperty(aKey); |
| 11832 | } |
| 11833 | |
| 11834 | uint32_t |
| 11835 | IOPMrootDomain::getWatchdogTimeout() |
| 11836 | { |
| 11837 | if (gSwdSleepWakeTimeout) { |
| 11838 | gSwdSleepTimeout = gSwdWakeTimeout = gSwdSleepWakeTimeout; |
| 11839 | } |
| 11840 | if ((pmTracer->getTracePhase() < kIOPMTracePointSystemSleep) || |
| 11841 | (pmTracer->getTracePhase() == kIOPMTracePointDarkWakeEntry)) { |
| 11842 | return gSwdSleepTimeout ? gSwdSleepTimeout : WATCHDOG_SLEEP_TIMEOUT; |
| 11843 | } else { |
| 11844 | return gSwdWakeTimeout ? gSwdWakeTimeout : WATCHDOG_WAKE_TIMEOUT; |
| 11845 | } |
| 11846 | } |
| 11847 | |
| 11848 | |
| 11849 | #if defined(__i386__) || defined(__x86_64__) || (defined(__arm64__) && HIBERNATION) |
| 11850 | IOReturn |
| 11851 | IOPMrootDomain::restartWithStackshot() |
| 11852 | { |
| 11853 | takeStackshot(true); |
| 11854 | |
| 11855 | return kIOReturnSuccess; |
| 11856 | } |
| 11857 | |
| 11858 | void |
| 11859 | IOPMrootDomain::sleepWakeDebugTrig(bool wdogTrigger) |
| 11860 | { |
| 11861 | takeStackshot(wdogTrigger); |
| 11862 | } |
| 11863 | |
| 11864 | void |
| 11865 | IOPMrootDomain::tracePhase2String(uint32_t tracePhase, const char **phaseString, const char **description) |
| 11866 | { |
| 11867 | switch (tracePhase) { |
| 11868 | case kIOPMTracePointSleepStarted: |
| 11869 | *phaseString = "kIOPMTracePointSleepStarted" ; |
| 11870 | *description = "starting sleep" ; |
| 11871 | break; |
| 11872 | |
| 11873 | case kIOPMTracePointSleepApplications: |
| 11874 | *phaseString = "kIOPMTracePointSleepApplications" ; |
| 11875 | *description = "notifying applications" ; |
| 11876 | break; |
| 11877 | |
| 11878 | case kIOPMTracePointSleepPriorityClients: |
| 11879 | *phaseString = "kIOPMTracePointSleepPriorityClients" ; |
| 11880 | *description = "notifying clients about upcoming system capability changes" ; |
| 11881 | break; |
| 11882 | |
| 11883 | case kIOPMTracePointSleepWillChangeInterests: |
| 11884 | *phaseString = "kIOPMTracePointSleepWillChangeInterests" ; |
| 11885 | *description = "creating hibernation file or while calling rootDomain's clients about upcoming rootDomain's state changes" ; |
| 11886 | break; |
| 11887 | |
| 11888 | case kIOPMTracePointSleepPowerPlaneDrivers: |
| 11889 | *phaseString = "kIOPMTracePointSleepPowerPlaneDrivers" ; |
| 11890 | *description = "calling power state change callbacks" ; |
| 11891 | break; |
| 11892 | |
| 11893 | case kIOPMTracePointSleepDidChangeInterests: |
| 11894 | *phaseString = "kIOPMTracePointSleepDidChangeInterests" ; |
| 11895 | *description = "calling rootDomain's clients about rootDomain's state changes" ; |
| 11896 | break; |
| 11897 | |
| 11898 | case kIOPMTracePointSleepCapabilityClients: |
| 11899 | *phaseString = "kIOPMTracePointSleepCapabilityClients" ; |
| 11900 | *description = "notifying clients about current system capabilities" ; |
| 11901 | break; |
| 11902 | |
| 11903 | case kIOPMTracePointSleepPlatformActions: |
| 11904 | *phaseString = "kIOPMTracePointSleepPlatformActions" ; |
| 11905 | *description = "calling Quiesce/Sleep action callbacks" ; |
| 11906 | break; |
| 11907 | |
| 11908 | case kIOPMTracePointSleepCPUs: |
| 11909 | { |
| 11910 | *phaseString = "kIOPMTracePointSleepCPUs" ; |
| 11911 | #if defined(__i386__) || defined(__x86_64__) |
| 11912 | /* |
| 11913 | * We cannot use the getCPUNumber() method to get the cpu number, since |
| 11914 | * that cpu number is unrelated to the cpu number we need (we need the cpu |
| 11915 | * number as enumerated by the scheduler, NOT the CPU number enumerated |
| 11916 | * by ACPIPlatform as the CPUs are enumerated in MADT order). |
| 11917 | * Instead, pass the Mach processor pointer associated with the current |
| 11918 | * shutdown target so its associated cpu_id can be used in |
| 11919 | * processor_to_datastring. |
| 11920 | */ |
| 11921 | if (currentShutdownTarget != NULL && |
| 11922 | currentShutdownTarget->getMachProcessor() != NULL) { |
| 11923 | const char *sbuf = processor_to_datastring("halting all non-boot CPUs" , |
| 11924 | currentShutdownTarget->getMachProcessor()); |
| 11925 | *description = sbuf; |
| 11926 | } else { |
| 11927 | *description = "halting all non-boot CPUs" ; |
| 11928 | } |
| 11929 | #else |
| 11930 | *description = "halting all non-boot CPUs" ; |
| 11931 | #endif |
| 11932 | break; |
| 11933 | } |
| 11934 | case kIOPMTracePointSleepPlatformDriver: |
| 11935 | *phaseString = "kIOPMTracePointSleepPlatformDriver" ; |
| 11936 | *description = "executing platform specific code" ; |
| 11937 | break; |
| 11938 | |
| 11939 | case kIOPMTracePointHibernate: |
| 11940 | *phaseString = "kIOPMTracePointHibernate" ; |
| 11941 | *description = "writing the hibernation image" ; |
| 11942 | break; |
| 11943 | |
| 11944 | case kIOPMTracePointSystemSleep: |
| 11945 | *phaseString = "kIOPMTracePointSystemSleep" ; |
| 11946 | *description = "in EFI/Bootrom after last point of entry to sleep" ; |
| 11947 | break; |
| 11948 | |
| 11949 | case kIOPMTracePointWakePlatformDriver: |
| 11950 | *phaseString = "kIOPMTracePointWakePlatformDriver" ; |
| 11951 | *description = "executing platform specific code" ; |
| 11952 | break; |
| 11953 | |
| 11954 | |
| 11955 | case kIOPMTracePointWakePlatformActions: |
| 11956 | *phaseString = "kIOPMTracePointWakePlatformActions" ; |
| 11957 | *description = "calling Wake action callbacks" ; |
| 11958 | break; |
| 11959 | |
| 11960 | case kIOPMTracePointWakeCPUs: |
| 11961 | *phaseString = "kIOPMTracePointWakeCPUs" ; |
| 11962 | *description = "starting non-boot CPUs" ; |
| 11963 | break; |
| 11964 | |
| 11965 | case kIOPMTracePointWakeWillPowerOnClients: |
| 11966 | *phaseString = "kIOPMTracePointWakeWillPowerOnClients" ; |
| 11967 | *description = "sending kIOMessageSystemWillPowerOn message to kernel and userspace clients" ; |
| 11968 | break; |
| 11969 | |
| 11970 | case kIOPMTracePointWakeWillChangeInterests: |
| 11971 | *phaseString = "kIOPMTracePointWakeWillChangeInterests" ; |
| 11972 | *description = "calling rootDomain's clients about upcoming rootDomain's state changes" ; |
| 11973 | break; |
| 11974 | |
| 11975 | case kIOPMTracePointWakeDidChangeInterests: |
| 11976 | *phaseString = "kIOPMTracePointWakeDidChangeInterests" ; |
| 11977 | *description = "calling rootDomain's clients about completed rootDomain's state changes" ; |
| 11978 | break; |
| 11979 | |
| 11980 | case kIOPMTracePointWakePowerPlaneDrivers: |
| 11981 | *phaseString = "kIOPMTracePointWakePowerPlaneDrivers" ; |
| 11982 | *description = "calling power state change callbacks" ; |
| 11983 | break; |
| 11984 | |
| 11985 | case kIOPMTracePointWakeCapabilityClients: |
| 11986 | *phaseString = "kIOPMTracePointWakeCapabilityClients" ; |
| 11987 | *description = "informing clients about current system capabilities" ; |
| 11988 | break; |
| 11989 | |
| 11990 | case kIOPMTracePointWakeApplications: |
| 11991 | *phaseString = "kIOPMTracePointWakeApplications" ; |
| 11992 | *description = "sending asynchronous kIOMessageSystemHasPoweredOn message to userspace clients" ; |
| 11993 | break; |
| 11994 | |
| 11995 | case kIOPMTracePointDarkWakeEntry: |
| 11996 | *phaseString = "kIOPMTracePointDarkWakeEntry" ; |
| 11997 | *description = "entering darkwake on way to sleep" ; |
| 11998 | break; |
| 11999 | |
| 12000 | case kIOPMTracePointDarkWakeExit: |
| 12001 | *phaseString = "kIOPMTracePointDarkWakeExit" ; |
| 12002 | *description = "entering fullwake from darkwake" ; |
| 12003 | break; |
| 12004 | |
| 12005 | default: |
| 12006 | *phaseString = NULL; |
| 12007 | *description = NULL; |
| 12008 | } |
| 12009 | } |
| 12010 | |
| 12011 | void |
| 12012 | IOPMrootDomain::saveFailureData2File() |
| 12013 | { |
| 12014 | unsigned int len = 0; |
| 12015 | char failureStr[512]; |
| 12016 | errno_t error; |
| 12017 | char *outbuf; |
| 12018 | OSNumber *statusCode; |
| 12019 | uint64_t pmStatusCode = 0; |
| 12020 | uint32_t phaseData = 0; |
| 12021 | uint32_t phaseDetail = 0; |
| 12022 | bool efiFailure = false; |
| 12023 | |
| 12024 | OSSharedPtr<OSObject> statusCodeProp = copyProperty(kIOPMSleepWakeFailureCodeKey); |
| 12025 | statusCode = OSDynamicCast(OSNumber, statusCodeProp.get()); |
| 12026 | if (statusCode) { |
| 12027 | pmStatusCode = statusCode->unsigned64BitValue(); |
| 12028 | phaseData = pmStatusCode & 0xFFFFFFFF; |
| 12029 | phaseDetail = (pmStatusCode >> 32) & 0xFFFFFFFF; |
| 12030 | if ((phaseData & 0xFF) == kIOPMTracePointSystemSleep) { |
| 12031 | LOG("Sleep Wake failure in EFI\n" ); |
| 12032 | efiFailure = true; |
| 12033 | failureStr[0] = 0; |
| 12034 | snprintf(failureStr, sizeof(failureStr), "Sleep Wake failure in EFI\n\nFailure code:: 0x%08x 0x%08x\n\nPlease IGNORE the below stackshot\n" , phaseDetail, phaseData); |
| 12035 | len = (typeof(len))strnlen(failureStr, sizeof(failureStr)); |
| 12036 | } |
| 12037 | } |
| 12038 | |
| 12039 | if (!efiFailure) { |
| 12040 | if (PEReadNVRAMProperty(kIOSleepWakeFailurePanic, NULL, &len)) { |
| 12041 | swd_flags |= SWD_BOOT_BY_SW_WDOG; |
| 12042 | PERemoveNVRAMProperty(kIOSleepWakeFailurePanic); |
| 12043 | // dump panic will handle saving nvram data |
| 12044 | return; |
| 12045 | } |
| 12046 | |
| 12047 | /* Keeping this around for capturing data during power |
| 12048 | * button press */ |
| 12049 | |
| 12050 | if (!PEReadNVRAMProperty(kIOSleepWakeFailureString, NULL, &len)) { |
| 12051 | DLOG("No sleep wake failure string\n" ); |
| 12052 | return; |
| 12053 | } |
| 12054 | if (len == 0) { |
| 12055 | DLOG("Ignoring zero byte SleepWake failure string\n" ); |
| 12056 | goto exit; |
| 12057 | } |
| 12058 | |
| 12059 | // if PMStatus code is zero, delete stackshot and return |
| 12060 | if (statusCode) { |
| 12061 | if (((pmStatusCode & 0xFFFFFFFF) & 0xFF) == 0) { |
| 12062 | // there was no sleep wake failure |
| 12063 | // this can happen if delete stackshot was called |
| 12064 | // before take stackshot completed. Let us delete any |
| 12065 | // sleep wake failure data in nvram |
| 12066 | DLOG("Deleting stackshot on successful wake\n" ); |
| 12067 | deleteStackshot(); |
| 12068 | return; |
| 12069 | } |
| 12070 | } |
| 12071 | |
| 12072 | if (len > sizeof(failureStr)) { |
| 12073 | len = sizeof(failureStr); |
| 12074 | } |
| 12075 | failureStr[0] = 0; |
| 12076 | PEReadNVRAMProperty(kIOSleepWakeFailureString, failureStr, &len); |
| 12077 | } |
| 12078 | if (failureStr[0] != 0) { |
| 12079 | error = sleepWakeDebugSaveFile(kSleepWakeFailureStringFile, failureStr, len); |
| 12080 | if (error) { |
| 12081 | DLOG("Failed to save SleepWake failure string to file. error:%d\n" , error); |
| 12082 | } else { |
| 12083 | DLOG("Saved SleepWake failure string to file.\n" ); |
| 12084 | } |
| 12085 | } |
| 12086 | |
| 12087 | if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) { |
| 12088 | goto exit; |
| 12089 | } |
| 12090 | |
| 12091 | if (swd_buffer) { |
| 12092 | unsigned int len = 0; |
| 12093 | errno_t error; |
| 12094 | char nvram_var_name_buffer[20]; |
| 12095 | unsigned int concat_len = 0; |
| 12096 | swd_hdr *hdr = NULL; |
| 12097 | |
| 12098 | |
| 12099 | hdr = (swd_hdr *)swd_buffer; |
| 12100 | outbuf = (char *)hdr + hdr->spindump_offset; |
| 12101 | OSBoundedArrayRef<char> boundedOutBuf(outbuf, hdr->alloc_size - hdr->spindump_offset); |
| 12102 | |
| 12103 | for (int i = 0; i < 8; i++) { |
| 12104 | snprintf(nvram_var_name_buffer, sizeof(nvram_var_name_buffer), "%s%02d" , SWD_STACKSHOT_VAR_PREFIX, i + 1); |
| 12105 | if (!PEReadNVRAMProperty(nvram_var_name_buffer, NULL, &len)) { |
| 12106 | LOG("No SleepWake blob to read beyond chunk %d\n" , i); |
| 12107 | break; |
| 12108 | } |
| 12109 | if (PEReadNVRAMProperty(nvram_var_name_buffer, boundedOutBuf.slice(concat_len, len).data(), &len) == FALSE) { |
| 12110 | PERemoveNVRAMProperty(nvram_var_name_buffer); |
| 12111 | LOG("Could not read the property :-(\n" ); |
| 12112 | break; |
| 12113 | } |
| 12114 | PERemoveNVRAMProperty(nvram_var_name_buffer); |
| 12115 | concat_len += len; |
| 12116 | } |
| 12117 | LOG("Concatenated length for the SWD blob %d\n" , concat_len); |
| 12118 | |
| 12119 | if (concat_len) { |
| 12120 | error = sleepWakeDebugSaveFile(kSleepWakeStacksFilename, outbuf, concat_len); |
| 12121 | if (error) { |
| 12122 | LOG("Failed to save SleepWake zipped data to file. error:%d\n" , error); |
| 12123 | } else { |
| 12124 | LOG("Saved SleepWake zipped data to file.\n" ); |
| 12125 | } |
| 12126 | } else { |
| 12127 | // There is a sleep wake failure string but no stackshot |
| 12128 | // Write a placeholder stacks file so that swd runs |
| 12129 | snprintf(outbuf, 20, "%s" , "No stackshot data\n" ); |
| 12130 | error = sleepWakeDebugSaveFile(kSleepWakeStacksFilename, outbuf, 20); |
| 12131 | if (error) { |
| 12132 | LOG("Failed to save SleepWake zipped data to file. error:%d\n" , error); |
| 12133 | } else { |
| 12134 | LOG("Saved SleepWake zipped data to file.\n" ); |
| 12135 | } |
| 12136 | } |
| 12137 | } else { |
| 12138 | LOG("No buffer allocated to save failure stackshot\n" ); |
| 12139 | } |
| 12140 | |
| 12141 | |
| 12142 | gRootDomain->swd_lock = 0; |
| 12143 | exit: |
| 12144 | PERemoveNVRAMProperty(kIOSleepWakeFailureString); |
| 12145 | return; |
| 12146 | } |
| 12147 | |
| 12148 | |
| 12149 | void |
| 12150 | IOPMrootDomain::getFailureData(thread_t *thread, char *failureStr, size_t strLen) |
| 12151 | { |
| 12152 | OSSharedPtr<IORegistryIterator> iter; |
| 12153 | OSSharedPtr<const OSSymbol> kextName = NULL; |
| 12154 | IORegistryEntry * entry; |
| 12155 | IOService * node; |
| 12156 | bool nodeFound = false; |
| 12157 | |
| 12158 | const void * callMethod = NULL; |
| 12159 | const char * objectName = NULL; |
| 12160 | uint32_t timeout = getWatchdogTimeout(); |
| 12161 | const char * phaseString = NULL; |
| 12162 | const char * phaseDescription = NULL; |
| 12163 | |
| 12164 | IOPMServiceInterestNotifier *notifier = OSDynamicCast(IOPMServiceInterestNotifier, notifierObject.get()); |
| 12165 | uint32_t tracePhase = pmTracer->getTracePhase(); |
| 12166 | |
| 12167 | *thread = NULL; |
| 12168 | if ((tracePhase < kIOPMTracePointSystemSleep) || (tracePhase == kIOPMTracePointDarkWakeEntry)) { |
| 12169 | snprintf(failureStr, strLen, "Sleep transition timed out after %d seconds" , timeout); |
| 12170 | } else { |
| 12171 | snprintf(failureStr, strLen, "Wake transition timed out after %d seconds" , timeout); |
| 12172 | } |
| 12173 | tracePhase2String(tracePhase, &phaseString, &phaseDescription); |
| 12174 | |
| 12175 | if (notifierThread) { |
| 12176 | if (notifier && (notifier->identifier)) { |
| 12177 | objectName = notifier->identifier->getCStringNoCopy(); |
| 12178 | } |
| 12179 | *thread = notifierThread; |
| 12180 | } else { |
| 12181 | iter = IORegistryIterator::iterateOver( |
| 12182 | getPMRootDomain(), gIOPowerPlane, kIORegistryIterateRecursively); |
| 12183 | |
| 12184 | if (iter) { |
| 12185 | while ((entry = iter->getNextObject())) { |
| 12186 | node = OSDynamicCast(IOService, entry); |
| 12187 | if (!node) { |
| 12188 | continue; |
| 12189 | } |
| 12190 | if (OSDynamicCast(IOPowerConnection, node)) { |
| 12191 | continue; |
| 12192 | } |
| 12193 | |
| 12194 | if (node->getBlockingDriverCall(thread, &callMethod)) { |
| 12195 | nodeFound = true; |
| 12196 | break; |
| 12197 | } |
| 12198 | } |
| 12199 | } |
| 12200 | if (nodeFound) { |
| 12201 | kextName = copyKextIdentifierWithAddress((vm_address_t) callMethod); |
| 12202 | if (kextName) { |
| 12203 | objectName = kextName->getCStringNoCopy(); |
| 12204 | } |
| 12205 | } |
| 12206 | } |
| 12207 | if (phaseDescription) { |
| 12208 | strlcat(failureStr, " while " , strLen); |
| 12209 | strlcat(failureStr, phaseDescription, strLen); |
| 12210 | strlcat(failureStr, "." , strLen); |
| 12211 | } |
| 12212 | if (objectName) { |
| 12213 | strlcat(failureStr, " Suspected bundle: " , strLen); |
| 12214 | strlcat(failureStr, objectName, strLen); |
| 12215 | strlcat(failureStr, "." , strLen); |
| 12216 | } |
| 12217 | if (*thread) { |
| 12218 | char threadName[40]; |
| 12219 | snprintf(threadName, sizeof(threadName), " Thread 0x%llx." , thread_tid(*thread)); |
| 12220 | strlcat(failureStr, threadName, strLen); |
| 12221 | } |
| 12222 | |
| 12223 | DLOG("%s\n" , failureStr); |
| 12224 | } |
| 12225 | |
| 12226 | struct swd_stackshot_compressed_data { |
| 12227 | z_output_func zoutput; |
| 12228 | size_t zipped; |
| 12229 | uint64_t totalbytes; |
| 12230 | uint64_t lastpercent; |
| 12231 | IOReturn error; |
| 12232 | unsigned outremain; |
| 12233 | unsigned outlen; |
| 12234 | unsigned writes; |
| 12235 | Bytef * outbuf; |
| 12236 | }; |
| 12237 | struct swd_stackshot_compressed_data swd_zip_var = { }; |
| 12238 | |
| 12239 | static void * |
| 12240 | swd_zs_alloc(void *__unused ref, u_int items, u_int size) |
| 12241 | { |
| 12242 | void *result; |
| 12243 | LOG("Alloc in zipping %d items of size %d\n" , items, size); |
| 12244 | |
| 12245 | result = (void *)(swd_zs_zmem + swd_zs_zoffset); |
| 12246 | swd_zs_zoffset += ~31L & (31 + (items * size)); // 32b align for vector crc |
| 12247 | LOG("Offset %zu\n" , swd_zs_zoffset); |
| 12248 | return result; |
| 12249 | } |
| 12250 | |
| 12251 | static int |
| 12252 | swd_zinput(z_streamp strm, Bytef *buf, unsigned size) |
| 12253 | { |
| 12254 | unsigned len; |
| 12255 | |
| 12256 | len = strm->avail_in; |
| 12257 | |
| 12258 | if (len > size) { |
| 12259 | len = size; |
| 12260 | } |
| 12261 | if (len == 0) { |
| 12262 | return 0; |
| 12263 | } |
| 12264 | |
| 12265 | if (strm->next_in != (Bytef *) strm) { |
| 12266 | memcpy(buf, strm->next_in, len); |
| 12267 | } else { |
| 12268 | bzero(buf, len); |
| 12269 | } |
| 12270 | |
| 12271 | strm->adler = z_crc32(strm->adler, buf, len); |
| 12272 | |
| 12273 | strm->avail_in -= len; |
| 12274 | strm->next_in += len; |
| 12275 | strm->total_in += len; |
| 12276 | |
| 12277 | return (int)len; |
| 12278 | } |
| 12279 | |
| 12280 | static int |
| 12281 | swd_zoutput(z_streamp strm, Bytef *buf, unsigned len) |
| 12282 | { |
| 12283 | unsigned int i = 0; |
| 12284 | // if outlen > max size don't add to the buffer |
| 12285 | assert(buf != NULL); |
| 12286 | if (strm && buf) { |
| 12287 | if (swd_zip_var.outlen + len > SWD_COMPRESSED_BUFSIZE) { |
| 12288 | LOG("No space to GZIP... not writing to NVRAM\n" ); |
| 12289 | return len; |
| 12290 | } |
| 12291 | } |
| 12292 | for (i = 0; i < len; i++) { |
| 12293 | *(swd_zip_var.outbuf + swd_zip_var.outlen + i) = *(buf + i); |
| 12294 | } |
| 12295 | swd_zip_var.outlen += len; |
| 12296 | return len; |
| 12297 | } |
| 12298 | |
| 12299 | static void |
| 12300 | swd_zs_free(void * __unused ref, void * __unused ptr) |
| 12301 | { |
| 12302 | } |
| 12303 | |
| 12304 | static int |
| 12305 | swd_compress(char *inPtr, char *outPtr, size_t numBytes) |
| 12306 | { |
| 12307 | int wbits = 12; |
| 12308 | int memlevel = 3; |
| 12309 | |
| 12310 | if (((unsigned int) numBytes) != numBytes) { |
| 12311 | return 0; |
| 12312 | } |
| 12313 | |
| 12314 | if (!swd_zs.zalloc) { |
| 12315 | swd_zs.zalloc = swd_zs_alloc; |
| 12316 | swd_zs.zfree = swd_zs_free; |
| 12317 | if (deflateInit2(&swd_zs, Z_BEST_SPEED, Z_DEFLATED, wbits + 16, memlevel, Z_DEFAULT_STRATEGY)) { |
| 12318 | // allocation failed |
| 12319 | bzero(&swd_zs, sizeof(swd_zs)); |
| 12320 | // swd_zs_zoffset = 0; |
| 12321 | } else { |
| 12322 | LOG("PMRD inited the zlib allocation routines\n" ); |
| 12323 | } |
| 12324 | } |
| 12325 | |
| 12326 | swd_zip_var.zipped = 0; |
| 12327 | swd_zip_var.totalbytes = 0; // should this be the max that we have? |
| 12328 | swd_zip_var.lastpercent = 0; |
| 12329 | swd_zip_var.error = kIOReturnSuccess; |
| 12330 | swd_zip_var.outremain = 0; |
| 12331 | swd_zip_var.outlen = 0; |
| 12332 | swd_zip_var.writes = 0; |
| 12333 | swd_zip_var.outbuf = (Bytef *)outPtr; |
| 12334 | |
| 12335 | swd_zip_var.totalbytes = numBytes; |
| 12336 | |
| 12337 | swd_zs.avail_in = 0; |
| 12338 | swd_zs.next_in = NULL; |
| 12339 | swd_zs.avail_out = 0; |
| 12340 | swd_zs.next_out = NULL; |
| 12341 | |
| 12342 | deflateResetWithIO(&swd_zs, swd_zinput, swd_zoutput); |
| 12343 | |
| 12344 | z_stream *zs; |
| 12345 | int zr; |
| 12346 | zs = &swd_zs; |
| 12347 | |
| 12348 | while (swd_zip_var.error >= 0) { |
| 12349 | if (!zs->avail_in) { |
| 12350 | zs->next_in = (unsigned char *)inPtr ? (Bytef *)inPtr : (Bytef *)zs; /* zero marker? */ |
| 12351 | zs->avail_in = (unsigned int) numBytes; |
| 12352 | } |
| 12353 | if (!zs->avail_out) { |
| 12354 | zs->next_out = (Bytef *)zs; |
| 12355 | zs->avail_out = UINT32_MAX; |
| 12356 | } |
| 12357 | zr = deflate(zs, Z_NO_FLUSH); |
| 12358 | if (Z_STREAM_END == zr) { |
| 12359 | break; |
| 12360 | } |
| 12361 | if (zr != Z_OK) { |
| 12362 | LOG("ZERR %d\n" , zr); |
| 12363 | swd_zip_var.error = zr; |
| 12364 | } else { |
| 12365 | if (zs->total_in == numBytes) { |
| 12366 | break; |
| 12367 | } |
| 12368 | } |
| 12369 | } |
| 12370 | |
| 12371 | //now flush the stream |
| 12372 | while (swd_zip_var.error >= 0) { |
| 12373 | if (!zs->avail_out) { |
| 12374 | zs->next_out = (Bytef *)zs; |
| 12375 | zs->avail_out = UINT32_MAX; |
| 12376 | } |
| 12377 | zr = deflate(zs, Z_FINISH); |
| 12378 | if (Z_STREAM_END == zr) { |
| 12379 | break; |
| 12380 | } |
| 12381 | if (zr != Z_OK) { |
| 12382 | LOG("ZERR %d\n" , zr); |
| 12383 | swd_zip_var.error = zr; |
| 12384 | } else { |
| 12385 | if (zs->total_in == numBytes) { |
| 12386 | LOG("Total output size %d\n" , swd_zip_var.outlen); |
| 12387 | break; |
| 12388 | } |
| 12389 | } |
| 12390 | } |
| 12391 | |
| 12392 | return swd_zip_var.outlen; |
| 12393 | } |
| 12394 | |
| 12395 | void |
| 12396 | IOPMrootDomain::deleteStackshot() |
| 12397 | { |
| 12398 | if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) { |
| 12399 | // takeStackshot hasn't completed |
| 12400 | return; |
| 12401 | } |
| 12402 | LOG("Deleting any sleepwake failure data in nvram\n" ); |
| 12403 | |
| 12404 | PERemoveNVRAMProperty(kIOSleepWakeFailureString); |
| 12405 | char nvram_var_name_buf[20]; |
| 12406 | for (int i = 0; i < 8; i++) { |
| 12407 | snprintf(nvram_var_name_buf, sizeof(nvram_var_name_buf), "%s%02d" , SWD_STACKSHOT_VAR_PREFIX, i + 1); |
| 12408 | if (PERemoveNVRAMProperty(nvram_var_name_buf) == false) { |
| 12409 | LOG("Removing %s returned false\n" , nvram_var_name_buf); |
| 12410 | } |
| 12411 | } |
| 12412 | // force NVRAM sync |
| 12413 | if (PEWriteNVRAMProperty(kIONVRAMSyncNowPropertyKey, kIONVRAMSyncNowPropertyKey, (unsigned int) strlen(kIONVRAMSyncNowPropertyKey)) == false) { |
| 12414 | DLOG("Failed to force nvram sync\n" ); |
| 12415 | } |
| 12416 | gRootDomain->swd_lock = 0; |
| 12417 | } |
| 12418 | |
| 12419 | void |
| 12420 | IOPMrootDomain::takeStackshot(bool wdogTrigger) |
| 12421 | { |
| 12422 | swd_hdr * hdr = NULL; |
| 12423 | int cnt = 0; |
| 12424 | int max_cnt; |
| 12425 | pid_t pid = 0; |
| 12426 | kern_return_t kr = KERN_SUCCESS; |
| 12427 | uint64_t flags; |
| 12428 | |
| 12429 | char * dstAddr; |
| 12430 | uint32_t size; |
| 12431 | uint32_t bytesRemaining; |
| 12432 | unsigned bytesWritten = 0; |
| 12433 | |
| 12434 | char failureStr[512]; |
| 12435 | thread_t thread = NULL; |
| 12436 | const char * swfPanic = "swfPanic" ; |
| 12437 | |
| 12438 | uint32_t bufSize; |
| 12439 | int success = 0; |
| 12440 | |
| 12441 | #if defined(__i386__) || defined(__x86_64__) |
| 12442 | const bool concise = false; |
| 12443 | #else |
| 12444 | const bool concise = true; |
| 12445 | #endif |
| 12446 | |
| 12447 | if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) { |
| 12448 | return; |
| 12449 | } |
| 12450 | |
| 12451 | failureStr[0] = 0; |
| 12452 | if ((kIOSleepWakeWdogOff & gIOKitDebug) || systemBooting || systemShutdown || gWillShutdown) { |
| 12453 | return; |
| 12454 | } |
| 12455 | |
| 12456 | if (wdogTrigger) { |
| 12457 | getFailureData(&thread, failureStr, sizeof(failureStr)); |
| 12458 | |
| 12459 | if (concise || (PEGetCoprocessorVersion() >= kCoprocessorVersion2)) { |
| 12460 | goto skip_stackshot; |
| 12461 | } |
| 12462 | } else { |
| 12463 | AbsoluteTime now; |
| 12464 | uint64_t nsec; |
| 12465 | clock_get_uptime(&now); |
| 12466 | SUB_ABSOLUTETIME(&now, &gIOLastWakeAbsTime); |
| 12467 | absolutetime_to_nanoseconds(now, &nsec); |
| 12468 | snprintf(failureStr, sizeof(failureStr), "Power button pressed during wake transition after %u ms.\n" , ((int)((nsec) / NSEC_PER_MSEC))); |
| 12469 | } |
| 12470 | |
| 12471 | if (swd_buffer == NULL) { |
| 12472 | sleepWakeDebugMemAlloc(); |
| 12473 | if (swd_buffer == NULL) { |
| 12474 | return; |
| 12475 | } |
| 12476 | } |
| 12477 | hdr = (swd_hdr *)swd_buffer; |
| 12478 | bufSize = hdr->alloc_size; |
| 12479 | |
| 12480 | dstAddr = (char*)hdr + hdr->spindump_offset; |
| 12481 | flags = STACKSHOT_KCDATA_FORMAT | STACKSHOT_NO_IO_STATS | STACKSHOT_SAVE_KEXT_LOADINFO | STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY | STACKSHOT_THREAD_WAITINFO | STACKSHOT_INCLUDE_DRIVER_THREADS_IN_KERNEL; |
| 12482 | |
| 12483 | /* If not wdogTrigger only take kernel tasks stackshot |
| 12484 | */ |
| 12485 | if (wdogTrigger) { |
| 12486 | pid = -1; |
| 12487 | max_cnt = 3; |
| 12488 | } else { |
| 12489 | pid = 0; |
| 12490 | max_cnt = 2; |
| 12491 | } |
| 12492 | |
| 12493 | /* Attempt to take stackshot with all ACTIVE_KERNEL_THREADS |
| 12494 | * If we run out of space, take stackshot with only kernel task |
| 12495 | */ |
| 12496 | while (success == 0 && cnt < max_cnt) { |
| 12497 | bytesRemaining = bufSize - hdr->spindump_offset; |
| 12498 | cnt++; |
| 12499 | DLOG("Taking snapshot. bytesRemaining: %d\n" , bytesRemaining); |
| 12500 | |
| 12501 | size = bytesRemaining; |
| 12502 | kr = stack_snapshot_from_kernel(pid, dstAddr, size, flags, 0, 0, &bytesWritten); |
| 12503 | DLOG("stack_snapshot_from_kernel returned 0x%x. pid: %d bufsize:0x%x flags:0x%llx bytesWritten: %d\n" , |
| 12504 | kr, pid, size, flags, bytesWritten); |
| 12505 | if (kr == KERN_INSUFFICIENT_BUFFER_SIZE) { |
| 12506 | if (pid == -1) { |
| 12507 | pid = 0; |
| 12508 | } else if (flags & STACKSHOT_INCLUDE_DRIVER_THREADS_IN_KERNEL) { |
| 12509 | flags = flags & ~STACKSHOT_INCLUDE_DRIVER_THREADS_IN_KERNEL; |
| 12510 | } else { |
| 12511 | LOG("Insufficient buffer size for only kernel task\n" ); |
| 12512 | break; |
| 12513 | } |
| 12514 | } |
| 12515 | if (kr == KERN_SUCCESS) { |
| 12516 | if (bytesWritten == 0) { |
| 12517 | MSG("Failed to get stackshot(0x%x) bufsize:0x%x flags:0x%llx\n" , kr, size, flags); |
| 12518 | continue; |
| 12519 | } |
| 12520 | bytesRemaining -= bytesWritten; |
| 12521 | hdr->spindump_size = (bufSize - bytesRemaining - hdr->spindump_offset); |
| 12522 | |
| 12523 | memset(hdr->reason, 0x20, sizeof(hdr->reason)); |
| 12524 | |
| 12525 | // Compress stackshot and save to NVRAM |
| 12526 | { |
| 12527 | char *outbuf = (char *)swd_compressed_buffer; |
| 12528 | int outlen = 0; |
| 12529 | int num_chunks = 0; |
| 12530 | int max_chunks = 0; |
| 12531 | int leftover = 0; |
| 12532 | char nvram_var_name_buffer[20]; |
| 12533 | |
| 12534 | outlen = swd_compress((char*)hdr + hdr->spindump_offset, outbuf, bytesWritten); |
| 12535 | |
| 12536 | if (outlen) { |
| 12537 | max_chunks = outlen / (2096 - 200); |
| 12538 | leftover = outlen % (2096 - 200); |
| 12539 | |
| 12540 | if (max_chunks < 8) { |
| 12541 | for (num_chunks = 0; num_chunks < max_chunks; num_chunks++) { |
| 12542 | snprintf(nvram_var_name_buffer, sizeof(nvram_var_name_buffer), "%s%02d" , SWD_STACKSHOT_VAR_PREFIX, num_chunks + 1); |
| 12543 | if (PEWriteNVRAMPropertyWithCopy(nvram_var_name_buffer, (outbuf + (num_chunks * (2096 - 200))), (2096 - 200)) == FALSE) { |
| 12544 | LOG("Failed to update NVRAM %d\n" , num_chunks); |
| 12545 | break; |
| 12546 | } |
| 12547 | } |
| 12548 | if (leftover) { |
| 12549 | snprintf(nvram_var_name_buffer, sizeof(nvram_var_name_buffer), "%s%02d" , SWD_STACKSHOT_VAR_PREFIX, num_chunks + 1); |
| 12550 | if (PEWriteNVRAMPropertyWithCopy(nvram_var_name_buffer, (outbuf + (num_chunks * (2096 - 200))), leftover) == FALSE) { |
| 12551 | LOG("Failed to update NVRAM with leftovers\n" ); |
| 12552 | } |
| 12553 | } |
| 12554 | success = 1; |
| 12555 | LOG("Successfully saved stackshot to NVRAM\n" ); |
| 12556 | } else { |
| 12557 | if (pid == -1) { |
| 12558 | LOG("Compressed failure stackshot is too large. size=%d bytes\n" , outlen); |
| 12559 | pid = 0; |
| 12560 | } else if (flags & STACKSHOT_INCLUDE_DRIVER_THREADS_IN_KERNEL) { |
| 12561 | LOG("Compressed failure stackshot of kernel+dexts is too large size=%d bytes\n" , outlen); |
| 12562 | flags = flags & ~STACKSHOT_INCLUDE_DRIVER_THREADS_IN_KERNEL; |
| 12563 | } else { |
| 12564 | LOG("Compressed failure stackshot of only kernel is too large size=%d bytes\n" , outlen); |
| 12565 | break; |
| 12566 | } |
| 12567 | } |
| 12568 | } |
| 12569 | } |
| 12570 | } |
| 12571 | } |
| 12572 | |
| 12573 | if (failureStr[0]) { |
| 12574 | // append sleep-wake failure code |
| 12575 | char traceCode[80]; |
| 12576 | snprintf(traceCode, sizeof(traceCode), "\nFailure code:: 0x%08x %08x\n" , |
| 12577 | pmTracer->getTraceData(), pmTracer->getTracePhase()); |
| 12578 | strlcat(failureStr, traceCode, sizeof(failureStr)); |
| 12579 | if (PEWriteNVRAMProperty(kIOSleepWakeFailureString, failureStr, (unsigned int) strnlen(failureStr, sizeof(failureStr))) == false) { |
| 12580 | DLOG("Failed to write SleepWake failure string\n" ); |
| 12581 | } |
| 12582 | } |
| 12583 | |
| 12584 | // force NVRAM sync |
| 12585 | if (PEWriteNVRAMProperty(kIONVRAMSyncNowPropertyKey, kIONVRAMSyncNowPropertyKey, (unsigned int) strlen(kIONVRAMSyncNowPropertyKey)) == false) { |
| 12586 | DLOG("Failed to force nvram sync\n" ); |
| 12587 | } |
| 12588 | |
| 12589 | skip_stackshot: |
| 12590 | if (wdogTrigger) { |
| 12591 | if (PEGetCoprocessorVersion() < kCoprocessorVersion2) { |
| 12592 | if (swd_flags & SWD_BOOT_BY_SW_WDOG) { |
| 12593 | // If current boot is due to this watch dog trigger restart in previous boot, |
| 12594 | // then don't trigger again until at least 1 successful sleep & wake. |
| 12595 | if (!(sleepCnt && (displayWakeCnt || darkWakeCnt))) { |
| 12596 | LOG("Shutting down due to repeated Sleep/Wake failures\n" ); |
| 12597 | updateTasksSuspend(kTasksSuspendSuspended, kTasksSuspendNoChange); |
| 12598 | PEHaltRestart(kPEHaltCPU); |
| 12599 | return; |
| 12600 | } |
| 12601 | } |
| 12602 | if (gSwdPanic == 0) { |
| 12603 | LOG("Calling panic prevented by swd_panic boot-args. Calling restart" ); |
| 12604 | updateTasksSuspend(kTasksSuspendSuspended, kTasksSuspendNoChange); |
| 12605 | PEHaltRestart(kPERestartCPU); |
| 12606 | } |
| 12607 | } |
| 12608 | if (!concise && (PEWriteNVRAMProperty(kIOSleepWakeFailurePanic, swfPanic, (unsigned int) strlen(swfPanic)) == false)) { |
| 12609 | DLOG("Failed to write SleepWake failure panic key\n" ); |
| 12610 | } |
| 12611 | #if defined(__x86_64__) |
| 12612 | if (thread) { |
| 12613 | panic_with_thread_context(0, NULL, DEBUGGER_OPTION_ATTEMPTCOREDUMPANDREBOOT, thread, "%s" , failureStr); |
| 12614 | } else |
| 12615 | #endif /* defined(__x86_64__) */ |
| 12616 | { |
| 12617 | panic_with_options(0, NULL, DEBUGGER_OPTION_ATTEMPTCOREDUMPANDREBOOT, "%s" , failureStr); |
| 12618 | } |
| 12619 | } else { |
| 12620 | gRootDomain->swd_lock = 0; |
| 12621 | return; |
| 12622 | } |
| 12623 | } |
| 12624 | |
| 12625 | void |
| 12626 | IOPMrootDomain::sleepWakeDebugMemAlloc() |
| 12627 | { |
| 12628 | vm_size_t size = SWD_STACKSHOT_SIZE + SWD_COMPRESSED_BUFSIZE + SWD_ZLIB_BUFSIZE; |
| 12629 | |
| 12630 | swd_hdr *hdr = NULL; |
| 12631 | void *bufPtr = NULL; |
| 12632 | |
| 12633 | OSSharedPtr<IOBufferMemoryDescriptor> memDesc; |
| 12634 | |
| 12635 | |
| 12636 | if (kIOSleepWakeWdogOff & gIOKitDebug) { |
| 12637 | return; |
| 12638 | } |
| 12639 | |
| 12640 | if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) { |
| 12641 | return; |
| 12642 | } |
| 12643 | |
| 12644 | memDesc = IOBufferMemoryDescriptor::inTaskWithOptions( |
| 12645 | kernel_task, kIODirectionIn | kIOMemoryMapperNone, |
| 12646 | size); |
| 12647 | if (memDesc == NULL) { |
| 12648 | DLOG("Failed to allocate Memory descriptor for sleepWake debug\n" ); |
| 12649 | goto exit; |
| 12650 | } |
| 12651 | |
| 12652 | bufPtr = memDesc->getBytesNoCopy(); |
| 12653 | |
| 12654 | // Carve out memory for zlib routines |
| 12655 | swd_zs_zmem = (vm_offset_t)bufPtr; |
| 12656 | bufPtr = (char *)bufPtr + SWD_ZLIB_BUFSIZE; |
| 12657 | |
| 12658 | // Carve out memory for compressed stackshots |
| 12659 | swd_compressed_buffer = bufPtr; |
| 12660 | bufPtr = (char *)bufPtr + SWD_COMPRESSED_BUFSIZE; |
| 12661 | |
| 12662 | // Remaining is used for holding stackshot |
| 12663 | hdr = (swd_hdr *)bufPtr; |
| 12664 | memset(hdr, 0, sizeof(swd_hdr)); |
| 12665 | |
| 12666 | hdr->signature = SWD_HDR_SIGNATURE; |
| 12667 | hdr->alloc_size = SWD_STACKSHOT_SIZE; |
| 12668 | |
| 12669 | hdr->spindump_offset = sizeof(swd_hdr); |
| 12670 | swd_buffer = (void *)hdr; |
| 12671 | swd_memDesc = os::move(memDesc); |
| 12672 | DLOG("SleepWake debug buffer size:0x%x spindump offset:0x%x\n" , hdr->alloc_size, hdr->spindump_offset); |
| 12673 | |
| 12674 | exit: |
| 12675 | gRootDomain->swd_lock = 0; |
| 12676 | } |
| 12677 | |
| 12678 | void |
| 12679 | IOPMrootDomain::sleepWakeDebugSpinDumpMemAlloc() |
| 12680 | { |
| 12681 | #if UNUSED |
| 12682 | vm_size_t size = SWD_SPINDUMP_SIZE; |
| 12683 | |
| 12684 | swd_hdr *hdr = NULL; |
| 12685 | |
| 12686 | OSSharedPtr<IOBufferMemoryDescriptor> memDesc; |
| 12687 | |
| 12688 | if (!OSCompareAndSwap(0, 1, &gRootDomain->swd_lock)) { |
| 12689 | return; |
| 12690 | } |
| 12691 | |
| 12692 | memDesc = IOBufferMemoryDescriptor::inTaskWithOptions( |
| 12693 | kernel_task, kIODirectionIn | kIOMemoryMapperNone, |
| 12694 | SWD_SPINDUMP_SIZE); |
| 12695 | |
| 12696 | if (memDesc == NULL) { |
| 12697 | DLOG("Failed to allocate Memory descriptor for sleepWake debug spindump\n" ); |
| 12698 | goto exit; |
| 12699 | } |
| 12700 | |
| 12701 | |
| 12702 | hdr = (swd_hdr *)memDesc->getBytesNoCopy(); |
| 12703 | memset(hdr, 0, sizeof(swd_hdr)); |
| 12704 | |
| 12705 | hdr->signature = SWD_HDR_SIGNATURE; |
| 12706 | hdr->alloc_size = size; |
| 12707 | |
| 12708 | hdr->spindump_offset = sizeof(swd_hdr); |
| 12709 | swd_spindump_buffer = (void *)hdr; |
| 12710 | swd_spindump_memDesc = os::move(memDesc); |
| 12711 | |
| 12712 | exit: |
| 12713 | gRootDomain->swd_lock = 0; |
| 12714 | #endif /* UNUSED */ |
| 12715 | } |
| 12716 | |
| 12717 | void |
| 12718 | IOPMrootDomain::sleepWakeDebugEnableWdog() |
| 12719 | { |
| 12720 | } |
| 12721 | |
| 12722 | bool |
| 12723 | IOPMrootDomain::sleepWakeDebugIsWdogEnabled() |
| 12724 | { |
| 12725 | return !systemBooting && !systemShutdown && !gWillShutdown; |
| 12726 | } |
| 12727 | |
| 12728 | void |
| 12729 | IOPMrootDomain::sleepWakeDebugSaveSpinDumpFile() |
| 12730 | { |
| 12731 | swd_hdr *hdr = NULL; |
| 12732 | errno_t error = EIO; |
| 12733 | |
| 12734 | if (swd_spindump_buffer && gSpinDumpBufferFull) { |
| 12735 | hdr = (swd_hdr *)swd_spindump_buffer; |
| 12736 | |
| 12737 | error = sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayStacks.dump" , |
| 12738 | (char*)hdr + hdr->spindump_offset, hdr->spindump_size); |
| 12739 | |
| 12740 | if (error) { |
| 12741 | return; |
| 12742 | } |
| 12743 | |
| 12744 | sleepWakeDebugSaveFile("/var/tmp/SleepWakeDelayLog.dump" , |
| 12745 | (char*)hdr + offsetof(swd_hdr, UUID), |
| 12746 | sizeof(swd_hdr) - offsetof(swd_hdr, UUID)); |
| 12747 | |
| 12748 | gSpinDumpBufferFull = false; |
| 12749 | } |
| 12750 | } |
| 12751 | |
| 12752 | errno_t |
| 12753 | IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len) |
| 12754 | { |
| 12755 | struct vnode *vp = NULL; |
| 12756 | vfs_context_t ctx = vfs_context_create(vfs_context_current()); |
| 12757 | kauth_cred_t cred = vfs_context_ucred(ctx); |
| 12758 | struct vnode_attr va; |
| 12759 | errno_t error = EIO; |
| 12760 | |
| 12761 | if (vnode_open(name, (O_CREAT | FWRITE | O_NOFOLLOW), |
| 12762 | S_IRUSR | S_IRGRP | S_IROTH, VNODE_LOOKUP_NOFOLLOW, &vp, ctx) != 0) { |
| 12763 | LOG("Failed to open the file %s\n" , name); |
| 12764 | swd_flags |= SWD_FILEOP_ERROR; |
| 12765 | goto exit; |
| 12766 | } |
| 12767 | VATTR_INIT(&va); |
| 12768 | VATTR_WANTED(&va, va_nlink); |
| 12769 | /* Don't dump to non-regular files or files with links. */ |
| 12770 | if (vp->v_type != VREG || |
| 12771 | vnode_getattr(vp, &va, ctx) || va.va_nlink != 1) { |
| 12772 | LOG("Bailing as this is not a regular file\n" ); |
| 12773 | swd_flags |= SWD_FILEOP_ERROR; |
| 12774 | goto exit; |
| 12775 | } |
| 12776 | VATTR_INIT(&va); |
| 12777 | VATTR_SET(&va, va_data_size, 0); |
| 12778 | vnode_setattr(vp, &va, ctx); |
| 12779 | |
| 12780 | |
| 12781 | if (buf != NULL) { |
| 12782 | error = vn_rdwr(UIO_WRITE, vp, buf, len, 0, |
| 12783 | UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, cred, (int *) NULL, vfs_context_proc(ctx)); |
| 12784 | if (error != 0) { |
| 12785 | LOG("Failed to save sleep wake log. err 0x%x\n" , error); |
| 12786 | swd_flags |= SWD_FILEOP_ERROR; |
| 12787 | } else { |
| 12788 | DLOG("Saved %d bytes to file %s\n" , len, name); |
| 12789 | } |
| 12790 | } |
| 12791 | |
| 12792 | exit: |
| 12793 | if (vp) { |
| 12794 | vnode_close(vp, FWRITE, ctx); |
| 12795 | } |
| 12796 | if (ctx) { |
| 12797 | vfs_context_rele(ctx); |
| 12798 | } |
| 12799 | |
| 12800 | return error; |
| 12801 | } |
| 12802 | |
| 12803 | #else /* defined(__i386__) || defined(__x86_64__) */ |
| 12804 | |
| 12805 | void |
| 12806 | IOPMrootDomain::sleepWakeDebugTrig(bool restart) |
| 12807 | { |
| 12808 | if (restart) { |
| 12809 | if (gSwdPanic == 0) { |
| 12810 | return; |
| 12811 | } |
| 12812 | panic("Sleep/Wake hang detected" ); |
| 12813 | return; |
| 12814 | } |
| 12815 | } |
| 12816 | |
| 12817 | void |
| 12818 | IOPMrootDomain::takeStackshot(bool restart) |
| 12819 | { |
| 12820 | #pragma unused(restart) |
| 12821 | } |
| 12822 | |
| 12823 | void |
| 12824 | IOPMrootDomain::deleteStackshot() |
| 12825 | { |
| 12826 | } |
| 12827 | |
| 12828 | void |
| 12829 | IOPMrootDomain::sleepWakeDebugMemAlloc() |
| 12830 | { |
| 12831 | } |
| 12832 | |
| 12833 | void |
| 12834 | IOPMrootDomain::saveFailureData2File() |
| 12835 | { |
| 12836 | } |
| 12837 | |
| 12838 | void |
| 12839 | IOPMrootDomain::sleepWakeDebugEnableWdog() |
| 12840 | { |
| 12841 | } |
| 12842 | |
| 12843 | bool |
| 12844 | IOPMrootDomain::sleepWakeDebugIsWdogEnabled() |
| 12845 | { |
| 12846 | return false; |
| 12847 | } |
| 12848 | |
| 12849 | void |
| 12850 | IOPMrootDomain::sleepWakeDebugSaveSpinDumpFile() |
| 12851 | { |
| 12852 | } |
| 12853 | |
| 12854 | errno_t |
| 12855 | IOPMrootDomain::sleepWakeDebugSaveFile(const char *name, char *buf, int len) |
| 12856 | { |
| 12857 | return 0; |
| 12858 | } |
| 12859 | |
| 12860 | #endif /* defined(__i386__) || defined(__x86_64__) */ |
| 12861 | |
| 12862 | |