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.

1436 lines
40 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. paction.c
  5. Abstract:
  6. This module implements power action handling for triggered
  7. power actions
  8. Author:
  9. Ken Reneris (kenr) 17-Jan-1997
  10. Revision History:
  11. --*/
  12. #include "pop.h"
  13. //
  14. // Interal prototypes
  15. //
  16. VOID
  17. PopPromoteActionFlag (
  18. OUT PUCHAR Updates,
  19. IN ULONG UpdateFlag,
  20. IN ULONG Flags,
  21. IN BOOLEAN Set,
  22. IN ULONG FlagBit
  23. );
  24. NTSTATUS
  25. PopIssueActionRequest (
  26. IN BOOLEAN Promote,
  27. IN POWER_ACTION Action,
  28. IN SYSTEM_POWER_STATE PowerState,
  29. IN ULONG Flags
  30. );
  31. VOID
  32. PopCompleteAction (
  33. PPOP_ACTION_TRIGGER Trigger,
  34. NTSTATUS Status
  35. );
  36. NTSTATUS
  37. PopDispatchStateCallout(
  38. IN PKWIN32_POWERSTATE_PARAMETERS Parms,
  39. IN PULONG SessionId OPTIONAL
  40. );
  41. #ifdef ALLOC_PRAGMA
  42. #pragma alloc_text(PAGE, PoShutdownBugCheck)
  43. #pragma alloc_text(PAGE, PopPromoteActionFlag)
  44. #pragma alloc_text(PAGE, PopSetPowerAction)
  45. #pragma alloc_text(PAGE, PopCompareActions)
  46. #pragma alloc_text(PAGE, PopPolicyWorkerAction)
  47. #pragma alloc_text(PAGE, PopIssueActionRequest)
  48. #pragma alloc_text(PAGE, PopCriticalShutdown)
  49. #pragma alloc_text(PAGE, PopCompleteAction)
  50. #pragma alloc_text(PAGE, PopPolicyWorkerActionPromote)
  51. #pragma alloc_text(PAGE, PopDispatchStateCallout)
  52. #endif
  53. VOID
  54. PoShutdownBugCheck (
  55. IN BOOLEAN AllowCrashDump,
  56. IN ULONG BugCheckCode,
  57. IN ULONG_PTR BugCheckParameter1,
  58. IN ULONG_PTR BugCheckParameter2,
  59. IN ULONG_PTR BugCheckParameter3,
  60. IN ULONG_PTR BugCheckParameter4
  61. )
  62. /*++
  63. Routine Description:
  64. This function is used to issue a controlled shutdown & then a bug
  65. check. This type of bugcheck can only be issued on a working system.
  66. Arguments:
  67. AllowCrashDump - If FALSE crashdump will be disabled
  68. BugCode - The bug code for the shutdown
  69. Return Value:
  70. Does not return
  71. --*/
  72. {
  73. POP_SHUTDOWN_BUG_CHECK BugCode;
  74. //
  75. // If crash dumps aren't allowed for this bugcheck, then go clear
  76. // the current crash dump state
  77. //
  78. if (!AllowCrashDump) {
  79. IoConfigureCrashDump (CrashDumpDisable);
  80. }
  81. //
  82. // Indicate the bugcheck to issue once the system has been shutdown
  83. //
  84. BugCode.Code = BugCheckCode;
  85. BugCode.Parameter1 = BugCheckParameter1;
  86. BugCode.Parameter2 = BugCheckParameter2;
  87. BugCode.Parameter3 = BugCheckParameter3;
  88. BugCode.Parameter4 = BugCheckParameter4;
  89. PopAction.ShutdownBugCode = &BugCode;
  90. //
  91. // Initiate a critical shutdown event
  92. //
  93. ZwInitiatePowerAction (
  94. PowerActionShutdown,
  95. PowerSystemSleeping3,
  96. POWER_ACTION_OVERRIDE_APPS | POWER_ACTION_DISABLE_WAKES | POWER_ACTION_CRITICAL,
  97. FALSE
  98. );
  99. //
  100. // Should not return, but just in case...
  101. //
  102. KeBugCheckEx (
  103. BugCheckCode,
  104. BugCheckParameter1,
  105. BugCheckParameter2,
  106. BugCheckParameter3,
  107. BugCheckParameter4
  108. );
  109. }
  110. VOID
  111. PopCriticalShutdown (
  112. POP_POLICY_DEVICE_TYPE Type
  113. )
  114. /*++
  115. Routine Description:
  116. Issue a critical system shutdown. No application notification
  117. (presumably they've ignored the issue til now), flush OS state
  118. and shut off.
  119. N.B. PopPolicyLock must be held.
  120. Arguments:
  121. Type - Root cause of critical shutdown
  122. Return Value:
  123. None.
  124. --*/
  125. {
  126. POP_ACTION_TRIGGER Trigger;
  127. POWER_ACTION_POLICY Action;
  128. ASSERT_POLICY_LOCK_OWNED();
  129. PoPrint (PO_ERROR, ("PopCriticalShutdown: type %x\n", Type));
  130. //
  131. // Go directly to setting the power state
  132. //
  133. RtlZeroMemory (&Action, sizeof(Action));
  134. Action.Action = PowerActionShutdownOff;
  135. Action.Flags = POWER_ACTION_OVERRIDE_APPS |
  136. POWER_ACTION_DISABLE_WAKES |
  137. POWER_ACTION_CRITICAL;
  138. RtlZeroMemory (&Trigger, sizeof(Trigger));
  139. Trigger.Type = Type;
  140. Trigger.Flags = PO_TRG_SET;
  141. try {
  142. //
  143. // The substitution policy and LightestState do not matter here as
  144. // the action restricts this to a shutdown.
  145. //
  146. PopSetPowerAction(
  147. &Trigger,
  148. 0,
  149. &Action,
  150. PowerSystemHibernate,
  151. SubstituteLightestOverallDownwardBounded
  152. );
  153. } except (EXCEPTION_EXECUTE_HANDLER) {
  154. ASSERT (!GetExceptionCode());
  155. }
  156. }
  157. VOID
  158. PopSetPowerAction(
  159. IN PPOP_ACTION_TRIGGER Trigger,
  160. IN ULONG UserNotify,
  161. IN PPOWER_ACTION_POLICY ActionPolicy,
  162. IN SYSTEM_POWER_STATE LightestState,
  163. IN POP_SUBSTITUTION_POLICY SubstitutionPolicy
  164. )
  165. /*++
  166. Routine Description:
  167. This function is called to "fire" an ActionPolicy. If there
  168. is already an action being taken this action is merged in, else
  169. a new power action is initiated.
  170. N.B. PopPolicyLock must be held.
  171. Arguments:
  172. Trigger - Action trigger structure (for ActionPolicy).
  173. UserNotify - Additional USER notifications to fire if trigger occurs
  174. ActionPolicy - This action policy which has fired.
  175. LightestState - For sleep type actions, the minimum sleeping
  176. state which must be entered for this operation.
  177. (Inferred to be PowerSystemHibernate for
  178. PowerActionHibernate and PowerActionWarmEject)
  179. SubstitutionPolicy - Specifies how LightestState should be treated if it
  180. is not supported.
  181. Return Value:
  182. None.
  183. --*/
  184. {
  185. UCHAR Updates;
  186. ULONG i, Flags;
  187. BOOLEAN Pending;
  188. BOOLEAN Disabled;
  189. POWER_ACTION Action;
  190. ASSERT_POLICY_LOCK_OWNED();
  191. if (PERFINFO_IS_GROUP_ON(PERF_POWER)) {
  192. PERFINFO_SET_POWER_ACTION LogEntry;
  193. LogEntry.PowerAction = (ULONG) ActionPolicy->Action;
  194. LogEntry.LightestState = (ULONG) LightestState;
  195. LogEntry.Trigger = Trigger;
  196. PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_POWER_ACTION,
  197. &LogEntry,
  198. sizeof(LogEntry));
  199. }
  200. //
  201. // If the trigger isn't set, then we're done
  202. //
  203. if (!(Trigger->Flags & PO_TRG_SET)) {
  204. PopCompleteAction (Trigger, STATUS_SUCCESS);
  205. return ;
  206. }
  207. PoPrint (PO_PACT, ("PopSetPowerAction: %s, Flags %x, Min=%s\n",
  208. PopPowerActionString(ActionPolicy->Action),
  209. ActionPolicy->Flags,
  210. PopSystemStateString(LightestState)
  211. ));
  212. //
  213. // Round request to system capabilities
  214. //
  215. PopVerifySystemPowerState(&LightestState, SubstitutionPolicy);
  216. Disabled = PopVerifyPowerActionPolicy (ActionPolicy);
  217. if (Disabled) {
  218. PopCompleteAction (Trigger, STATUS_NOT_SUPPORTED);
  219. return ;
  220. }
  221. //
  222. // If system action not already triggered, do so now
  223. //
  224. Pending = FALSE;
  225. if (!(Trigger->Flags & PO_TRG_SYSTEM)) {
  226. Trigger->Flags |= PO_TRG_SYSTEM;
  227. Action = ActionPolicy->Action;
  228. Flags = ActionPolicy->Flags;
  229. //
  230. // If state is idle, then clear residue values
  231. //
  232. if (PopAction.State == PO_ACT_IDLE) {
  233. PopResetActionDefaults();
  234. }
  235. //
  236. // If the action is for something other then none, then check it against the
  237. // current action
  238. //
  239. if (Action != PowerActionNone) {
  240. Updates = 0;
  241. //
  242. // Hibernate actions are treated like sleep actions with a min state
  243. // of hibernate. Warm ejects are like sleeps that start light and
  244. // deepen only as neccessary, but we do not map them to the same
  245. // action because we don't want to be constrained by the users
  246. // power policy.
  247. //
  248. if (Action == PowerActionWarmEject) {
  249. ASSERT (LightestState <= PowerSystemHibernate);
  250. Flags |= POWER_ACTION_LIGHTEST_FIRST;
  251. }
  252. if (Action == PowerActionHibernate) {
  253. ASSERT (LightestState <= PowerSystemHibernate);
  254. LightestState = PowerSystemHibernate;
  255. }
  256. //
  257. // Is this action is as good as the current action?
  258. //
  259. if ( PopCompareActions(Action, PopAction.Action) >= 0) {
  260. //
  261. // allow the absence of query_allowed, ui_allowed.
  262. //
  263. PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, FALSE, POWER_ACTION_QUERY_ALLOWED);
  264. PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, FALSE, POWER_ACTION_UI_ALLOWED);
  265. //
  266. // Always favor the deepest sleep first, and restart if we
  267. // switch.
  268. //
  269. PopPromoteActionFlag (&Updates, PO_PM_SETSTATE, Flags, FALSE, POWER_ACTION_LIGHTEST_FIRST);
  270. //
  271. // If this is a sleep action, then make sure Lightest is at least whatever
  272. // the current policy is set for
  273. //
  274. if (Action == PowerActionSleep && LightestState < PopPolicy->MinSleep) {
  275. LightestState = PopPolicy->MinSleep;
  276. }
  277. //
  278. // If LightestState is more restrictive (deeper) than the one
  279. // specified by the current action, promote it.
  280. //
  281. if (LightestState > PopAction.LightestState) {
  282. PopAction.LightestState = LightestState;
  283. Updates |= PO_PM_SETSTATE;
  284. }
  285. }
  286. //
  287. // Promote the critical & override_apps flags
  288. //
  289. PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, TRUE, POWER_ACTION_OVERRIDE_APPS);
  290. PopPromoteActionFlag (&Updates, PO_PM_USER | PO_PM_SETSTATE, Flags, TRUE, POWER_ACTION_CRITICAL);
  291. //
  292. // Promote disable_wake flag. No updates are needed for this - it will be
  293. // picked up in NtSetSystemPowerState regardless of the params passed from
  294. // user mode
  295. //
  296. PopPromoteActionFlag (&Updates, 0, Flags, TRUE, POWER_ACTION_DISABLE_WAKES);
  297. //
  298. // If the new action is more agressive then the old action, promote it
  299. //
  300. if ( PopCompareActions(Action, PopAction.Action) > 0) {
  301. //
  302. // If we are promoting, the old action certainly cannot be a
  303. // shutdown, as that is the deepest action.
  304. //
  305. ASSERT(PopCompareActions(PopAction.Action, PowerActionShutdownOff) < 0);
  306. //
  307. // If we are promoting into a deeper *action*, and the new
  308. // action is hibernate or shutdown, then we want to reissue.
  309. //
  310. // ADRIAO N.B. 08/02/1999 -
  311. // We might want to reissue for hibernate only if the new
  312. // state brings in POWER_ACTION_CRITICAL to the mix. This is
  313. // because there are two scenario's for hibernate, one where
  314. // the user selects standby then hibernate in quick succession
  315. // (consider a lid switch set to hiber, and the user closes the
  316. // lid after selecting standby), or this might be hibernate due
  317. // to low battery (ie, we're deepening standby). Believe it or
  318. // not, the user can disable the "critical flag" in the critical
  319. // power-down menu.
  320. //
  321. if (PopCompareActions(Action, PowerActionHibernate) >= 0) {
  322. Updates |= PO_PM_REISSUE;
  323. }
  324. Updates |= PO_PM_USER | PO_PM_SETSTATE;
  325. PopAction.Action = Action;
  326. }
  327. if (Action == PowerActionHibernate) {
  328. Action = PowerActionSleep;
  329. }
  330. //
  331. // PopAction.Action may be explicitely set to PowerActionHibernate
  332. // by NtSetSystemPowerState during a wake.
  333. //
  334. if (PopAction.Action == PowerActionHibernate) {
  335. PopAction.Action = PowerActionSleep;
  336. }
  337. //
  338. // If the current action was updated, then get a worker
  339. //
  340. if (Updates) {
  341. Pending = TRUE;
  342. if (PopAction.State == PO_ACT_IDLE || PopAction.State == PO_ACT_NEW_REQUEST) {
  343. //
  344. // New request
  345. //
  346. PopAction.State = PO_ACT_NEW_REQUEST;
  347. PopAction.Status = STATUS_SUCCESS;
  348. PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL);
  349. } else {
  350. //
  351. // Something outstanding. Promote it.
  352. //
  353. PopAction.Updates |= Updates;
  354. PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE);
  355. }
  356. }
  357. }
  358. }
  359. //
  360. // If user events haven't been handled, do it now
  361. //
  362. if (!(Trigger->Flags & PO_TRG_USER)) {
  363. Trigger->Flags |= PO_TRG_USER;
  364. //
  365. // If there's an eventcode for the action, dispatch it
  366. //
  367. if (ActionPolicy->EventCode) {
  368. // if event code already queued, drop it
  369. for (i=0; i < POP_MAX_EVENT_CODES; i++) {
  370. if (PopEventCode[i] == ActionPolicy->EventCode) {
  371. break;
  372. }
  373. }
  374. if (i >= POP_MAX_EVENT_CODES) {
  375. // not queued, add it
  376. for (i=0; i < POP_MAX_EVENT_CODES; i++) {
  377. if (!PopEventCode[i]) {
  378. PopEventCode[i] = ActionPolicy->EventCode;
  379. UserNotify |= PO_NOTIFY_EVENT_CODES;
  380. break;
  381. }
  382. }
  383. if (i >= POP_MAX_EVENT_CODES) {
  384. PoPrint (PO_WARN, ("PopAction: dropped user event %x\n", ActionPolicy->EventCode));
  385. }
  386. }
  387. }
  388. PopSetNotificationWork (UserNotify);
  389. }
  390. //
  391. // If sync request, queue it or complete it
  392. //
  393. if (Trigger->Flags & PO_TRG_SYNC) {
  394. if (Pending) {
  395. InsertTailList (&PopActionWaiters, &Trigger->Wait->Link);
  396. } else {
  397. PopCompleteAction (Trigger, STATUS_SUCCESS);
  398. }
  399. }
  400. }
  401. LONG
  402. PopCompareActions(
  403. IN POWER_ACTION FutureAction,
  404. IN POWER_ACTION CurrentAction
  405. )
  406. /*++
  407. Routine Description:
  408. Used to determine whether the current action should be promoted to the
  409. future action or not.
  410. N.B. PopPolicyLock must be held.
  411. Arguments:
  412. FutureAction - Action which we are now being asked to do.
  413. CurrentAction - Action which we are currently doing.
  414. Return Value:
  415. Zero if the current and future actions are identical.
  416. Positive if the future action should be used.
  417. Negative if the current action is already more important than the future
  418. request.
  419. --*/
  420. {
  421. //
  422. // We could just return (FutureAction - CurrentAction) if it weren't for
  423. // PowerActionWarmEject, which is less important than sleeping (because
  424. // sleeping may be induced by critically low power). So we "insert"
  425. // PowerActionWarmEject right before PowerActionSleep.
  426. //
  427. if (FutureAction == PowerActionWarmEject) {
  428. FutureAction = PowerActionSleep;
  429. } else if (FutureAction >= PowerActionSleep) {
  430. FutureAction++;
  431. }
  432. if (CurrentAction == PowerActionWarmEject) {
  433. CurrentAction = PowerActionSleep;
  434. } else if (CurrentAction >= PowerActionSleep) {
  435. CurrentAction++;
  436. }
  437. return (FutureAction - CurrentAction);
  438. }
  439. VOID
  440. PopPromoteActionFlag (
  441. OUT PUCHAR Updates,
  442. IN ULONG UpdateFlag,
  443. IN ULONG Flags,
  444. IN BOOLEAN Set,
  445. IN ULONG FlagBit
  446. )
  447. /*++
  448. Routine Description:
  449. Used to merge existing action flags with new action flags.
  450. The FlagBit bit in PopAction.Flags is promoted to set/clear
  451. according UpdateFlag. If a change occured Updates is
  452. updated.
  453. N.B. PopPolicyLock must be held.
  454. Arguments:
  455. Updates - Current outstanding updates to the power action
  456. which is in progress
  457. UpdateFlag - Bit(s) to set into Updates if a change is made
  458. Flags - Flags to test for FlagBit
  459. Set - To test either set or clear
  460. FlagBit - The bit to check in Flags
  461. Return Value:
  462. None.
  463. --*/
  464. {
  465. ULONG New, Current;
  466. ULONG Mask;
  467. Mask = Set ? 0 : FlagBit;
  468. New = (Flags & FlagBit) ^ Mask;
  469. Current = (PopAction.Flags & FlagBit) ^ Mask;
  470. //
  471. // If the bit is not set accordingly in Flags but is set accordingly in
  472. // PoAction.Flags then update it
  473. //
  474. if (New & ~Current) {
  475. PopAction.Flags = (PopAction.Flags | New) & ~Mask;
  476. *Updates |= (UCHAR) UpdateFlag;
  477. }
  478. }
  479. ULONG
  480. PopPolicyWorkerAction (
  481. VOID
  482. )
  483. /*++
  484. Routine Description:
  485. Dispatch function for: worker_action_normal. This worker
  486. thread checks for an initial pending action and synchronously
  487. issued it to USER. The thread is returned after the USER
  488. has completed the action. (e.g., apps have been notified if
  489. allowed, etc..)
  490. Arguments:
  491. None.
  492. Return Value:
  493. None.
  494. --*/
  495. {
  496. POWER_ACTION Action;
  497. SYSTEM_POWER_STATE LightestState;
  498. ULONG Flags;
  499. NTSTATUS Status;
  500. PLIST_ENTRY Link;
  501. PPOP_TRIGGER_WAIT SyncRequest;
  502. PopAcquirePolicyLock ();
  503. if (PopAction.State == PO_ACT_NEW_REQUEST) {
  504. //
  505. // We'll handle this update
  506. //
  507. Action = PopAction.Action;
  508. LightestState = PopAction.LightestState;
  509. Flags = PopAction.Flags;
  510. PopAction.State = PO_ACT_CALLOUT;
  511. //
  512. // Perform callout
  513. //
  514. Status = PopIssueActionRequest (FALSE, Action, LightestState, Flags);
  515. //
  516. // Clear switch triggers
  517. //
  518. PopResetSwitchTriggers ();
  519. //
  520. // If the system was sleeping
  521. //
  522. if (!NT_SUCCESS(Status)) {
  523. PoPrint (PO_WARN | PO_PACT,
  524. ("PopPolicyWorkerAction: action request %d failed %08lx\n", Action, Status));
  525. }
  526. if (PopAction.Updates & PO_PM_REISSUE) {
  527. //
  528. // There's a new outstanding request. Claim it.
  529. //
  530. PopAction.Updates &= ~PO_PM_REISSUE;
  531. PopAction.State = PO_ACT_NEW_REQUEST;
  532. PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL);
  533. } else {
  534. //
  535. // All power actions are complete.
  536. //
  537. if (PERFINFO_IS_GROUP_ON(PERF_POWER)) {
  538. PERFINFO_SET_POWER_ACTION_RET LogEntry;
  539. LogEntry.Trigger = (PVOID)(ULONG_PTR)Action;
  540. LogEntry.Status = Status;
  541. PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_POWER_ACTION_RET,
  542. &LogEntry,
  543. sizeof(LogEntry));
  544. }
  545. PopAction.Status = Status;
  546. PopAction.State = PO_ACT_IDLE;
  547. if (IsListEmpty(&PopActionWaiters)) {
  548. //
  549. // If there was an error and no one is waiting for it, issue a notify
  550. //
  551. if (!NT_SUCCESS(Status)) {
  552. PopSetNotificationWork (PO_NOTIFY_STATE_FAILURE);
  553. }
  554. } else {
  555. //
  556. // Free any synchronous waiters
  557. //
  558. for (Link = PopActionWaiters.Flink; Link != &PopActionWaiters; Link = Link->Flink) {
  559. SyncRequest = CONTAINING_RECORD (Link, POP_TRIGGER_WAIT, Link);
  560. PopCompleteAction (SyncRequest->Trigger, Status);
  561. }
  562. }
  563. //
  564. // Let promotion worker check for anything else
  565. //
  566. PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE);
  567. }
  568. }
  569. PopReleasePolicyLock (FALSE);
  570. return 0;
  571. }
  572. VOID
  573. PopCompleteAction (
  574. PPOP_ACTION_TRIGGER Trigger,
  575. NTSTATUS Status
  576. )
  577. {
  578. PPOP_TRIGGER_WAIT SyncRequest;
  579. if (Trigger->Flags & PO_TRG_SYNC) {
  580. Trigger->Flags &= ~PO_TRG_SYNC;
  581. SyncRequest = Trigger->Wait;
  582. SyncRequest->Status = Status;
  583. KeSetEvent (&SyncRequest->Event, 0, FALSE);
  584. }
  585. }
  586. ULONG
  587. PopPolicyWorkerActionPromote (
  588. VOID
  589. )
  590. /*++
  591. Routine Description:
  592. Dispatch function for: worker_action_promote. This worker
  593. thread checks for a pending promotion needed for a power
  594. action request in USER and calls USER with the promotion.
  595. This function, PopPolicyWorkerAction, and NtSetSystemPowerState
  596. corridinate to handle ordering issues of when each function
  597. is called.
  598. N.B. Part of the cleanup from PopPolicyWorkerAction is to invoke
  599. this function. So this worker function may have 2 threads
  600. at any one time. (But in this case, the normal action worker
  601. thread would only find a promotion turning into to a new
  602. request and then exit back to a normal action worker)
  603. Arguments:
  604. None.
  605. Return Value:
  606. None.
  607. --*/
  608. {
  609. ULONG Updates;
  610. NTSTATUS Status;
  611. PopAcquirePolicyLock ();
  612. if (PopAction.Updates) {
  613. //
  614. // Get update info
  615. //
  616. Updates = PopAction.Updates;
  617. //
  618. // Handle based on state of original request worker
  619. //
  620. switch (PopAction.State) {
  621. case PO_ACT_IDLE:
  622. //
  623. // Normal worker is no longer in progress, this is no
  624. // longer a promotion. If the updates have PO_PM_REISSUE
  625. // then turn this into a new request, else the promotion can
  626. // be skipped as the original operation completion
  627. // was good enough
  628. //
  629. if (Updates & PO_PM_REISSUE) {
  630. PopAction.State = PO_ACT_NEW_REQUEST;
  631. PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL);
  632. } else {
  633. Updates = 0;
  634. }
  635. break;
  636. case PO_ACT_SET_SYSTEM_STATE:
  637. //
  638. // If reissue or setstate is set, abort the current operation.
  639. //
  640. if (Updates & (PO_PM_REISSUE | PO_PM_SETSTATE)) {
  641. PopRestartSetSystemState ();
  642. }
  643. break;
  644. case PO_ACT_CALLOUT:
  645. //
  646. // Worker is in the callout. Call again to issue the promotion
  647. //
  648. Status = PopIssueActionRequest (
  649. TRUE,
  650. PopAction.Action,
  651. PopAction.LightestState,
  652. PopAction.Flags
  653. );
  654. if (NT_SUCCESS(Status)) {
  655. //
  656. // Promotion worked, clear the updates we performed
  657. //
  658. PopAction.Updates &= ~Updates;
  659. } else {
  660. //
  661. // If the state has changed, test again else do nothing
  662. // (the original worker thread will recheck on exit)
  663. //
  664. if (PopAction.State != PO_ACT_CALLOUT) {
  665. PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE);
  666. }
  667. }
  668. break;
  669. default:
  670. PoPrint (PO_ERROR, ("PopAction: invalid state %d\n", PopAction.State));
  671. }
  672. }
  673. PopReleasePolicyLock (FALSE);
  674. return 0;
  675. }
  676. NTSTATUS
  677. PopIssueActionRequest (
  678. IN BOOLEAN Promote,
  679. IN POWER_ACTION Action,
  680. IN SYSTEM_POWER_STATE LightestState,
  681. IN ULONG Flags
  682. )
  683. /*++
  684. Routine Description:
  685. This function is used by the normal action worker or the promtion worker
  686. when its time to call USER or NtSetSystemPowerState with a new request.
  687. Arguments:
  688. Promote - Indicates flag for USER call
  689. Action - The action to take
  690. LightestState - The minimum power state to enter
  691. Flags - Flags for the action to take. E.g., how it should be processed
  692. Return Value:
  693. Status as returned from USER or NtSetSystemPowerState
  694. --*/
  695. {
  696. BOOLEAN DirectCall;
  697. NTSTATUS Status;
  698. ULONG Console;
  699. //
  700. // If there's no vector to call, then its a direct call
  701. //
  702. DirectCall = PopStateCallout ? FALSE : TRUE;
  703. //
  704. // If the critical flag is set and it's a ShutdownReset or ShutdownOff,
  705. // then it's done via a direct call.
  706. //
  707. if ((Flags & POWER_ACTION_CRITICAL) &&
  708. (Action == PowerActionShutdownReset ||
  709. Action == PowerActionShutdown ||
  710. Action == PowerActionShutdownOff)) {
  711. DirectCall = TRUE;
  712. }
  713. //
  714. // If this is a direct call, then drop any reissue flag
  715. //
  716. if (DirectCall) {
  717. PopAction.Updates &= ~PO_PM_REISSUE;
  718. }
  719. //
  720. // If the policy has lock console set, make sure it's set in
  721. // the flags as well
  722. //
  723. if (PopPolicy->WinLogonFlags & WINLOGON_LOCK_ON_SLEEP) {
  724. Flags |= POWER_ACTION_LOCK_CONSOLE;
  725. }
  726. //
  727. // Debug
  728. //
  729. PoPrint (PO_PACT, ("PowerAction: %s%s, Min=%s, Flags %x\n",
  730. Promote ? "Promote, " : "",
  731. PopPowerActionString(Action),
  732. PopSystemStateString(LightestState),
  733. Flags
  734. ));
  735. if (DirectCall) {
  736. PoPrint (PO_PACT, ("PowerAction: Setting with direct call\n"));
  737. }
  738. //
  739. // Drop lock while performing callout to dispatch request
  740. //
  741. PopReleasePolicyLock (FALSE);
  742. if (DirectCall) {
  743. Status = ZwSetSystemPowerState (Action, LightestState, Flags);
  744. } else {
  745. WIN32_POWERSTATE_PARAMETERS Parms;
  746. Parms.Promotion = Promote;
  747. Parms.SystemAction = Action;
  748. Parms.MinSystemState = LightestState;
  749. Parms.Flags = Flags;
  750. Parms.fQueryDenied = FALSE;
  751. if (!Promote) {
  752. //
  753. // we want to deliver some messages to only the console session.
  754. // lets find out active console session here, and ask that active console win2k
  755. // to block the console switch while we are in power switch
  756. //
  757. LARGE_INTEGER ShortSleep;
  758. ShortSleep.QuadPart = -10 * 1000 * 10; // 10 milliseconds
  759. Status = STATUS_UNSUCCESSFUL;
  760. do {
  761. Console = SharedUserData->ActiveConsoleId;
  762. if (Console != -1) {
  763. //
  764. // lets ask this console session, not to switch console,
  765. // untill we are done with power callouts.
  766. //
  767. Parms.PowerStateTask = PowerState_BlockSessionSwitch;
  768. Status = PopDispatchStateCallout(&Parms, &Console);
  769. if (Status == STATUS_CTX_NOT_CONSOLE) {
  770. //
  771. // we failed to block status switch
  772. // loop again
  773. Console = (ULONG) -1;
  774. }
  775. }
  776. if (Console == -1) {
  777. //
  778. // we are in session switch, wait till we get a valid active console session
  779. //
  780. KeDelayExecutionThread(KernelMode, FALSE, &ShortSleep);
  781. }
  782. } while (Console == -1);
  783. ASSERT(NT_SUCCESS(Status));
  784. }
  785. Parms.PowerStateTask = PowerState_Init;
  786. Status = PopDispatchStateCallout(&Parms, NULL);
  787. if (!Promote && NT_SUCCESS(Status)) {
  788. Parms.PowerStateTask = PowerState_QueryApps;
  789. Status = PopDispatchStateCallout(&Parms, NULL);
  790. if (!NT_SUCCESS(Status) || Parms.fQueryDenied) {
  791. //
  792. // ISSUE-2000/11/28-jamesca:
  793. //
  794. // Win32k depends on PowerState_QueryFailed to unset the
  795. // fInProgress bit, set during PowerState_Init. Ideally, some
  796. // other operation should be used to do that, without having to
  797. // issue a PowerState_QueryFailed cancel message to sessions
  798. // (and apps) that never received PowerState_QueryApps query.
  799. //
  800. Parms.PowerStateTask = PowerState_QueryFailed;
  801. PopDispatchStateCallout(&Parms, NULL);
  802. } else {
  803. Parms.PowerStateTask = PowerState_SuspendApps;
  804. PopDispatchStateCallout(&Parms, NULL);
  805. Parms.PowerStateTask = PowerState_ShowUI;
  806. PopDispatchStateCallout(&Parms, NULL);
  807. Parms.PowerStateTask = PowerState_NotifyWL;
  808. Status = PopDispatchStateCallout(&Parms, &Console);
  809. if( !NT_SUCCESS(Status) ) {
  810. //
  811. // Someone failed the request. We should notify win32k.sys
  812. // and let him notify the world that we aren't really
  813. // going down.
  814. //
  815. // Note that some services may not really know what to
  816. // do if we send down a QueryFailed message right now,
  817. // so once we're done here, fall through and send down
  818. // the ResumeApps message too.
  819. //
  820. Parms.PowerStateTask = PowerState_QueryFailed;
  821. PopDispatchStateCallout(&Parms, NULL);
  822. }
  823. Parms.PowerStateTask = PowerState_ResumeApps;
  824. PopDispatchStateCallout(&Parms, NULL);
  825. }
  826. }
  827. if (!Promote) {
  828. //
  829. // we are done with power callouts, now its ok if active console session switches
  830. //
  831. Parms.PowerStateTask = PowerState_UnBlockSessionSwitch;
  832. PopDispatchStateCallout(&Parms, &Console);
  833. }
  834. }
  835. PopAcquirePolicyLock ();
  836. return Status;
  837. }
  838. VOID
  839. PopResetActionDefaults(
  840. VOID
  841. )
  842. /*++
  843. Routine Description:
  844. This function is used to initialize the current PopAction to reflect
  845. the idle state.
  846. Arguments:
  847. None.
  848. Return Value:
  849. None.
  850. --*/
  851. {
  852. PopAction.Updates = 0;
  853. PopAction.Shutdown = FALSE;
  854. PopAction.Action = PowerActionNone;
  855. PopAction.LightestState = PowerSystemUnspecified;
  856. PopAction.Status = STATUS_SUCCESS;
  857. PopAction.IrpMinor = 0;
  858. PopAction.SystemState = PowerSystemUnspecified;
  859. //
  860. // When we promote a power action (say from idle), various flags must be
  861. // agreed upon by both actions to stay around. We must set those flags
  862. // here otherwise they can never be set after promoting (idle).
  863. //
  864. PopAction.Flags = (
  865. POWER_ACTION_QUERY_ALLOWED |
  866. POWER_ACTION_UI_ALLOWED |
  867. POWER_ACTION_LIGHTEST_FIRST
  868. );
  869. }
  870. VOID
  871. PopActionRetrieveInitialState(
  872. IN OUT PSYSTEM_POWER_STATE LightestSystemState,
  873. OUT PSYSTEM_POWER_STATE DeepestSystemState,
  874. OUT PSYSTEM_POWER_STATE InitialSystemState,
  875. OUT PBOOLEAN QueryDevices
  876. )
  877. /*++
  878. Routine Description:
  879. This function is used to determine the lightest, deepest, and initial Sx
  880. states prior to putting the system to sleep, or turning it off. Power
  881. policies for sleep are also applied if the action is a sleep.
  882. Arguments:
  883. LightestSystemState - Lightest sleep state. May be adjusted if the action
  884. in progress is a shutdown.
  885. DeepestSystemState - Deepest sleep state possible.
  886. InitialSystemState - State to start with.
  887. QueryDevices - TRUE if devices should be queries, FALSE if devices
  888. shouldn't be queried.
  889. Return Value:
  890. None.
  891. --*/
  892. {
  893. //
  894. // Check if the action is a shutdown. If so, map it to the appropiate
  895. // system shutdown state
  896. //
  897. if ((PopAction.Action == PowerActionShutdown) ||
  898. (PopAction.Action == PowerActionShutdownReset) ||
  899. (PopAction.Action == PowerActionShutdownOff)) {
  900. //
  901. // This is a shutdown. The lightest we can do is S5.
  902. //
  903. *LightestSystemState = PowerSystemShutdown;
  904. *DeepestSystemState = PowerSystemShutdown;
  905. } else if (PopAction.Action == PowerActionWarmEject) {
  906. //
  907. // Warm Ejects have an implicit policy of either S1-S4 or S4-S4.
  908. // The caller passes in LightestSystemState to choose the lightest,
  909. // and the deepest is always a hibernate.
  910. //
  911. *DeepestSystemState = PowerSystemHibernate;
  912. PopVerifySystemPowerState (DeepestSystemState, SubstituteLightenSleep);
  913. } else {
  914. //
  915. // This a sleep request. Min is current set to the best the hardware
  916. // can do relative to our caller. We apply the minimum from the current
  917. // policy here. We also choose the maximum from either the policy or
  918. // the default for current latency setting. Note that all of these
  919. // values in PopPolicy have been verified at some point.
  920. //
  921. // Note that PopSetPowerAction fixes up PowerActionHibernate long before
  922. // we get here.
  923. //
  924. if (PopAttributes[POP_LOW_LATENCY_ATTRIBUTE].Count &&
  925. (PopPolicy->MaxSleep >= PopPolicy->ReducedLatencySleep)) {
  926. *DeepestSystemState = PopPolicy->ReducedLatencySleep;
  927. } else {
  928. *DeepestSystemState = PopPolicy->MaxSleep;
  929. }
  930. if (PopPolicy->MinSleep > *LightestSystemState) {
  931. *LightestSystemState = PopPolicy->MinSleep;
  932. }
  933. }
  934. //
  935. // If there's an explicit min state which is deeper than the
  936. // max state, then raise the max to allow it
  937. //
  938. if (*LightestSystemState > *DeepestSystemState) {
  939. *DeepestSystemState = *LightestSystemState;
  940. }
  941. //
  942. // We query devices unless this is a critical operation with no range.
  943. //
  944. *QueryDevices = TRUE;
  945. if ((PopAction.Flags & POWER_ACTION_CRITICAL) &&
  946. *LightestSystemState == *DeepestSystemState) {
  947. *QueryDevices = FALSE;
  948. }
  949. //
  950. // Pick the appropriate initial state.
  951. //
  952. if (PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) {
  953. *InitialSystemState = *LightestSystemState;
  954. } else {
  955. *InitialSystemState = *DeepestSystemState;
  956. }
  957. }
  958. NTSTATUS
  959. PopDispatchStateCallout(
  960. IN PKWIN32_POWERSTATE_PARAMETERS Parms,
  961. IN PULONG SessionId OPTIONAL
  962. )
  963. /*++
  964. Routine Description:
  965. Dispatches a session state callout to PopStateCallout
  966. Arguments:
  967. Parms - Supplies the parameters
  968. SessionId - Optionally, supplies the specific session the callout should be
  969. dispatched to. If not present, the callout will be dispatched
  970. to all sessions.
  971. Return Value:
  972. NTSTATUS code.
  973. Note:
  974. For compatibility reasons, the previous behavior of MmDispatchSessionCallout
  975. only returning the status of the callout to session 0 has been maintained.
  976. --*/
  977. {
  978. NTSTATUS Status = STATUS_SUCCESS, CallStatus = STATUS_NOT_FOUND;
  979. PVOID OpaqueSession;
  980. KAPC_STATE ApcState;
  981. if (PERFINFO_IS_GROUP_ON(PERF_POWER)) {
  982. PERFINFO_PO_SESSION_CALLOUT LogEntry;
  983. LogEntry.SystemAction = Parms->SystemAction;
  984. LogEntry.MinSystemState = Parms->MinSystemState;
  985. LogEntry.Flags = Parms->Flags;
  986. LogEntry.PowerStateTask = Parms->PowerStateTask;
  987. PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_SESSION_CALLOUT,
  988. &LogEntry,
  989. sizeof(LogEntry));
  990. }
  991. if (ARGUMENT_PRESENT(SessionId)) {
  992. //
  993. // Dispatch only to the specified session.
  994. //
  995. ASSERT(*SessionId != (ULONG)-1);
  996. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  997. (*SessionId == PsGetCurrentProcessSessionId())) {
  998. //
  999. // If the call is from a user mode process, and we are asked to call
  1000. // the current session, call directly.
  1001. //
  1002. CallStatus = PopStateCallout((PVOID)Parms);
  1003. } else {
  1004. //
  1005. // Attach to the specified session.
  1006. //
  1007. OpaqueSession = MmGetSessionById(*SessionId);
  1008. if (OpaqueSession) {
  1009. Status = MmAttachSession(OpaqueSession, &ApcState);
  1010. ASSERT(NT_SUCCESS(Status));
  1011. if (NT_SUCCESS(Status)) {
  1012. CallStatus = PopStateCallout((PVOID)Parms);
  1013. Status = MmDetachSession(OpaqueSession, &ApcState);
  1014. ASSERT(NT_SUCCESS(Status));
  1015. }
  1016. Status = MmQuitNextSession(OpaqueSession);
  1017. ASSERT(NT_SUCCESS(Status));
  1018. }
  1019. }
  1020. } else {
  1021. //
  1022. // Should be dispatched to all sessions.
  1023. //
  1024. for (OpaqueSession = MmGetNextSession(NULL);
  1025. OpaqueSession != NULL;
  1026. OpaqueSession = MmGetNextSession(OpaqueSession)) {
  1027. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  1028. (MmGetSessionId(OpaqueSession) == PsGetCurrentProcessSessionId())) {
  1029. //
  1030. // If the call is from a user mode process, and we are asked to
  1031. // call the current session, call directly.
  1032. //
  1033. if (MmGetSessionId(OpaqueSession) == 0) {
  1034. CallStatus = PopStateCallout((PVOID)Parms);
  1035. } else {
  1036. PopStateCallout((PVOID)Parms);
  1037. }
  1038. } else {
  1039. //
  1040. // Attach to the specified session.
  1041. //
  1042. Status = MmAttachSession(OpaqueSession, &ApcState);
  1043. ASSERT(NT_SUCCESS(Status));
  1044. if (NT_SUCCESS(Status)) {
  1045. if (MmGetSessionId(OpaqueSession) == 0) {
  1046. CallStatus = PopStateCallout((PVOID)Parms);
  1047. } else {
  1048. PopStateCallout((PVOID)Parms);
  1049. }
  1050. Status = MmDetachSession(OpaqueSession, &ApcState);
  1051. ASSERT(NT_SUCCESS(Status));
  1052. }
  1053. }
  1054. }
  1055. }
  1056. if (PERFINFO_IS_GROUP_ON(PERF_POWER)) {
  1057. PERFINFO_PO_SESSION_CALLOUT_RET LogEntry;
  1058. LogEntry.Status = CallStatus;
  1059. PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_SESSION_CALLOUT_RET,
  1060. &LogEntry,
  1061. sizeof(LogEntry));
  1062. }
  1063. return(CallStatus);
  1064. }