Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1546 lines
38 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. misc.c
  5. Abstract:
  6. This module implements miscellaneous power management functions
  7. Author:
  8. Ken Reneris (kenr) 19-July-1994
  9. Revision History:
  10. --*/
  11. #include "pop.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(PAGE,PoInitializeDeviceObject)
  14. #pragma alloc_text(PAGE,PopCleanupPowerState)
  15. #pragma alloc_text(PAGE,PopChangeCapability)
  16. #pragma alloc_text(PAGE,PopExceptionFilter)
  17. #pragma alloc_text(PAGELK,PopSystemStateString)
  18. #pragma alloc_text(PAGE,PopOpenPowerKey)
  19. #pragma alloc_text(PAGE,PopInitializePowerPolicySimulate)
  20. #pragma alloc_text(PAGE,PopSaveHeuristics)
  21. #pragma alloc_text(PAGE,PoInvalidateDevicePowerRelations)
  22. #pragma alloc_text(PAGE,PoGetLightestSystemStateForEject)
  23. #pragma alloc_text(PAGE,PoGetDevicePowerState)
  24. #pragma alloc_text(PAGE, PopUnlockAfterSleepWorker)
  25. #if DBG
  26. #pragma alloc_text(PAGE, PopPowerActionString)
  27. #pragma alloc_text(PAGE, PopAssertPolicyLockOwned)
  28. #endif
  29. #endif
  30. //
  31. // TCP/IP checksum that we use if it is available
  32. //
  33. ULONG
  34. tcpxsum(
  35. IN ULONG cksum,
  36. IN PUCHAR buf,
  37. IN ULONG_PTR len
  38. );
  39. VOID
  40. PoInitializeDeviceObject (
  41. IN PDEVOBJ_EXTENSION DeviceObjectExtension
  42. )
  43. {
  44. //
  45. // default to unspecified power states, not Inrush, Pageable.
  46. //
  47. DeviceObjectExtension->PowerFlags = 0L;
  48. PopSetDoSystemPowerState(DeviceObjectExtension, PowerSystemUnspecified);
  49. PopSetDoDevicePowerState(DeviceObjectExtension, PowerDeviceUnspecified);
  50. DeviceObjectExtension->Dope = NULL;
  51. }
  52. VOID
  53. PoRunDownDeviceObject (
  54. IN PDEVICE_OBJECT DeviceObject
  55. )
  56. {
  57. KIRQL OldIrql;
  58. PDEVOBJ_EXTENSION doe;
  59. PDEVICE_OBJECT_POWER_EXTENSION pdope;
  60. doe = (PDEVOBJ_EXTENSION) DeviceObject->DeviceObjectExtension;
  61. //
  62. // force off any idle counter that may be active
  63. //
  64. PoRegisterDeviceForIdleDetection(
  65. DeviceObject, 0, 0, PowerDeviceUnspecified
  66. );
  67. PopLockIrpSerialList( &OldIrql );
  68. if (PopFindIrpByDeviceObject(DeviceObject, DevicePowerState) ||
  69. PopFindIrpByDeviceObject(DeviceObject, SystemPowerState))
  70. {
  71. PopInternalAddToDumpFile( NULL, 0, DeviceObject, NULL, NULL, NULL );
  72. KeBugCheckEx(
  73. DRIVER_POWER_STATE_FAILURE,
  74. DEVICE_DELETED_WITH_POWER_IRPS,
  75. (ULONG_PTR) DeviceObject,
  76. 0,
  77. 0
  78. );
  79. }
  80. PopUnlockIrpSerialList( OldIrql );
  81. //
  82. // knock down notify structures attached to this DO.
  83. //
  84. PopRunDownSourceTargetList(DeviceObject);
  85. //
  86. // knock down the dope
  87. //
  88. pdope = doe->Dope;
  89. if (pdope) {
  90. ASSERT(ExPageLockHandle);
  91. MmLockPagableSectionByHandle(ExPageLockHandle);
  92. PopAcquireVolumeLock ();
  93. PopLockDopeGlobal(&OldIrql);
  94. if (pdope->Volume.Flink) {
  95. RemoveEntryList (&pdope->Volume);
  96. doe->Dope->Volume.Flink = NULL;
  97. doe->Dope->Volume.Blink = NULL;
  98. }
  99. doe->Dope = NULL;
  100. ExFreePool(pdope);
  101. PopUnlockDopeGlobal(OldIrql);
  102. PopReleaseVolumeLock ();
  103. MmUnlockPagableImageSection (ExPageLockHandle);
  104. }
  105. }
  106. VOID
  107. PopCleanupPowerState (
  108. IN OUT PUCHAR PowerState
  109. )
  110. /*++
  111. Routine Description:
  112. Used to cleanup the Thread->Tcb.PowerState or the Process->Pcb.PowerState
  113. during thread or process rundown
  114. Arguments:
  115. PowerState - Which power state to cleanup
  116. Return Value:
  117. None
  118. --*/
  119. {
  120. ULONG OldFlags;
  121. //
  122. // If power state is set, clean it up
  123. //
  124. if (*PowerState) {
  125. PopAcquirePolicyLock ();
  126. //
  127. // Get current settings and clear them
  128. //
  129. OldFlags = *PowerState | ES_CONTINUOUS;
  130. *PowerState = 0;
  131. //
  132. // Account for attribute settings which are being cleared
  133. //
  134. PopApplyAttributeState (ES_CONTINUOUS, OldFlags);
  135. //
  136. // Done
  137. //
  138. PopReleasePolicyLock (TRUE);
  139. }
  140. }
  141. VOID
  142. PoNotifySystemTimeSet (
  143. VOID
  144. )
  145. /*++
  146. Routine Description:
  147. Called by KE after a new system time has been set. Enqueues
  148. a notification to the proper system components that the time
  149. has been changed.
  150. Arguments:
  151. None
  152. Return Value:
  153. None
  154. --*/
  155. {
  156. KIRQL OldIrql;
  157. if (PopEventCallout) {
  158. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  159. ExNotifyCallback (ExCbSetSystemTime, NULL, NULL);
  160. PopGetPolicyWorker (PO_WORKER_TIME_CHANGE);
  161. PopCheckForWork (TRUE);
  162. KeLowerIrql (OldIrql);
  163. }
  164. }
  165. VOID
  166. PopChangeCapability (
  167. IN PBOOLEAN PresentFlag,
  168. IN BOOLEAN IsPresent
  169. )
  170. {
  171. //
  172. // If feature wasn't present before, it is now. Re-compute policies
  173. // as system capabilities changed
  174. //
  175. if (*PresentFlag != IsPresent) {
  176. *PresentFlag = IsPresent;
  177. PopResetCurrentPolicies ();
  178. PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
  179. }
  180. }
  181. #if DBG
  182. VOID
  183. PopAssertPolicyLockOwned(
  184. VOID
  185. )
  186. {
  187. PAGED_CODE();
  188. ASSERT (PopPolicyLockThread == KeGetCurrentThread());
  189. }
  190. #endif // DBG
  191. VOID
  192. FASTCALL
  193. PopInternalAddToDumpFile (
  194. IN OPTIONAL PVOID DataBlock,
  195. IN OPTIONAL ULONG DataBlockSize,
  196. IN OPTIONAL PDEVICE_OBJECT DeviceObject,
  197. IN OPTIONAL PDRIVER_OBJECT DriverObject,
  198. IN OPTIONAL PDEVOBJ_EXTENSION Doe,
  199. IN OPTIONAL PDEVICE_OBJECT_POWER_EXTENSION Dope
  200. )
  201. /*++
  202. Routine Description:
  203. Called just before bugchecking. This function will ensure that
  204. things we care about get into the dump file for later debugging.
  205. It should be noted that many of the parameters can be derived
  206. from each other. However, since we're about to bugcheck, we run
  207. a risk of double-faulting while we're chasing pointers. Therefore,
  208. we give the caller the option to override some of the pointers by
  209. sending us a pointer directly.
  210. Arguments:
  211. DataBlock - Generic block of memory to place into the dump file.
  212. DataBlockSize - Size of DataBlock (in bytes).
  213. DeviceObject - DEVICE_OBJECT to place into dump file.
  214. DriverObject - DRIVER_OBJECT to place into dump file.
  215. N.B. This overrides a value we may find in DeviceObject->DriverObject
  216. Doe - DEVOBJ_EXTENSION to place into dump file.
  217. N.B. This overrides a value we may find in DeviceObject->DeviceObjectExtension
  218. Dope - DEVICE_OBJECT_POWER_EXTENSION to place into the dump file.
  219. N.B. This overrides a value we may find in Doe->Dope (or by induction, DeviceObject->DeviceObjectExtension->Dope
  220. Return Value:
  221. None
  222. --*/
  223. {
  224. PDRIVER_OBJECT lPDriverObject = NULL;
  225. PDEVOBJ_EXTENSION lPDoe = NULL;
  226. PDEVICE_OBJECT_POWER_EXTENSION lPDope = NULL;
  227. //
  228. // Insert any parameters that were sent in.
  229. //
  230. if( DataBlock ) {
  231. IoAddTriageDumpDataBlock(
  232. PAGE_ALIGN(DataBlock),
  233. (DataBlockSize ? BYTES_TO_PAGES(DataBlockSize) : PAGE_SIZE) );
  234. }
  235. if( DeviceObject ) {
  236. IoAddTriageDumpDataBlock(DeviceObject, sizeof(DEVICE_OBJECT));
  237. }
  238. if( DriverObject ) {
  239. lPDriverObject = DriverObject;
  240. } else if( (DeviceObject) && (DeviceObject->DriverObject) ) {
  241. lPDriverObject = DeviceObject->DriverObject;
  242. }
  243. if( lPDriverObject ) {
  244. IoAddTriageDumpDataBlock(lPDriverObject, lPDriverObject->Size);
  245. if( lPDriverObject->DriverName.Buffer ) {
  246. IoAddTriageDumpDataBlock(lPDriverObject->DriverName.Buffer, lPDriverObject->DriverName.Length);
  247. }
  248. }
  249. if( Doe ) {
  250. lPDoe = Doe;
  251. } else if( DeviceObject ) {
  252. lPDoe = DeviceObject->DeviceObjectExtension;
  253. }
  254. if( lPDoe ) {
  255. IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe), sizeof(DEVOBJ_EXTENSION));
  256. if( lPDoe->DeviceNode ) {
  257. IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->DeviceNode), PAGE_SIZE);
  258. }
  259. if( lPDoe->AttachedTo ) {
  260. IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->AttachedTo), PAGE_SIZE);
  261. }
  262. if( lPDoe->Vpb ) {
  263. IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDoe->Vpb), PAGE_SIZE);
  264. }
  265. }
  266. if( Dope ) {
  267. lPDope = Dope;
  268. } else if( lPDoe ) {
  269. lPDope = lPDoe->Dope;
  270. }
  271. if( lPDope ) {
  272. IoAddTriageDumpDataBlock(PAGE_ALIGN(lPDope), sizeof(DEVICE_OBJECT_POWER_EXTENSION));
  273. }
  274. //
  275. // Globals that may be of interest.
  276. //
  277. IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopHiberFile), sizeof(POP_HIBER_FILE));
  278. IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopAction), sizeof(POP_POWER_ACTION));
  279. if(PopAction.DevState) {
  280. IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopAction.DevState)), sizeof(POP_DEVICE_SYS_STATE));
  281. }
  282. if(PopAction.HiberContext) {
  283. IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopAction.HiberContext)), sizeof(POP_HIBER_CONTEXT));
  284. }
  285. IoAddTriageDumpDataBlock(PAGE_ALIGN(&PopCB), sizeof(POP_COMPOSITE_BATTERY));
  286. if(PopCB.StatusIrp) {
  287. IoAddTriageDumpDataBlock(PAGE_ALIGN(&(PopCB.StatusIrp)), sizeof(IRP));
  288. }
  289. IoAddTriageDumpDataBlock(PAGE_ALIGN(PopAttributes), sizeof(POP_STATE_ATTRIBUTE) * POP_NUMBER_ATTRIBUTES);
  290. }
  291. VOID
  292. FASTCALL
  293. _PopInternalError (
  294. IN ULONG BugCode
  295. )
  296. {
  297. KeBugCheckEx( INTERNAL_POWER_ERROR,
  298. POP_INTERNAL,
  299. BugCode,
  300. 0,
  301. 0);
  302. }
  303. EXCEPTION_DISPOSITION
  304. PopExceptionFilter (
  305. IN PEXCEPTION_POINTERS ExceptionInfo,
  306. IN BOOLEAN AllowRaisedException
  307. )
  308. {
  309. //
  310. // If handler wants raised expceptions, check the exception code
  311. //
  312. if (AllowRaisedException) {
  313. switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
  314. case STATUS_INVALID_PARAMETER:
  315. case STATUS_INVALID_PARAMETER_1:
  316. case STATUS_INVALID_PARAMETER_2:
  317. return EXCEPTION_EXECUTE_HANDLER;
  318. }
  319. }
  320. //
  321. // Not allowed
  322. //
  323. PoPrint (PO_ERROR, ("PoExceptionFilter: exr %x, cxr %x",
  324. ExceptionInfo->ExceptionRecord,
  325. ExceptionInfo->ContextRecord
  326. ));
  327. PopInternalAddToDumpFile( ExceptionInfo->ExceptionRecord,
  328. sizeof(EXCEPTION_RECORD),
  329. NULL,
  330. NULL,
  331. NULL,
  332. NULL );
  333. PopInternalAddToDumpFile( ExceptionInfo->ContextRecord,
  334. sizeof(CONTEXT),
  335. NULL,
  336. NULL,
  337. NULL,
  338. NULL );
  339. KeBugCheckEx( INTERNAL_POWER_ERROR,
  340. 0x101,
  341. POP_MISC,
  342. (ULONG_PTR)ExceptionInfo,
  343. 0 );
  344. }
  345. PCHAR
  346. PopSystemStateString(
  347. IN SYSTEM_POWER_STATE SystemState
  348. )
  349. // This function is not DBG because...
  350. {
  351. PCHAR p;
  352. switch (SystemState) {
  353. case PowerSystemUnspecified: p = "Unspecified"; break;
  354. case PowerSystemWorking: p = "Working"; break;
  355. case PowerSystemSleeping1: p = "Sleeping1"; break;
  356. case PowerSystemSleeping2: p = "Sleeping2"; break;
  357. case PowerSystemSleeping3: p = "Sleeping3"; break;
  358. case PowerSystemHibernate: p = "Hibernate"; break;
  359. case PowerSystemShutdown: p = "Shutdown"; break;
  360. default: p = "?";
  361. }
  362. return p;
  363. }
  364. #if DBG
  365. PCHAR
  366. PopPowerActionString(
  367. IN POWER_ACTION PowerAction
  368. )
  369. // This function is not DBG because...
  370. {
  371. PCHAR p;
  372. switch (PowerAction) {
  373. case PowerActionNone: p = "None"; break;
  374. case PowerActionSleep: p = "Sleep"; break;
  375. case PowerActionHibernate: p = "Hibernate"; break;
  376. case PowerActionShutdown: p = "Shutdown"; break;
  377. case PowerActionShutdownReset: p = "ShutdownReset"; break;
  378. case PowerActionShutdownOff: p = "ShutdownOff"; break;
  379. case PowerActionWarmEject: p = "WarmEject"; break;
  380. default: p = "?";
  381. }
  382. return p;
  383. }
  384. #endif
  385. #if DBG
  386. //
  387. // PowerTrace variables
  388. //
  389. ULONG PoPowerTraceControl = 0L;
  390. ULONG PoPowerTraceCount = 0L;
  391. PCHAR PoPowerTraceMinorCode[] = {
  392. "wait", "seq", "set", "query"
  393. };
  394. PCHAR PoPowerTracePoint[] = {
  395. "calldrv", "present", "startnxt", "setstate", "complete"
  396. };
  397. PCHAR PoPowerType[] = {
  398. "sys", "dev"
  399. };
  400. VOID
  401. PoPowerTracePrint(
  402. ULONG TracePoint,
  403. ULONG_PTR Caller,
  404. ULONG_PTR CallerCaller,
  405. ULONG_PTR DeviceObject,
  406. ULONG_PTR Arg1,
  407. ULONG_PTR Arg2
  408. )
  409. /*
  410. Example:
  411. PLOG,00015,startnxt,c@ffea1345,cc@ffea5643,do@80081234,irp@8100ff00,ios@8100ff10,query,sys,3
  412. */
  413. {
  414. PIO_STACK_LOCATION Isp;
  415. PCHAR tracename;
  416. ULONG j;
  417. ULONG tp;
  418. UNREFERENCED_PARAMETER (Caller);
  419. UNREFERENCED_PARAMETER (CallerCaller);
  420. PoPowerTraceCount++;
  421. if (PoPowerTraceControl & TracePoint) {
  422. tracename = NULL;
  423. tp = TracePoint;
  424. for (j = 0; j < 33; tp = tp >> 1, j = j+1)
  425. {
  426. if (tp & 1) {
  427. tracename = PoPowerTracePoint[j];
  428. j = 33;
  429. }
  430. }
  431. DbgPrint("PLOG,%05ld,%8s,do@%08lx",
  432. PoPowerTraceCount,tracename,DeviceObject
  433. );
  434. if ((TracePoint == POWERTRACE_CALL) ||
  435. (TracePoint == POWERTRACE_PRESENT) ||
  436. (TracePoint == POWERTRACE_STARTNEXT))
  437. {
  438. DbgPrint(",irp@%08lx,isp@%08lx",Arg1,Arg2);
  439. Isp = (PIO_STACK_LOCATION)Arg2;
  440. DbgPrint(",%5s", PoPowerTraceMinorCode[Isp->MinorFunction]);
  441. if ((Isp->MinorFunction == IRP_MN_SET_POWER) ||
  442. (Isp->MinorFunction == IRP_MN_QUERY_POWER))
  443. {
  444. DbgPrint(",%s,%d",
  445. PoPowerType[Isp->Parameters.Power.Type],
  446. ((ULONG)Isp->Parameters.Power.State.DeviceState)-1 // hack - works for sys state too
  447. );
  448. }
  449. } else if (TracePoint == POWERTRACE_SETSTATE) {
  450. DbgPrint(",,,,%s,%d", PoPowerType[Arg1], Arg2-1);
  451. } else if (TracePoint == POWERTRACE_COMPLETE) {
  452. DbgPrint(",irp@%08lx,isp@%08lx",Arg1,Arg2);
  453. }
  454. DbgPrint("\n");
  455. }
  456. return;
  457. }
  458. #endif
  459. ULONG PoSimpleCheck(IN ULONG PartialSum,
  460. IN PVOID SourceVa,
  461. IN ULONG_PTR Length)
  462. {
  463. // Just use the TCP/IP check sum
  464. //
  465. return tcpxsum(PartialSum, (PUCHAR)SourceVa, Length);
  466. }
  467. NTSTATUS
  468. PopOpenPowerKey (
  469. OUT PHANDLE Handle
  470. )
  471. /*++
  472. Routine Description:
  473. Open and return the handle to the power policy key in the registry
  474. Arguments:
  475. Handle - Handle to power policy key
  476. Return Value:
  477. Status
  478. --*/
  479. {
  480. UNICODE_STRING UnicodeString;
  481. OBJECT_ATTRIBUTES ObjectAttributes;
  482. NTSTATUS Status;
  483. HANDLE BaseHandle;
  484. ULONG disposition;
  485. //
  486. // Open current control set
  487. //
  488. InitializeObjectAttributes(
  489. &ObjectAttributes,
  490. &CmRegistryMachineSystemCurrentControlSet,
  491. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  492. NULL,
  493. (PSECURITY_DESCRIPTOR) NULL
  494. );
  495. Status = ZwOpenKey (
  496. &BaseHandle,
  497. KEY_READ | KEY_WRITE,
  498. &ObjectAttributes
  499. );
  500. if (!NT_SUCCESS(Status)) {
  501. return Status;
  502. }
  503. //
  504. // Open power branch
  505. //
  506. RtlInitUnicodeString (&UnicodeString, PopRegKey);
  507. InitializeObjectAttributes(
  508. &ObjectAttributes,
  509. &UnicodeString,
  510. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  511. BaseHandle,
  512. (PSECURITY_DESCRIPTOR) NULL
  513. );
  514. Status = ZwCreateKey (
  515. Handle,
  516. KEY_READ | KEY_WRITE,
  517. &ObjectAttributes,
  518. 0,
  519. (PUNICODE_STRING) NULL,
  520. REG_OPTION_NON_VOLATILE,
  521. &disposition
  522. );
  523. ZwClose (BaseHandle);
  524. return Status;
  525. }
  526. VOID
  527. PopInitializePowerPolicySimulate(
  528. VOID
  529. )
  530. /*++
  531. Routine Description:
  532. Reads PopSimulate out of the registry. Also applies any overrides that might
  533. be required as a result of the installed system (hydra for example)
  534. Arguments:
  535. NONE.
  536. Return Value:
  537. Status
  538. --*/
  539. {
  540. UNICODE_STRING UnicodeString;
  541. OBJECT_ATTRIBUTES ObjectAttributes;
  542. NTSTATUS Status;
  543. HANDLE BaseHandle;
  544. HANDLE Handle;
  545. ULONG Length;
  546. ULONG disposition;
  547. struct {
  548. KEY_VALUE_PARTIAL_INFORMATION Inf;
  549. ULONG Data;
  550. } PartialInformation;
  551. PAGED_CODE();
  552. //
  553. // Open current control set
  554. //
  555. InitializeObjectAttributes(
  556. &ObjectAttributes,
  557. &CmRegistryMachineSystemCurrentControlSet,
  558. OBJ_CASE_INSENSITIVE,
  559. NULL,
  560. (PSECURITY_DESCRIPTOR) NULL
  561. );
  562. Status = ZwOpenKey(
  563. &BaseHandle,
  564. KEY_READ,
  565. &ObjectAttributes
  566. );
  567. if (!NT_SUCCESS(Status)) {
  568. goto done;
  569. }
  570. // Get the right key
  571. RtlInitUnicodeString (&UnicodeString, PopSimulateRegKey);
  572. InitializeObjectAttributes(
  573. &ObjectAttributes,
  574. &UnicodeString,
  575. OBJ_CASE_INSENSITIVE,
  576. BaseHandle,
  577. (PSECURITY_DESCRIPTOR) NULL
  578. );
  579. Status = ZwCreateKey(
  580. &Handle,
  581. KEY_READ,
  582. &ObjectAttributes,
  583. 0,
  584. (PUNICODE_STRING) NULL,
  585. REG_OPTION_NON_VOLATILE,
  586. &disposition
  587. );
  588. ZwClose(BaseHandle);
  589. if(!NT_SUCCESS(Status)) {
  590. goto done;
  591. }
  592. //
  593. // Get the value of the simulation
  594. //
  595. RtlInitUnicodeString (&UnicodeString,PopSimulateRegName);
  596. Status = ZwQueryValueKey(
  597. Handle,
  598. &UnicodeString,
  599. KeyValuePartialInformation,
  600. &PartialInformation,
  601. sizeof (PartialInformation),
  602. &Length
  603. );
  604. ZwClose (Handle);
  605. if (!NT_SUCCESS(Status)) {
  606. goto done;
  607. }
  608. //
  609. // Check to make sure the retrieved data makes sense
  610. //
  611. if(PartialInformation.Inf.DataLength != sizeof(ULONG)) {
  612. goto done;
  613. }
  614. //
  615. // Initialize PopSimulate
  616. //
  617. PopSimulate = *((PULONG)(PartialInformation.Inf.Data));
  618. done:
  619. return;
  620. }
  621. VOID
  622. PopSaveHeuristics (
  623. VOID
  624. )
  625. /*++
  626. Routine Description:
  627. Open and return the handle to the power policy key in the registry
  628. Arguments:
  629. Handle - Handle to power policy key
  630. Return Value:
  631. Status
  632. --*/
  633. {
  634. HANDLE handle;
  635. UNICODE_STRING UnicodeString;
  636. NTSTATUS Status;
  637. ASSERT_POLICY_LOCK_OWNED();
  638. Status = PopOpenPowerKey (&handle);
  639. if (NT_SUCCESS(Status)) {
  640. PopHeuristics.Dirty = FALSE;
  641. RtlInitUnicodeString (&UnicodeString, PopHeuristicsRegName);
  642. Status = ZwSetValueKey (
  643. handle,
  644. &UnicodeString,
  645. 0L,
  646. REG_BINARY,
  647. &PopHeuristics,
  648. sizeof (PopHeuristics)
  649. );
  650. ZwClose(handle);
  651. }
  652. }
  653. VOID
  654. PoInvalidateDevicePowerRelations(
  655. PDEVICE_OBJECT DeviceObject
  656. )
  657. /*++
  658. Routine Description:
  659. This routine is called by IoInvalidateDeviceRelations when the
  660. type of invalidation is for power relations.
  661. It will will knock down the notify network around the supplied
  662. device object.
  663. Arguments:
  664. DeviceObject - supplies the address of the device object whose
  665. power relations are now invalid.
  666. Return Value:
  667. None.
  668. --*/
  669. {
  670. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  671. PopRunDownSourceTargetList(DeviceObject);
  672. return;
  673. }
  674. NTSTATUS
  675. PoGetLightestSystemStateForEject(
  676. IN BOOLEAN DockBeingEjected,
  677. IN BOOLEAN HotEjectSupported,
  678. IN BOOLEAN WarmEjectSupported,
  679. OUT PSYSTEM_POWER_STATE LightestSleepState
  680. )
  681. /*++
  682. Routine Description:
  683. This routine is called by ntos\pnp\pnpevent.c to determine the lightest
  684. sleep state an eject operation can be executed in. This function is to be
  685. called after all appropriate batteries/power adapters have been *removed*,
  686. but before the eject has occured.
  687. Arguments:
  688. DockBeingEjected - TRUE iff a dock is among the items that may be
  689. ejected.
  690. HotEjectSupported - TRUE if the device being ejected supports S0 VCR
  691. style eject.
  692. WarmEjectSupported - TRUE if the device being ejected supports S1-S4
  693. style warm ejection.
  694. LightestSleepState - Set to the lightest sleep state the device can be
  695. ejected in. On error, this is set to
  696. PowerSystemUnspecified.
  697. N.B. If both HotEjectSupported and WarmEjectSupported are FALSE, it is
  698. assumed this device is "user" ejectable in S0 (ie. Hot ejectable).
  699. Return Value:
  700. NTSTATUS - If there is insufficient power to do the indicated operation,
  701. STATUS_INSUFFICIENT_POWER is returned.
  702. --*/
  703. {
  704. SYSTEM_BATTERY_STATE systemBatteryInfo;
  705. UNICODE_STRING unicodeString;
  706. NTSTATUS status;
  707. HANDLE handle;
  708. ULONG length;
  709. ULONG currentPercentage;
  710. UCHAR ejectPartialInfo[SIZEOF_EJECT_PARTIAL_INFO];
  711. PUNDOCK_POWER_RESTRICTIONS undockRestrictions;
  712. PKEY_VALUE_PARTIAL_INFORMATION partialInfoHeader;
  713. PAGED_CODE();
  714. //
  715. // Preinit sleep to failure.
  716. //
  717. *LightestSleepState = PowerSystemUnspecified;
  718. //
  719. // If neither, then it's a "user" assisted hot eject.
  720. //
  721. if ((!HotEjectSupported) && (!WarmEjectSupported)) {
  722. HotEjectSupported = TRUE;
  723. }
  724. //
  725. // If it's not a dock device being ejected, we assume no great changes
  726. // in power will occur after the eject. Therefore our policy is simple,
  727. // if we can't do hot eject, we'll try warm eject in the best possible
  728. // sleep state.
  729. //
  730. if (!DockBeingEjected) {
  731. if (HotEjectSupported) {
  732. *LightestSleepState = PowerSystemWorking;
  733. } else {
  734. ASSERT(WarmEjectSupported);
  735. *LightestSleepState = PowerSystemSleeping1;
  736. }
  737. return STATUS_SUCCESS;
  738. }
  739. //
  740. // We are going to eject a dock, so we retrieve our undock power policy.
  741. //
  742. status = PopOpenPowerKey (&handle);
  743. if (!NT_SUCCESS(status)) {
  744. return status;
  745. }
  746. // Get the right key
  747. RtlInitUnicodeString (&unicodeString, PopUndockPolicyRegName);
  748. status = ZwQueryValueKey (
  749. handle,
  750. &unicodeString,
  751. KeyValuePartialInformation,
  752. &ejectPartialInfo[0],
  753. sizeof (ejectPartialInfo),
  754. &length
  755. );
  756. ZwClose (handle);
  757. if ((!NT_SUCCESS(status)) && (status != STATUS_OBJECT_NAME_NOT_FOUND)) {
  758. return status;
  759. }
  760. // Check to make sure the retrieved data makes sense
  761. partialInfoHeader = (PKEY_VALUE_PARTIAL_INFORMATION) ejectPartialInfo;
  762. undockRestrictions =
  763. (PUNDOCK_POWER_RESTRICTIONS) (ejectPartialInfo + SIZEOF_PARTIAL_INFO_HEADER);
  764. if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
  765. //
  766. // These defaults match Win9x behavior. 0% for sleep undock means
  767. // we always allow undock into Sx. This is bad for some laptops, but
  768. // legal for those that have reserve power we don't see.
  769. //
  770. undockRestrictions->HotUndockMinimumCapacity = 10; // In percent
  771. undockRestrictions->SleepUndockMinimumCapacity = 0; // In percent
  772. } else if (partialInfoHeader->DataLength <
  773. FIELD_OFFSET(UNDOCK_POWER_RESTRICTIONS, HotUndockMinimumCapacity)) {
  774. return STATUS_REGISTRY_CORRUPT;
  775. } else if (undockRestrictions->Version != 1) {
  776. //
  777. // We cannot interpret the information stored in the registry. Bail.
  778. //
  779. return STATUS_UNSUCCESSFUL;
  780. } else if ((partialInfoHeader->DataLength < sizeof(UNDOCK_POWER_RESTRICTIONS)) ||
  781. (undockRestrictions->Size != partialInfoHeader->DataLength)) {
  782. //
  783. // Malformed for version 1.
  784. //
  785. return STATUS_REGISTRY_CORRUPT;
  786. }
  787. //
  788. // Retrieve all the fun battery info. Note that we do not examine the
  789. // AC power adapter information as the best bus we have today (ACPI) doesn't
  790. // let us know if an AC adapter is leaving when we undock (so we assume all
  791. // will). If the vendor *did* put his adapter in the AML namespace, we would
  792. // enter CRITICAL shutdown immediately upon change driver in our current
  793. // design.
  794. //
  795. status = NtPowerInformation(
  796. SystemBatteryState,
  797. NULL,
  798. 0,
  799. &systemBatteryInfo,
  800. sizeof(systemBatteryInfo)
  801. ) ;
  802. if (!NT_SUCCESS(status)) {
  803. return status;
  804. }
  805. //
  806. // Convert current capacity in milliwatt hours to percentage remaining. We
  807. // should really make a decision based on the amount of time remaining under
  808. // peak milliwatt usage, but we do not collect enough information for this
  809. // today...
  810. //
  811. if (systemBatteryInfo.MaxCapacity == 0) {
  812. currentPercentage = 0;
  813. } else {
  814. //
  815. // Did we "wrap" around?
  816. //
  817. if ((systemBatteryInfo.RemainingCapacity * 100) <=
  818. systemBatteryInfo.RemainingCapacity) {
  819. currentPercentage = 0;
  820. } else {
  821. currentPercentage = (systemBatteryInfo.RemainingCapacity * 100)/
  822. systemBatteryInfo.MaxCapacity;
  823. }
  824. }
  825. //
  826. // Pick the appropriate sleep state based on our imposed limits.
  827. //
  828. if ((currentPercentage >= undockRestrictions->HotUndockMinimumCapacity) &&
  829. HotEjectSupported) {
  830. *LightestSleepState = PowerSystemWorking;
  831. } else if (WarmEjectSupported) {
  832. if (currentPercentage >= undockRestrictions->SleepUndockMinimumCapacity) {
  833. *LightestSleepState = PowerSystemSleeping1;
  834. } else {
  835. *LightestSleepState = PowerSystemHibernate;
  836. }
  837. } else {
  838. status = STATUS_INSUFFICIENT_POWER;
  839. }
  840. return status;
  841. }
  842. VOID
  843. PoGetDevicePowerState(
  844. IN PDEVICE_OBJECT PhysicalDeviceObject,
  845. OUT DEVICE_POWER_STATE *DevicePowerState
  846. )
  847. /*++
  848. Routine Description:
  849. This routine gets the power state of a given device. The object should be
  850. the Physical Device Object for a *Started* WDM device stack.
  851. Arguments:
  852. PhysicalDeviceObject - Device object representing the bottom of a WDM
  853. device stack.
  854. DevicePowerState - Receives the power state of the given device.
  855. Return Value:
  856. None.
  857. --*/
  858. {
  859. PDEVOBJ_EXTENSION doe;
  860. DEVICE_POWER_STATE deviceState;
  861. PAGED_CODE();
  862. doe = PhysicalDeviceObject->DeviceObjectExtension;
  863. deviceState = PopLockGetDoDevicePowerState(doe);
  864. if (deviceState == PowerDeviceUnspecified) {
  865. //
  866. // The PDO isn't bothering to call PoSetPowerState. Since this API
  867. // shouldn't be called on non-started devices, we will call it D0.
  868. //
  869. deviceState = PowerDeviceD0;
  870. }
  871. *DevicePowerState = deviceState;
  872. }
  873. VOID
  874. PopUnlockAfterSleepWorker(
  875. IN PVOID NotUsed
  876. )
  877. /*++
  878. Routine Description:
  879. This work item performs the unlocking of code and worker threads that
  880. corresponds to the locking done at the beginning of NtSetSystemPowerState.
  881. The unlocking is queued off to a delayed worker thread because it is likely
  882. to block on disk I/O, which will force the resume to get stuck waiting for
  883. the disks to spin up.
  884. Arguments:
  885. NotUsed - not used
  886. Return Value:
  887. None
  888. --*/
  889. {
  890. UNREFERENCED_PARAMETER (NotUsed);
  891. MmUnlockPagableImageSection(ExPageLockHandle);
  892. ExSwapinWorkerThreads(TRUE);
  893. ExNotifyCallback (ExCbPowerState, (PVOID) PO_CB_SYSTEM_STATE_LOCK, (PVOID) 1);
  894. //
  895. // Signal that unlocking is done and it is safe to lock again.
  896. //
  897. KeSetEvent(&PopUnlockComplete, 0, FALSE);
  898. }
  899. NTSTATUS
  900. PopLoggingInformation(
  901. OUT PVOID * Buffer,
  902. OUT ULONG * BufferSize
  903. )
  904. /*++
  905. Routine Description:
  906. This routine walks the list of logging reasons, allocating
  907. space for a packed array of the reasons, and copying the
  908. reasons into that array.
  909. Arguments:
  910. Buffer - receives buffer containing logging information.
  911. Buffer must be freed with ExFreePool
  912. BufferSize - receives the size of the buffer
  913. Return Value:
  914. NTSTATUS code indicating outcome.
  915. --*/
  916. {
  917. ULONG ReasonCount,ReasonSize;
  918. PLIST_ENTRY Entry;
  919. PSYSTEM_POWER_STATE_DISABLE_REASON destReason;
  920. PSYSTEM_POWER_STATE_DISABLE_LIST pList;
  921. ReasonCount = 0;
  922. ReasonSize = 0;
  923. Entry = PowerStateDisableReasonListHead.Flink;
  924. //
  925. // find out how much space we need
  926. //
  927. while (Entry != &PowerStateDisableReasonListHead) {
  928. pList = CONTAINING_RECORD(
  929. Entry,
  930. SYSTEM_POWER_STATE_DISABLE_LIST,
  931. ListEntry);
  932. ReasonCount += 1;
  933. ReasonSize += sizeof(SYSTEM_POWER_STATE_DISABLE_REASON)+ pList->Reason->PowerReasonLength;
  934. Entry = Entry->Flink;
  935. }
  936. //
  937. // if there aren't any reasons, then we allocate space for one reason
  938. //
  939. if (ReasonCount == 0) {
  940. ReasonSize = sizeof(SYSTEM_POWER_STATE_DISABLE_REASON);
  941. }
  942. //
  943. // add in room for the returned buffer size.
  944. //
  945. ReasonSize += sizeof(ULONG);
  946. //
  947. // bugbug tag
  948. //
  949. *Buffer = ExAllocatePoolWithTag(PagedPool,ReasonSize,POP_COMMON_BUFFER_TAG);
  950. if (!*Buffer) {
  951. *BufferSize = 0;
  952. return(STATUS_INSUFFICIENT_RESOURCES);
  953. }
  954. *BufferSize = ReasonSize;
  955. **(PULONG *)Buffer = ReasonSize;
  956. destReason = (PSYSTEM_POWER_STATE_DISABLE_REASON) (PCHAR)(*(PCHAR *)Buffer + sizeof(ULONG));
  957. //
  958. // now fill in the reasons
  959. //
  960. if (ReasonCount != 0) {
  961. Entry = PowerStateDisableReasonListHead.Flink;
  962. while (ReasonCount != 0 &&
  963. Entry != &PowerStateDisableReasonListHead) {
  964. pList = CONTAINING_RECORD(
  965. Entry,
  966. SYSTEM_POWER_STATE_DISABLE_LIST,
  967. ListEntry);
  968. RtlCopyMemory(
  969. (UNALIGNED PSYSTEM_POWER_STATE_DISABLE_REASON)destReason,
  970. pList->Reason,
  971. sizeof(SYSTEM_POWER_STATE_DISABLE_REASON) +pList->Reason->PowerReasonLength);
  972. destReason = (PSYSTEM_POWER_STATE_DISABLE_REASON)(PCHAR)((PCHAR)destReason + (sizeof(SYSTEM_POWER_STATE_DISABLE_REASON)+ pList->Reason->PowerReasonLength));
  973. Entry = Entry->Flink;
  974. ReasonCount -= 1;
  975. }
  976. } else {
  977. //
  978. // in the case of no reasons, just zero the memory and set the reason code
  979. // to none.
  980. //
  981. RtlZeroMemory(destReason, sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
  982. destReason->PowerReasonCode = SPSD_REASON_NONE;
  983. }
  984. return(STATUS_SUCCESS);
  985. }
  986. NTSTATUS
  987. PopDestroyLoggingList(
  988. VOID
  989. )
  990. /*++
  991. Routine Description:
  992. This routine destroys and tears down the list of
  993. SYSTEM_POWER_STATE_DISABLE_REASON records.
  994. Arguments:
  995. None.
  996. Return Value:
  997. NTSTATUS code indicating outcome.
  998. --*/
  999. {
  1000. PLIST_ENTRY Entry;
  1001. PSYSTEM_POWER_STATE_DISABLE_LIST pList;
  1002. Entry = PowerStateDisableReasonListHead.Flink;
  1003. //
  1004. // walk the list of records
  1005. //
  1006. while (Entry != &PowerStateDisableReasonListHead) {
  1007. //
  1008. // get the record
  1009. //
  1010. pList = CONTAINING_RECORD(
  1011. Entry,
  1012. SYSTEM_POWER_STATE_DISABLE_LIST,
  1013. ListEntry);
  1014. //
  1015. // get the next entry
  1016. //
  1017. Entry = Entry->Flink;
  1018. //
  1019. // now remove this entry from the list
  1020. //
  1021. RemoveEntryList(&pList->ListEntry);
  1022. //
  1023. // now deallocate the entry
  1024. //
  1025. ExFreePool(pList->Reason);
  1026. ExFreePool(pList);
  1027. }
  1028. return(STATUS_SUCCESS);
  1029. }
  1030. PSYSTEM_POWER_STATE_DISABLE_LIST
  1031. PopGetReasonListByReasonCode(
  1032. IN ULONG ReasonCode
  1033. )
  1034. /*++
  1035. Routine Description:
  1036. This routine locates a SYSTEM_POWER_STATE_DISABLE_LIST
  1037. record in the global list of records.
  1038. Arguments:
  1039. ReasonCode - reason code to search for.
  1040. Return Value:
  1041. Pointer to SYSTEM_POWER_STATE_DISABLE_LIST record. NULL if no record
  1042. currently exists with this reason code
  1043. --*/
  1044. {
  1045. PLIST_ENTRY Entry;
  1046. PSYSTEM_POWER_STATE_DISABLE_LIST pList = NULL;
  1047. BOOLEAN FoundRecord;
  1048. Entry = PowerStateDisableReasonListHead.Flink;
  1049. FoundRecord = FALSE;
  1050. //
  1051. // walk the list of records
  1052. //
  1053. while (Entry != &PowerStateDisableReasonListHead) {
  1054. //
  1055. // get the record
  1056. //
  1057. pList = CONTAINING_RECORD(
  1058. Entry,
  1059. SYSTEM_POWER_STATE_DISABLE_LIST,
  1060. ListEntry);
  1061. if (pList->Reason->PowerReasonCode == ReasonCode) {
  1062. FoundRecord = TRUE;
  1063. break;
  1064. }
  1065. //
  1066. // get the next entry
  1067. //
  1068. Entry = Entry->Flink;
  1069. }
  1070. if (!FoundRecord) {
  1071. pList = NULL;
  1072. }
  1073. return(pList);
  1074. }
  1075. NTSTATUS
  1076. PopInsertLoggingEntry(
  1077. IN PSYSTEM_POWER_STATE_DISABLE_REASON Reason
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. This routine inserts a SYSTEM_POWER_STATE_DISABLE_REASON
  1082. record into the list of records.
  1083. Internally, we use a SYSTEM_POWER_STATE_DISABLE_LIST to track
  1084. this list, and this memory must be freed via ExFreePool.
  1085. Further, we assume that the SYSTEM_POWER_STATE_DISABLE_REASON has been
  1086. allocated via ExAllocatePoolWithTag.
  1087. Arguments:
  1088. Reason - pointer to the record to be inserted.
  1089. Return Value:
  1090. NTSTATUS code indicating outcome.
  1091. --*/
  1092. {
  1093. PSYSTEM_POWER_STATE_DISABLE_LIST pList;
  1094. //
  1095. // make sure the reason code isn't already in the list.
  1096. //
  1097. pList = PopGetReasonListByReasonCode(Reason->PowerReasonCode);
  1098. if (pList) {
  1099. return(STATUS_OBJECT_NAME_EXISTS);
  1100. }
  1101. pList = ExAllocatePoolWithTag(
  1102. PagedPool,
  1103. sizeof(SYSTEM_POWER_STATE_DISABLE_LIST),
  1104. POP_COMMON_BUFFER_TAG);
  1105. if (!pList) {
  1106. return(STATUS_INSUFFICIENT_RESOURCES);
  1107. }
  1108. pList->Reason = Reason;
  1109. InsertTailList(&PowerStateDisableReasonListHead, &pList->ListEntry);
  1110. return(STATUS_SUCCESS);
  1111. }
  1112. PSYSTEM_POWER_STATE_DISABLE_REASON
  1113. PopGetReasonRecordByReasonCode(
  1114. IN ULONG ReasonCode
  1115. )
  1116. /*++
  1117. Routine Description:
  1118. This routine locates a SYSTEM_POWER_STATE_DISABLE_LIST
  1119. record in the global list of records.
  1120. BUGBUG is this a safe API to use? reason might get deallocated
  1121. while we are using it?
  1122. Arguments:
  1123. ReasonCode - reason code to search for.
  1124. Return Value:
  1125. Pointer to SYSTEM_POWER_STATE_DISABLE_LIST record. NULL if no record
  1126. currently exists with this reason code
  1127. --*/
  1128. {
  1129. PSYSTEM_POWER_STATE_DISABLE_LIST pList;
  1130. pList = PopGetReasonListByReasonCode(ReasonCode);
  1131. if (!pList) {
  1132. return(NULL);
  1133. }
  1134. return(pList->Reason);
  1135. }
  1136. NTSTATUS
  1137. PopRemoveReasonRecordByReasonCode(
  1138. IN ULONG ReasonCode
  1139. )
  1140. /*++
  1141. Routine Description:
  1142. This routine locates a SYSTEM_POWER_STATE_DISABLE_REASON
  1143. record in the global list of records and removes it,
  1144. deallocating the space for the record as well.
  1145. Arguments:
  1146. ReasonCode - reason code to search for.
  1147. Return Value:
  1148. NTSTATUS code indicating outcome.
  1149. --*/
  1150. {
  1151. PSYSTEM_POWER_STATE_DISABLE_LIST pList;
  1152. pList = PopGetReasonListByReasonCode(ReasonCode);
  1153. if (!pList) {
  1154. return(STATUS_NOT_FOUND);
  1155. }
  1156. RemoveEntryList(&pList->ListEntry);
  1157. ExFreePool(pList->Reason);
  1158. ExFreePool(pList);
  1159. return(STATUS_SUCCESS);
  1160. }