[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / po / power.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/po/power.c
5 * PURPOSE: Power Manager
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Hervé Poussineau (hpoussin@reactos.com)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 typedef struct _REQUEST_POWER_ITEM
19 {
20 PREQUEST_POWER_COMPLETE CompletionRoutine;
21 POWER_STATE PowerState;
22 PVOID Context;
23 } REQUEST_POWER_ITEM, *PREQUEST_POWER_ITEM;
24
25 PDEVICE_NODE PopSystemPowerDeviceNode = NULL;
26 BOOLEAN PopAcpiPresent = FALSE;
27 POP_POWER_ACTION PopAction;
28 WORK_QUEUE_ITEM PopShutdownWorkItem;
29
30 /* PRIVATE FUNCTIONS *********************************************************/
31
32 static
33 NTSTATUS
34 NTAPI
35 PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject,
36 IN PIRP Irp,
37 IN PVOID Context)
38 {
39 PIO_STACK_LOCATION Stack;
40 PREQUEST_POWER_ITEM RequestPowerItem;
41
42 Stack = IoGetNextIrpStackLocation(Irp);
43 RequestPowerItem = (PREQUEST_POWER_ITEM)Context;
44
45 RequestPowerItem->CompletionRoutine(DeviceObject,
46 Stack->MinorFunction,
47 RequestPowerItem->PowerState,
48 RequestPowerItem->Context,
49 &Irp->IoStatus);
50
51 ExFreePool(Context);
52
53 IoFreeIrp(Irp);
54
55 return STATUS_SUCCESS;
56 }
57
58 VOID
59 NTAPI
60 PopCleanupPowerState(IN PPOWER_STATE PowerState)
61 {
62 //UNIMPLEMENTED;
63 }
64
65 NTSTATUS
66 NTAPI
67 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState)
68 {
69 IO_STATUS_BLOCK IoStatusBlock;
70 PDEVICE_OBJECT DeviceObject;
71 PIO_STACK_LOCATION IrpSp;
72 PDEVICE_OBJECT Fdo;
73 NTSTATUS Status;
74 KEVENT Event;
75 PIRP Irp;
76
77 if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
78
79 Status = IopGetSystemPowerDeviceObject(&DeviceObject);
80 if (!NT_SUCCESS(Status))
81 {
82 DPRINT1("No system power driver available\n");
83 return STATUS_UNSUCCESSFUL;
84 }
85
86 Fdo = IoGetAttachedDeviceReference(DeviceObject);
87
88 if (Fdo == DeviceObject)
89 {
90 DPRINT("An FDO was not attached\n");
91 return STATUS_UNSUCCESSFUL;
92 }
93
94 KeInitializeEvent(&Event,
95 NotificationEvent,
96 FALSE);
97
98 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
99 Fdo,
100 NULL,
101 0,
102 NULL,
103 &Event,
104 &IoStatusBlock);
105
106 IrpSp = IoGetNextIrpStackLocation(Irp);
107 IrpSp->MinorFunction = IRP_MN_SET_POWER;
108 IrpSp->Parameters.Power.Type = SystemPowerState;
109 IrpSp->Parameters.Power.State.SystemState = PowerState;
110
111 DPRINT("Calling ACPI driver");
112 Status = PoCallDriver(Fdo, Irp);
113 if (Status == STATUS_PENDING)
114 {
115 KeWaitForSingleObject(&Event,
116 Executive,
117 KernelMode,
118 FALSE,
119 NULL);
120 Status = IoStatusBlock.Status;
121 }
122
123 ObDereferenceObject(Fdo);
124
125 return Status;
126 }
127
128 BOOLEAN
129 NTAPI
130 PoInitSystem(IN ULONG BootPhase)
131 {
132 PVOID NotificationEntry;
133 PCHAR CommandLine;
134 BOOLEAN ForceAcpiDisable = FALSE;
135
136 /* Check if this is phase 1 init */
137 if (BootPhase == 1)
138 {
139 /* Register power button notification */
140 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
141 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
142 (PVOID)&GUID_DEVICE_SYS_BUTTON,
143 IopRootDeviceNode->
144 PhysicalDeviceObject->DriverObject,
145 PopAddRemoveSysCapsCallback,
146 NULL,
147 &NotificationEntry);
148
149 /* Register lid notification */
150 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
151 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
152 (PVOID)&GUID_DEVICE_LID,
153 IopRootDeviceNode->
154 PhysicalDeviceObject->DriverObject,
155 PopAddRemoveSysCapsCallback,
156 NULL,
157 &NotificationEntry);
158 return TRUE;
159 }
160
161 /* Get the Command Line */
162 CommandLine = KeLoaderBlock->LoadOptions;
163
164 /* Upcase it */
165 _strupr(CommandLine);
166
167 /* Check for ACPI disable */
168 if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE;
169
170 if (ForceAcpiDisable)
171 {
172 /* Set the ACPI State to False if it's been forced that way */
173 PopAcpiPresent = FALSE;
174 }
175 else
176 {
177 /* Otherwise check if the LoaderBlock has a ACPI Table */
178 PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE;
179 }
180
181
182 /* Initialize volume support */
183 InitializeListHead(&PopVolumeDevices);
184 KeInitializeGuardedMutex(&PopVolumeLock);
185
186 /* Initialize support for dope */
187 KeInitializeSpinLock(&PopDopeGlobalLock);
188 return TRUE;
189 }
190
191 VOID
192 NTAPI
193 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState)
194 {
195 DPRINT1("PerfIdle function: %p\n", PowerState);
196 }
197
198 VOID
199 NTAPI
200 PopPerfIdleDpc(IN PKDPC Dpc,
201 IN PVOID DeferredContext,
202 IN PVOID SystemArgument1,
203 IN PVOID SystemArgument2)
204 {
205 /* Call the Perf Idle function */
206 PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState);
207 }
208
209 VOID
210 FASTCALL
211 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
212 {
213 /* FIXME: Extremly naive implementation */
214 HalProcessorIdle();
215 }
216
217 VOID
218 NTAPI
219 PoInitializePrcb(IN PKPRCB Prcb)
220 {
221 /* Initialize the Power State */
222 RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState));
223 Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF;
224 Prcb->PowerState.CurrentThrottle = 100;
225 Prcb->PowerState.CurrentThrottleIndex = 0;
226 Prcb->PowerState.IdleFunction = PopIdle0;
227
228 /* Initialize the Perf DPC and Timer */
229 KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb);
230 KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number);
231 KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer);
232 }
233
234 /* PUBLIC FUNCTIONS **********************************************************/
235
236 /*
237 * @unimplemented
238 */
239 NTSTATUS
240 NTAPI
241 PoCancelDeviceNotify(IN PVOID NotifyBlock)
242 {
243 UNIMPLEMENTED;
244 return STATUS_NOT_IMPLEMENTED;
245 }
246
247 /*
248 * @unimplemented
249 */
250 NTSTATUS
251 NTAPI
252 PoRegisterDeviceNotify(OUT PVOID Unknown0,
253 IN ULONG Unknown1,
254 IN ULONG Unknown2,
255 IN ULONG Unknown3,
256 IN PVOID Unknown4,
257 IN PVOID Unknown5)
258 {
259 UNIMPLEMENTED;
260 return STATUS_NOT_IMPLEMENTED;
261 }
262
263 /*
264 * @unimplemented
265 */
266 VOID
267 NTAPI
268 PoShutdownBugCheck(IN BOOLEAN LogError,
269 IN ULONG BugCheckCode,
270 IN ULONG_PTR BugCheckParameter1,
271 IN ULONG_PTR BugCheckParameter2,
272 IN ULONG_PTR BugCheckParameter3,
273 IN ULONG_PTR BugCheckParameter4)
274 {
275 DPRINT1("PoShutdownBugCheck called\n");
276
277 /* FIXME: Log error if requested */
278 /* FIXME: Initiate a shutdown */
279
280 /* Bugcheck the system */
281 KeBugCheckEx(BugCheckCode,
282 BugCheckParameter1,
283 BugCheckParameter2,
284 BugCheckParameter3,
285 BugCheckParameter4);
286 }
287
288 /*
289 * @unimplemented
290 */
291 NTSTATUS
292 NTAPI
293 PoRequestShutdownEvent(OUT PVOID *Event)
294 {
295 UNIMPLEMENTED;
296 return STATUS_NOT_IMPLEMENTED;
297 }
298
299 /*
300 * @unimplemented
301 */
302 VOID
303 NTAPI
304 PoSetHiberRange(IN PVOID HiberContext,
305 IN ULONG Flags,
306 IN OUT PVOID StartPage,
307 IN ULONG Length,
308 IN ULONG PageTag)
309 {
310 UNIMPLEMENTED;
311 return;
312 }
313
314 /*
315 * @implemented
316 */
317 NTSTATUS
318 NTAPI
319 PoCallDriver(IN PDEVICE_OBJECT DeviceObject,
320 IN OUT PIRP Irp)
321 {
322 NTSTATUS Status;
323
324 /* Forward to Io -- FIXME! */
325 Status = IoCallDriver(DeviceObject, Irp);
326
327 /* Return status */
328 return Status;
329 }
330
331 /*
332 * @unimplemented
333 */
334 PULONG
335 NTAPI
336 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject,
337 IN ULONG ConservationIdleTime,
338 IN ULONG PerformanceIdleTime,
339 IN DEVICE_POWER_STATE State)
340 {
341 UNIMPLEMENTED;
342 return NULL;
343 }
344
345 /*
346 * @unimplemented
347 */
348 PVOID
349 NTAPI
350 PoRegisterSystemState(IN PVOID StateHandle,
351 IN EXECUTION_STATE Flags)
352 {
353 UNIMPLEMENTED;
354 return NULL;
355 }
356
357 /*
358 * @implemented
359 */
360 NTSTATUS
361 NTAPI
362 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
363 IN UCHAR MinorFunction,
364 IN POWER_STATE PowerState,
365 IN PREQUEST_POWER_COMPLETE CompletionFunction,
366 IN PVOID Context,
367 OUT PIRP *pIrp OPTIONAL)
368 {
369 PDEVICE_OBJECT TopDeviceObject;
370 PIO_STACK_LOCATION Stack;
371 PIRP Irp;
372 PREQUEST_POWER_ITEM RequestPowerItem;
373 NTSTATUS Status;
374
375 if (MinorFunction != IRP_MN_QUERY_POWER
376 && MinorFunction != IRP_MN_SET_POWER
377 && MinorFunction != IRP_MN_WAIT_WAKE)
378 return STATUS_INVALID_PARAMETER_2;
379
380 RequestPowerItem = ExAllocatePool(NonPagedPool, sizeof(REQUEST_POWER_ITEM));
381 if (!RequestPowerItem)
382 return STATUS_INSUFFICIENT_RESOURCES;
383
384 /* Always call the top of the device stack */
385 TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
386
387 Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_POWER,
388 TopDeviceObject,
389 NULL,
390 0,
391 NULL,
392 NULL);
393 if (!Irp)
394 {
395 ExFreePool(RequestPowerItem);
396 return STATUS_INSUFFICIENT_RESOURCES;
397 }
398
399 /* POWER IRPs are always initialized with a status code of
400 STATUS_NOT_IMPLEMENTED */
401 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
402 Irp->IoStatus.Information = 0;
403
404 Stack = IoGetNextIrpStackLocation(Irp);
405 Stack->MinorFunction = MinorFunction;
406 if (MinorFunction == IRP_MN_WAIT_WAKE)
407 Stack->Parameters.WaitWake.PowerState = PowerState.SystemState;
408 else
409 {
410 Stack->Parameters.Power.Type = DevicePowerState;
411 Stack->Parameters.Power.State = PowerState;
412 }
413
414 RequestPowerItem->CompletionRoutine = CompletionFunction;
415 RequestPowerItem->PowerState = PowerState;
416 RequestPowerItem->Context = Context;
417
418 if (pIrp != NULL)
419 *pIrp = Irp;
420
421 IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, RequestPowerItem, TRUE, TRUE, TRUE);
422 Status = IoCallDriver(TopDeviceObject, Irp);
423
424 /* Always return STATUS_PENDING. The completion routine
425 * will call CompletionFunction and complete the Irp.
426 */
427 return STATUS_PENDING;
428 }
429
430 /*
431 * @unimplemented
432 */
433 POWER_STATE
434 NTAPI
435 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject,
436 IN POWER_STATE_TYPE Type,
437 IN POWER_STATE State)
438 {
439 POWER_STATE ps;
440
441 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
442
443 ps.SystemState = PowerSystemWorking; // Fully on
444 ps.DeviceState = PowerDeviceD0; // Fully on
445
446 return ps;
447 }
448
449 /*
450 * @unimplemented
451 */
452 VOID
453 NTAPI
454 PoSetSystemState(IN EXECUTION_STATE Flags)
455 {
456 UNIMPLEMENTED;
457 }
458
459 /*
460 * @unimplemented
461 */
462 VOID
463 NTAPI
464 PoStartNextPowerIrp(IN PIRP Irp)
465 {
466 UNIMPLEMENTED;
467 }
468
469 /*
470 * @unimplemented
471 */
472 VOID
473 NTAPI
474 PoUnregisterSystemState(IN PVOID StateHandle)
475 {
476 UNIMPLEMENTED;
477 }
478
479 /*
480 * @unimplemented
481 */
482 NTSTATUS
483 NTAPI
484 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem)
485 {
486 PAGED_CODE();
487
488 UNIMPLEMENTED;
489 return STATUS_NOT_IMPLEMENTED;
490 }
491
492 /*
493 * @unimplemented
494 */
495 NTSTATUS
496 NTAPI
497 NtInitiatePowerAction (IN POWER_ACTION SystemAction,
498 IN SYSTEM_POWER_STATE MinSystemState,
499 IN ULONG Flags,
500 IN BOOLEAN Asynchronous)
501 {
502 UNIMPLEMENTED;
503 return STATUS_NOT_IMPLEMENTED;
504 }
505
506 /*
507 * @unimplemented
508 */
509 NTSTATUS
510 NTAPI
511 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel,
512 IN PVOID InputBuffer OPTIONAL,
513 IN ULONG InputBufferLength,
514 OUT PVOID OutputBuffer OPTIONAL,
515 IN ULONG OutputBufferLength)
516 {
517 NTSTATUS Status;
518
519 PAGED_CODE();
520
521 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
522 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
523 PowerInformationLevel,
524 InputBuffer, InputBufferLength,
525 OutputBuffer, OutputBufferLength);
526
527 switch (PowerInformationLevel)
528 {
529 case SystemBatteryState:
530 {
531 PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer;
532
533 if (InputBuffer != NULL)
534 return STATUS_INVALID_PARAMETER;
535 if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE))
536 return STATUS_BUFFER_TOO_SMALL;
537
538 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
539 RtlZeroMemory(BatteryState, sizeof(SYSTEM_BATTERY_STATE));
540 BatteryState->EstimatedTime = MAXULONG;
541
542 Status = STATUS_SUCCESS;
543 break;
544 }
545 case SystemPowerCapabilities:
546 {
547 PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer;
548
549 if (InputBuffer != NULL)
550 return STATUS_INVALID_PARAMETER;
551 if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES))
552 return STATUS_BUFFER_TOO_SMALL;
553
554 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
555 RtlZeroMemory(PowerCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES));
556 //PowerCapabilities->SystemBatteriesPresent = 0;
557
558 Status = STATUS_SUCCESS;
559 break;
560 }
561
562 default:
563 Status = STATUS_NOT_IMPLEMENTED;
564 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
565 PowerInformationLevel);
566 break;
567 }
568
569 return Status;
570 }
571
572 NTSTATUS
573 NTAPI
574 NtGetDevicePowerState(IN HANDLE Device,
575 IN PDEVICE_POWER_STATE PowerState)
576 {
577 UNIMPLEMENTED;
578 return STATUS_NOT_IMPLEMENTED;
579 }
580
581 BOOLEAN
582 NTAPI
583 NtIsSystemResumeAutomatic(VOID)
584 {
585 UNIMPLEMENTED;
586 return FALSE;
587 }
588
589 NTSTATUS
590 NTAPI
591 NtRequestWakeupLatency(IN LATENCY_TIME Latency)
592 {
593 UNIMPLEMENTED;
594 return STATUS_NOT_IMPLEMENTED;
595 }
596
597 NTSTATUS
598 NTAPI
599 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
600 OUT EXECUTION_STATE *PreviousFlags)
601 {
602 PKTHREAD Thread = KeGetCurrentThread();
603 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
604 EXECUTION_STATE PreviousState;
605 PAGED_CODE();
606
607 /* Validate flags */
608 if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
609 {
610 /* Fail the request */
611 return STATUS_INVALID_PARAMETER;
612 }
613
614 /* Check for user parameters */
615 if (PreviousMode != KernelMode)
616 {
617 /* Protect the probes */
618 _SEH2_TRY
619 {
620 /* Check if the pointer is valid */
621 ProbeForWriteUlong(PreviousFlags);
622 }
623 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
624 {
625 /* It isn't -- fail */
626 _SEH2_YIELD(return _SEH2_GetExceptionCode());
627 }
628 _SEH2_END;
629 }
630
631 /* Save the previous state, always masking in the continous flag */
632 PreviousState = Thread->PowerState | ES_CONTINUOUS;
633
634 /* Check if we need to update the power state */
635 if (esFlags & ES_CONTINUOUS) Thread->PowerState = esFlags;
636
637 /* Protect the write back to user mode */
638 _SEH2_TRY
639 {
640 /* Return the previous flags */
641 *PreviousFlags = PreviousState;
642 }
643 _SEH2_EXCEPT(ExSystemExceptionFilter())
644 {
645 /* Something's wrong, fail */
646 _SEH2_YIELD(return _SEH2_GetExceptionCode());
647 }
648 _SEH2_END;
649
650 /* All is good */
651 return STATUS_SUCCESS;
652 }
653
654 NTSTATUS
655 NTAPI
656 NtSetSystemPowerState(IN POWER_ACTION SystemAction,
657 IN SYSTEM_POWER_STATE MinSystemState,
658 IN ULONG Flags)
659 {
660 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
661 POP_POWER_ACTION Action = {0};
662 NTSTATUS Status;
663
664 /* Check for invalid parameter combinations */
665 if ((MinSystemState >= PowerSystemMaximum) ||
666 (MinSystemState <= PowerSystemUnspecified) ||
667 (SystemAction > PowerActionWarmEject) ||
668 (SystemAction < PowerActionReserved) ||
669 (Flags & ~(POWER_ACTION_QUERY_ALLOWED |
670 POWER_ACTION_UI_ALLOWED |
671 POWER_ACTION_OVERRIDE_APPS |
672 POWER_ACTION_LIGHTEST_FIRST |
673 POWER_ACTION_LOCK_CONSOLE |
674 POWER_ACTION_DISABLE_WAKES |
675 POWER_ACTION_CRITICAL)))
676 {
677 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
678 DPRINT1(" SystemAction: 0x%x\n", SystemAction);
679 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState);
680 DPRINT1(" Flags: 0x%x\n", Flags);
681 return STATUS_INVALID_PARAMETER;
682 }
683
684 /* Check for user caller */
685 if (PreviousMode != KernelMode)
686 {
687 /* Check for shutdown permission */
688 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
689 {
690 /* Not granted */
691 DPRINT1("ERROR: Privilege not held for shutdown\n");
692 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
693 }
694
695 /* Do it as a kernel-mode caller for consistency with system state */
696 return ZwSetSystemPowerState (SystemAction, MinSystemState, Flags);
697 }
698
699 /* Read policy settings (partial shutdown vs. full shutdown) */
700 if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
701
702 /* Disable lazy flushing of registry */
703 DPRINT1("Stopping lazy flush\n");
704 CmSetLazyFlushState(FALSE);
705
706 /* Setup the power action */
707 Action.Action = SystemAction;
708 Action.Flags = Flags;
709
710 /* Notify callbacks */
711 DPRINT1("Notifying callbacks\n");
712 ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
713
714 /* Swap in any worker thread stacks */
715 DPRINT1("Swapping worker threads\n");
716 ExSwapinWorkerThreads(FALSE);
717
718 /* Make our action global */
719 PopAction = Action;
720
721 /* Start power loop */
722 Status = STATUS_CANCELLED;
723 while (TRUE)
724 {
725 /* Break out if there's nothing to do */
726 if (Action.Action == PowerActionNone) break;
727
728 /* Check for first-pass or restart */
729 if (Status == STATUS_CANCELLED)
730 {
731 /* Check for shutdown action */
732 if ((PopAction.Action == PowerActionShutdown) ||
733 (PopAction.Action == PowerActionShutdownReset) ||
734 (PopAction.Action == PowerActionShutdownOff))
735 {
736 /* Set the action */
737 PopAction.Shutdown = TRUE;
738 }
739
740 /* Now we are good to go */
741 Status = STATUS_SUCCESS;
742 }
743
744 /* Check if we're still in an invalid status */
745 if (!NT_SUCCESS(Status)) break;
746
747 /* Flush all volumes and the registry */
748 DPRINT1("Flushing volumes\n");
749 PopFlushVolumes(PopAction.Shutdown);
750
751 /* Set IRP for drivers */
752 PopAction.IrpMinor = IRP_MN_SET_POWER;
753 if (PopAction.Shutdown)
754 {
755 DPRINT1("Queueing shutdown thread\n");
756 /* Check if we are running in the system context */
757 if (PsGetCurrentProcess() != PsInitialSystemProcess)
758 {
759 /* We're not, so use a worker thread for shutdown */
760 ExInitializeWorkItem(&PopShutdownWorkItem,
761 &PopGracefulShutdown,
762 NULL);
763
764 ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
765
766 /* Spend us -- when we wake up, the system is good to go down */
767 KeSuspendThread(KeGetCurrentThread());
768 Status = STATUS_SYSTEM_SHUTDOWN;
769 goto Exit;
770
771 }
772 else
773 {
774 /* Do the shutdown inline */
775 PopGracefulShutdown(NULL);
776 }
777 }
778
779 /* You should not have made it this far */
780 ASSERT(FALSE && "System is still up and running?!");
781 break;
782 }
783
784 Exit:
785 /* We're done, return */
786 return Status;
787 }