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 | |