xref: /reactos/ntoskrnl/po/power.c (revision 201f00ab)
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 _POWER_STATE_TRAVERSE_CONTEXT
19 {
20     SYSTEM_POWER_STATE SystemPowerState;
21     POWER_ACTION PowerAction;
22     PDEVICE_OBJECT PowerDevice;
23 } POWER_STATE_TRAVERSE_CONTEXT, *PPOWER_STATE_TRAVERSE_CONTEXT;
24 
25 PDEVICE_NODE PopSystemPowerDeviceNode = NULL;
26 BOOLEAN PopAcpiPresent = FALSE;
27 POP_POWER_ACTION PopAction;
28 WORK_QUEUE_ITEM PopShutdownWorkItem;
29 SYSTEM_POWER_CAPABILITIES PopCapabilities;
30 
31 /* PRIVATE FUNCTIONS *********************************************************/
32 
33 static WORKER_THREAD_ROUTINE PopPassivePowerCall;
34 _Use_decl_annotations_
35 static
36 VOID
37 NTAPI
38 PopPassivePowerCall(
39     PVOID Parameter)
40 {
41     PIRP Irp = Parameter;
42     PIO_STACK_LOCATION IoStack;
43 
44     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
45 
46     _Analysis_assume_(Irp != NULL);
47     IoStack = IoGetNextIrpStackLocation(Irp);
48 
49     (VOID)IoCallDriver(IoStack->DeviceObject, Irp);
50 }
51 
52 _IRQL_requires_max_(DISPATCH_LEVEL)
53 _IRQL_requires_same_
54 static
55 NTSTATUS
56 PopPresentIrp(
57     _In_ PIO_STACK_LOCATION NextStack,
58     _In_ PIRP Irp)
59 {
60     NTSTATUS Status;
61     BOOLEAN CallAtPassiveLevel;
62     PDEVICE_OBJECT DeviceObject;
63     PWORK_QUEUE_ITEM WorkQueueItem;
64 
65     ASSERT(NextStack->MajorFunction == IRP_MJ_POWER);
66 
67     DeviceObject = NextStack->DeviceObject;
68 
69     /* Determine whether the IRP must be handled at PASSIVE_LEVEL.
70      * Only SET_POWER to working state can happen at raised IRQL. */
71     CallAtPassiveLevel = TRUE;
72     if ((NextStack->MinorFunction == IRP_MN_SET_POWER) &&
73         !(DeviceObject->Flags & DO_POWER_PAGABLE))
74     {
75         if (NextStack->Parameters.Power.Type == DevicePowerState &&
76             NextStack->Parameters.Power.State.DeviceState == PowerDeviceD0)
77         {
78             CallAtPassiveLevel = FALSE;
79         }
80         if (NextStack->Parameters.Power.Type == SystemPowerState &&
81             NextStack->Parameters.Power.State.SystemState == PowerSystemWorking)
82         {
83             CallAtPassiveLevel = FALSE;
84         }
85     }
86 
87     if (CallAtPassiveLevel)
88     {
89         /* We need to fit a work item into the DriverContext below */
90         C_ASSERT(sizeof(Irp->Tail.Overlay.DriverContext) >= sizeof(WORK_QUEUE_ITEM));
91 
92         if (KeGetCurrentIrql() == PASSIVE_LEVEL)
93         {
94             /* Already at passive, call next driver directly */
95             return IoCallDriver(DeviceObject, Irp);
96         }
97 
98         /* Need to schedule a work item and return pending */
99         NextStack->Control |= SL_PENDING_RETURNED;
100 
101         WorkQueueItem = (PWORK_QUEUE_ITEM)&Irp->Tail.Overlay.DriverContext;
102         ExInitializeWorkItem(WorkQueueItem,
103                              PopPassivePowerCall,
104                              Irp);
105         ExQueueWorkItem(WorkQueueItem, DelayedWorkQueue);
106 
107         return STATUS_PENDING;
108     }
109 
110     /* Direct call. Raise IRQL in debug to catch invalid paged memory access. */
111 #if DBG
112     {
113     KIRQL OldIrql;
114     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
115 #endif
116 
117     Status = IoCallDriver(DeviceObject, Irp);
118 
119 #if DBG
120     KeLowerIrql(OldIrql);
121     }
122 #endif
123 
124     return Status;
125 }
126 
127 static IO_COMPLETION_ROUTINE PopRequestPowerIrpCompletion;
128 
129 static
130 NTSTATUS
131 NTAPI
132 PopRequestPowerIrpCompletion(
133     _In_ PDEVICE_OBJECT DeviceObject,
134     _In_ PIRP Irp,
135     _In_reads_opt_(_Inexpressible_("varies")) PVOID Context)
136 {
137     PIO_STACK_LOCATION Stack;
138     PREQUEST_POWER_COMPLETE CompletionRoutine;
139     POWER_STATE PowerState;
140 
141     Stack = IoGetCurrentIrpStackLocation(Irp);
142     CompletionRoutine = Context;
143 
144     PowerState.DeviceState = (ULONG_PTR)Stack->Parameters.Others.Argument3;
145 
146     if (CompletionRoutine)
147     {
148         CompletionRoutine(Stack->Parameters.Others.Argument1,
149                           (UCHAR)(ULONG_PTR)Stack->Parameters.Others.Argument2,
150                           PowerState,
151                           Stack->Parameters.Others.Argument4,
152                           &Irp->IoStatus);
153     }
154 
155     IoSkipCurrentIrpStackLocation(Irp);
156     IoFreeIrp(Irp);
157     ObDereferenceObject(DeviceObject);
158 
159     return STATUS_MORE_PROCESSING_REQUIRED;
160 }
161 
162 VOID
163 NTAPI
164 PopCleanupPowerState(IN PPOWER_STATE PowerState)
165 {
166     //UNIMPLEMENTED;
167 }
168 
169 NTSTATUS
170 PopSendQuerySystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
171 {
172     KEVENT Event;
173     IO_STATUS_BLOCK IoStatusBlock;
174     PIO_STACK_LOCATION IrpSp;
175     PIRP Irp;
176     NTSTATUS Status;
177 
178     KeInitializeEvent(&Event,
179                       NotificationEvent,
180                       FALSE);
181 
182     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
183                                        DeviceObject,
184                                        NULL,
185                                        0,
186                                        NULL,
187                                        &Event,
188                                        &IoStatusBlock);
189     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
190 
191     IrpSp = IoGetNextIrpStackLocation(Irp);
192     IrpSp->MinorFunction = IRP_MN_QUERY_POWER;
193     IrpSp->Parameters.Power.Type = SystemPowerState;
194     IrpSp->Parameters.Power.State.SystemState = SystemState;
195     IrpSp->Parameters.Power.ShutdownType = PowerAction;
196 
197     Status = PoCallDriver(DeviceObject, Irp);
198     if (Status == STATUS_PENDING)
199     {
200         KeWaitForSingleObject(&Event,
201                               Executive,
202                               KernelMode,
203                               FALSE,
204                               NULL);
205         Status = IoStatusBlock.Status;
206     }
207 
208     return Status;
209 }
210 
211 NTSTATUS
212 PopSendSetSystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
213 {
214     KEVENT Event;
215     IO_STATUS_BLOCK IoStatusBlock;
216     PIO_STACK_LOCATION IrpSp;
217     PIRP Irp;
218     NTSTATUS Status;
219 
220     KeInitializeEvent(&Event,
221                       NotificationEvent,
222                       FALSE);
223 
224     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
225                                        DeviceObject,
226                                        NULL,
227                                        0,
228                                        NULL,
229                                        &Event,
230                                        &IoStatusBlock);
231     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
232 
233     IrpSp = IoGetNextIrpStackLocation(Irp);
234     IrpSp->MinorFunction = IRP_MN_SET_POWER;
235     IrpSp->Parameters.Power.Type = SystemPowerState;
236     IrpSp->Parameters.Power.State.SystemState = SystemState;
237     IrpSp->Parameters.Power.ShutdownType = PowerAction;
238 
239     Status = PoCallDriver(DeviceObject, Irp);
240     if (Status == STATUS_PENDING)
241     {
242         KeWaitForSingleObject(&Event,
243                               Executive,
244                               KernelMode,
245                               FALSE,
246                               NULL);
247         Status = IoStatusBlock.Status;
248     }
249 
250     return Status;
251 }
252 
253 NTSTATUS
254 PopQuerySystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
255                                  PVOID Context)
256 {
257     PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
258     PDEVICE_OBJECT TopDeviceObject;
259     NTSTATUS Status;
260 
261     DPRINT("PopQuerySystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
262 
263     if (DeviceNode == IopRootDeviceNode)
264         return STATUS_SUCCESS;
265 
266     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
267         return STATUS_SUCCESS;
268 
269     TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
270 
271     Status = PopSendQuerySystemPowerState(TopDeviceObject,
272                                           PowerStateContext->SystemPowerState,
273                                           PowerStateContext->PowerAction);
274     if (!NT_SUCCESS(Status))
275     {
276         DPRINT1("Device '%wZ' failed IRP_MN_QUERY_POWER\n", &DeviceNode->InstancePath);
277     }
278     ObDereferenceObject(TopDeviceObject);
279 
280 #if 0
281     return Status;
282 #else
283     return STATUS_SUCCESS;
284 #endif
285 }
286 
287 NTSTATUS
288 PopSetSystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
289                                PVOID Context)
290 {
291     PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
292     PDEVICE_OBJECT TopDeviceObject;
293     NTSTATUS Status;
294 
295     DPRINT("PopSetSystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
296 
297     if (DeviceNode == IopRootDeviceNode)
298         return STATUS_SUCCESS;
299 
300     if (DeviceNode->PhysicalDeviceObject == PowerStateContext->PowerDevice)
301         return STATUS_SUCCESS;
302 
303     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
304         return STATUS_SUCCESS;
305 
306     TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
307     if (TopDeviceObject == PowerStateContext->PowerDevice)
308     {
309         ObDereferenceObject(TopDeviceObject);
310         return STATUS_SUCCESS;
311     }
312 
313     Status = PopSendSetSystemPowerState(TopDeviceObject,
314                                         PowerStateContext->SystemPowerState,
315                                         PowerStateContext->PowerAction);
316     if (!NT_SUCCESS(Status))
317     {
318         DPRINT1("Device '%wZ' failed IRP_MN_SET_POWER\n", &DeviceNode->InstancePath);
319     }
320 
321     ObDereferenceObject(TopDeviceObject);
322 
323 #if 0
324     return Status;
325 #else
326     return STATUS_SUCCESS;
327 #endif
328 }
329 
330 NTSTATUS
331 NTAPI
332 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState, POWER_ACTION PowerAction)
333 {
334     PDEVICE_OBJECT DeviceObject;
335     PDEVICE_OBJECT Fdo;
336     NTSTATUS Status;
337     DEVICETREE_TRAVERSE_CONTEXT Context;
338     POWER_STATE_TRAVERSE_CONTEXT PowerContext;
339 
340     Status = IopGetSystemPowerDeviceObject(&DeviceObject);
341     if (!NT_SUCCESS(Status))
342     {
343         DPRINT1("No system power driver available\n");
344         Fdo = NULL;
345     }
346     else
347     {
348         Fdo = IoGetAttachedDeviceReference(DeviceObject);
349         if (Fdo == DeviceObject)
350         {
351             DPRINT("An FDO was not attached\n");
352             return STATUS_UNSUCCESSFUL;
353         }
354     }
355 
356     /* Set up context */
357     PowerContext.PowerAction = PowerAction;
358     PowerContext.SystemPowerState = PowerState;
359     PowerContext.PowerDevice = Fdo;
360 
361     /* Query for system power change */
362     IopInitDeviceTreeTraverseContext(&Context,
363                                      IopRootDeviceNode,
364                                      PopQuerySystemPowerStateTraverse,
365                                      &PowerContext);
366 
367     Status = IopTraverseDeviceTree(&Context);
368     if (!NT_SUCCESS(Status))
369     {
370         DPRINT1("Query system power state failed; changing state anyway\n");
371     }
372 
373     /* Set system power change */
374     IopInitDeviceTreeTraverseContext(&Context,
375                                      IopRootDeviceNode,
376                                      PopSetSystemPowerStateTraverse,
377                                      &PowerContext);
378 
379     IopTraverseDeviceTree(&Context);
380 
381     if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
382 
383     if (Fdo != NULL)
384     {
385         if (PowerAction != PowerActionShutdownReset)
386             PopSendSetSystemPowerState(Fdo, PowerState, PowerAction);
387 
388         ObDereferenceObject(Fdo);
389     }
390 
391     return Status;
392 }
393 
394 CODE_SEG("INIT")
395 BOOLEAN
396 NTAPI
397 PoInitSystem(IN ULONG BootPhase)
398 {
399     PVOID NotificationEntry;
400     PCHAR CommandLine;
401     BOOLEAN ForceAcpiDisable = FALSE;
402 
403     /* Check if this is phase 1 init */
404     if (BootPhase == 1)
405     {
406         NTSTATUS Status;
407         /* Register power button notification */
408         Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
409                                                 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
410                                                 (PVOID)&GUID_DEVICE_SYS_BUTTON,
411                                                 IopRootDeviceNode->PhysicalDeviceObject->DriverObject,
412                                                 PopAddRemoveSysCapsCallback,
413                                                 (PVOID)(ULONG_PTR)PolicyDeviceSystemButton,
414                                                 &NotificationEntry);
415         if (!NT_SUCCESS(Status))
416             return FALSE;
417 
418         /* Register lid notification */
419         Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
420                                                 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
421                                                 (PVOID)&GUID_DEVICE_LID,
422                                                 IopRootDeviceNode->PhysicalDeviceObject->DriverObject,
423                                                 PopAddRemoveSysCapsCallback,
424                                                 (PVOID)(ULONG_PTR)PolicyDeviceSystemButton,
425                                                 &NotificationEntry);
426         if (!NT_SUCCESS(Status))
427             return FALSE;
428 
429         /* Register battery notification */
430         Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
431                                                 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
432                                                 (PVOID)&GUID_DEVICE_BATTERY,
433                                                 IopRootDeviceNode->PhysicalDeviceObject->DriverObject,
434                                                 PopAddRemoveSysCapsCallback,
435                                                 (PVOID)(ULONG_PTR)PolicyDeviceBattery,
436                                                 &NotificationEntry);
437 
438         return NT_SUCCESS(Status);
439     }
440 
441     /* Initialize the power capabilities */
442     RtlZeroMemory(&PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES));
443 
444     /* Get the Command Line */
445     CommandLine = KeLoaderBlock->LoadOptions;
446 
447     /* Upcase it */
448     _strupr(CommandLine);
449 
450     /* Check for ACPI disable */
451     if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE;
452 
453     if (ForceAcpiDisable)
454     {
455         /* Set the ACPI State to False if it's been forced that way */
456         PopAcpiPresent = FALSE;
457     }
458     else
459     {
460         /* Otherwise check if the LoaderBlock has a ACPI Table  */
461         PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE;
462     }
463 
464     /* Enable shutdown by power button */
465     if (PopAcpiPresent)
466         PopCapabilities.SystemS5 = TRUE;
467 
468     /* Initialize volume support */
469     InitializeListHead(&PopVolumeDevices);
470     KeInitializeGuardedMutex(&PopVolumeLock);
471 
472     /* Initialize support for dope */
473     KeInitializeSpinLock(&PopDopeGlobalLock);
474 
475     /* Initialize support for shutdown waits and work-items */
476     PopInitShutdownList();
477 
478     return TRUE;
479 }
480 
481 VOID
482 NTAPI
483 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState)
484 {
485     DPRINT1("PerfIdle function: %p\n", PowerState);
486 }
487 
488 VOID
489 NTAPI
490 PopPerfIdleDpc(IN PKDPC Dpc,
491                IN PVOID DeferredContext,
492                IN PVOID SystemArgument1,
493                IN PVOID SystemArgument2)
494 {
495     /* Call the Perf Idle function */
496     PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState);
497 }
498 
499 VOID
500 FASTCALL
501 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
502 {
503     /* FIXME: Extremly naive implementation */
504     HalProcessorIdle();
505 }
506 
507 CODE_SEG("INIT")
508 VOID
509 NTAPI
510 PoInitializePrcb(IN PKPRCB Prcb)
511 {
512     /* Initialize the Power State */
513     RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState));
514     Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF;
515     Prcb->PowerState.CurrentThrottle = 100;
516     Prcb->PowerState.CurrentThrottleIndex = 0;
517     Prcb->PowerState.IdleFunction = PopIdle0;
518 
519     /* Initialize the Perf DPC and Timer */
520     KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb);
521     KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number);
522     KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer);
523 }
524 
525 /* PUBLIC FUNCTIONS **********************************************************/
526 
527 /*
528  * @unimplemented
529  */
530 NTSTATUS
531 NTAPI
532 PoCancelDeviceNotify(IN PVOID NotifyBlock)
533 {
534     UNIMPLEMENTED;
535     return STATUS_NOT_IMPLEMENTED;
536 }
537 
538 /*
539  * @unimplemented
540  */
541 NTSTATUS
542 NTAPI
543 PoRegisterDeviceNotify(OUT PVOID Unknown0,
544                        IN ULONG Unknown1,
545                        IN ULONG Unknown2,
546                        IN ULONG Unknown3,
547                        IN PVOID Unknown4,
548                        IN PVOID Unknown5)
549 {
550     UNIMPLEMENTED;
551     return STATUS_NOT_IMPLEMENTED;
552 }
553 
554 /*
555  * @unimplemented
556  */
557 VOID
558 NTAPI
559 PoShutdownBugCheck(IN BOOLEAN LogError,
560                    IN ULONG BugCheckCode,
561                    IN ULONG_PTR BugCheckParameter1,
562                    IN ULONG_PTR BugCheckParameter2,
563                    IN ULONG_PTR BugCheckParameter3,
564                    IN ULONG_PTR BugCheckParameter4)
565 {
566     DPRINT1("PoShutdownBugCheck called\n");
567 
568     /* FIXME: Log error if requested */
569     /* FIXME: Initiate a shutdown */
570 
571     /* Bugcheck the system */
572     KeBugCheckEx(BugCheckCode,
573                  BugCheckParameter1,
574                  BugCheckParameter2,
575                  BugCheckParameter3,
576                  BugCheckParameter4);
577 }
578 
579 /*
580  * @unimplemented
581  */
582 VOID
583 NTAPI
584 PoSetHiberRange(IN PVOID HiberContext,
585                 IN ULONG Flags,
586                 IN OUT PVOID StartPage,
587                 IN ULONG Length,
588                 IN ULONG PageTag)
589 {
590     UNIMPLEMENTED;
591     return;
592 }
593 
594 /*
595  * @implemented
596  */
597 _IRQL_requires_max_(DISPATCH_LEVEL)
598 NTSTATUS
599 NTAPI
600 PoCallDriver(
601     _In_ PDEVICE_OBJECT DeviceObject,
602     _Inout_ __drv_aliasesMem PIRP Irp)
603 {
604     PIO_STACK_LOCATION NextStack;
605 
606     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
607 
608     ASSERT(DeviceObject);
609     ASSERT(Irp);
610 
611     NextStack = IoGetNextIrpStackLocation(Irp);
612     ASSERT(NextStack->MajorFunction == IRP_MJ_POWER);
613 
614     /* Set DeviceObject for PopPresentIrp */
615     NextStack->DeviceObject = DeviceObject;
616 
617     /* Only QUERY_POWER and SET_POWER use special handling */
618     if (NextStack->MinorFunction != IRP_MN_SET_POWER &&
619         NextStack->MinorFunction != IRP_MN_QUERY_POWER)
620     {
621         return IoCallDriver(DeviceObject, Irp);
622     }
623 
624     /* Call the next driver, either directly or at PASSIVE_LEVEL */
625     return PopPresentIrp(NextStack, Irp);
626 }
627 
628 /*
629  * @unimplemented
630  */
631 PULONG
632 NTAPI
633 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject,
634                                  IN ULONG ConservationIdleTime,
635                                  IN ULONG PerformanceIdleTime,
636                                  IN DEVICE_POWER_STATE State)
637 {
638     UNIMPLEMENTED;
639     return NULL;
640 }
641 
642 /*
643  * @unimplemented
644  */
645 PVOID
646 NTAPI
647 PoRegisterSystemState(IN PVOID StateHandle,
648                       IN EXECUTION_STATE Flags)
649 {
650     UNIMPLEMENTED;
651     return NULL;
652 }
653 
654 /*
655  * @implemented
656  */
657 NTSTATUS
658 NTAPI
659 PoRequestPowerIrp(
660     _In_ PDEVICE_OBJECT DeviceObject,
661     _In_ UCHAR MinorFunction,
662     _In_ POWER_STATE PowerState,
663     _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction,
664     _In_opt_ __drv_aliasesMem PVOID Context,
665     _Outptr_opt_ PIRP *pIrp)
666 {
667     PDEVICE_OBJECT TopDeviceObject;
668     PIO_STACK_LOCATION Stack;
669     PIRP Irp;
670 
671     if (MinorFunction != IRP_MN_QUERY_POWER
672         && MinorFunction != IRP_MN_SET_POWER
673         && MinorFunction != IRP_MN_WAIT_WAKE)
674         return STATUS_INVALID_PARAMETER_2;
675 
676     /* Always call the top of the device stack */
677     TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
678 
679     Irp = IoAllocateIrp(TopDeviceObject->StackSize + 2, FALSE);
680     if (!Irp)
681     {
682         ObDereferenceObject(TopDeviceObject);
683         return STATUS_INSUFFICIENT_RESOURCES;
684     }
685 
686     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
687     Irp->IoStatus.Information = 0;
688 
689     IoSetNextIrpStackLocation(Irp);
690 
691     Stack = IoGetNextIrpStackLocation(Irp);
692     Stack->Parameters.Others.Argument1 = DeviceObject;
693     Stack->Parameters.Others.Argument2 = (PVOID)(ULONG_PTR)MinorFunction;
694     Stack->Parameters.Others.Argument3 = (PVOID)(ULONG_PTR)PowerState.DeviceState;
695     Stack->Parameters.Others.Argument4 = Context;
696     Stack->DeviceObject = TopDeviceObject;
697     IoSetNextIrpStackLocation(Irp);
698 
699     Stack = IoGetNextIrpStackLocation(Irp);
700     Stack->MajorFunction = IRP_MJ_POWER;
701     Stack->MinorFunction = MinorFunction;
702     if (MinorFunction == IRP_MN_WAIT_WAKE)
703     {
704         Stack->Parameters.WaitWake.PowerState = PowerState.SystemState;
705     }
706     else
707     {
708         Stack->Parameters.Power.Type = DevicePowerState;
709         Stack->Parameters.Power.State = PowerState;
710     }
711 
712     if (pIrp != NULL)
713         *pIrp = Irp;
714 
715     IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, CompletionFunction, TRUE, TRUE, TRUE);
716     PoCallDriver(TopDeviceObject, Irp);
717 
718     /* Always return STATUS_PENDING. The completion routine
719      * will call CompletionFunction and complete the Irp.
720      */
721     return STATUS_PENDING;
722 }
723 
724 /*
725  * @unimplemented
726  */
727 POWER_STATE
728 NTAPI
729 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject,
730                 IN POWER_STATE_TYPE Type,
731                 IN POWER_STATE State)
732 {
733     POWER_STATE ps;
734 
735     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
736 
737     ps.SystemState = PowerSystemWorking;  // Fully on
738     ps.DeviceState = PowerDeviceD0;       // Fully on
739 
740     return ps;
741 }
742 
743 /*
744  * @unimplemented
745  */
746 VOID
747 NTAPI
748 PoSetSystemState(IN EXECUTION_STATE Flags)
749 {
750     UNIMPLEMENTED;
751 }
752 
753 /*
754  * @unimplemented
755  */
756 VOID
757 NTAPI
758 PoStartNextPowerIrp(IN PIRP Irp)
759 {
760     UNIMPLEMENTED_ONCE;
761 }
762 
763 /*
764  * @unimplemented
765  */
766 VOID
767 NTAPI
768 PoUnregisterSystemState(IN PVOID StateHandle)
769 {
770     UNIMPLEMENTED;
771 }
772 
773 /*
774  * @unimplemented
775  */
776 NTSTATUS
777 NTAPI
778 NtInitiatePowerAction(IN POWER_ACTION SystemAction,
779                       IN SYSTEM_POWER_STATE MinSystemState,
780                       IN ULONG Flags,
781                       IN BOOLEAN Asynchronous)
782 {
783     UNIMPLEMENTED;
784     return STATUS_NOT_IMPLEMENTED;
785 }
786 
787 /*
788  * @unimplemented
789  */
790 NTSTATUS
791 NTAPI
792 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel,
793                    IN PVOID InputBuffer  OPTIONAL,
794                    IN ULONG InputBufferLength,
795                    OUT PVOID OutputBuffer  OPTIONAL,
796                    IN ULONG OutputBufferLength)
797 {
798     NTSTATUS Status;
799     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
800 
801     PAGED_CODE();
802 
803     DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%p, "
804            "InputBufferLength 0x%x, OutputBuffer 0x%p, OutputBufferLength 0x%x)\n",
805            PowerInformationLevel,
806            InputBuffer, InputBufferLength,
807            OutputBuffer, OutputBufferLength);
808 
809     if (PreviousMode != KernelMode)
810     {
811         _SEH2_TRY
812         {
813             ProbeForRead(InputBuffer, InputBufferLength, 1);
814             ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));
815         }
816         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
817         {
818             _SEH2_YIELD(return _SEH2_GetExceptionCode());
819         }
820         _SEH2_END;
821     }
822 
823     switch (PowerInformationLevel)
824     {
825         case SystemBatteryState:
826         {
827             PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer;
828 
829             if (InputBuffer != NULL)
830                 return STATUS_INVALID_PARAMETER;
831             if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE))
832                 return STATUS_BUFFER_TOO_SMALL;
833 
834             _SEH2_TRY
835             {
836                 /* Just zero the struct */
837                 RtlZeroMemory(BatteryState, sizeof(*BatteryState));
838                 BatteryState->EstimatedTime = MAXULONG;
839                 BatteryState->BatteryPresent = PopCapabilities.SystemBatteriesPresent;
840 //                BatteryState->AcOnLine = TRUE;
841 //                BatteryState->MaxCapacity = ;
842 //                BatteryState->RemainingCapacity = ;
843 
844                 Status = STATUS_SUCCESS;
845             }
846             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
847             {
848                 Status = _SEH2_GetExceptionCode();
849             }
850             _SEH2_END;
851 
852             break;
853         }
854 
855         case SystemPowerCapabilities:
856         {
857             PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer;
858 
859             if (InputBuffer != NULL)
860                 return STATUS_INVALID_PARAMETER;
861             if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES))
862                 return STATUS_BUFFER_TOO_SMALL;
863 
864             _SEH2_TRY
865             {
866                 RtlCopyMemory(PowerCapabilities,
867                               &PopCapabilities,
868                               sizeof(SYSTEM_POWER_CAPABILITIES));
869 
870                 Status = STATUS_SUCCESS;
871             }
872             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
873             {
874                 Status = _SEH2_GetExceptionCode();
875             }
876             _SEH2_END;
877 
878             break;
879         }
880 
881         case ProcessorInformation:
882         {
883             PPROCESSOR_POWER_INFORMATION PowerInformation = (PPROCESSOR_POWER_INFORMATION)OutputBuffer;
884 
885             if (InputBuffer != NULL)
886                 return STATUS_INVALID_PARAMETER;
887             if (OutputBufferLength < sizeof(PROCESSOR_POWER_INFORMATION))
888                 return STATUS_BUFFER_TOO_SMALL;
889 
890             /* FIXME: return structures for all processors */
891 
892             _SEH2_TRY
893             {
894                 /* FIXME: some values are hardcoded */
895                 PowerInformation->Number = 0;
896                 PowerInformation->MaxMhz = 1000;
897                 PowerInformation->CurrentMhz = KeGetCurrentPrcb()->MHz;
898                 PowerInformation->MhzLimit = 1000;
899                 PowerInformation->MaxIdleState = 0;
900                 PowerInformation->CurrentIdleState = 0;
901 
902                 Status = STATUS_SUCCESS;
903             }
904             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
905             {
906                 Status = _SEH2_GetExceptionCode();
907             }
908             _SEH2_END;
909 
910             break;
911         }
912 
913         default:
914             Status = STATUS_NOT_IMPLEMENTED;
915             DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
916                     PowerInformationLevel);
917             break;
918     }
919 
920     return Status;
921 }
922 
923 NTSTATUS
924 NTAPI
925 NtGetDevicePowerState(IN HANDLE Device,
926                       IN PDEVICE_POWER_STATE PowerState)
927 {
928     UNIMPLEMENTED;
929     return STATUS_NOT_IMPLEMENTED;
930 }
931 
932 BOOLEAN
933 NTAPI
934 NtIsSystemResumeAutomatic(VOID)
935 {
936     UNIMPLEMENTED;
937     return FALSE;
938 }
939 
940 NTSTATUS
941 NTAPI
942 NtRequestWakeupLatency(IN LATENCY_TIME Latency)
943 {
944     UNIMPLEMENTED;
945     return STATUS_NOT_IMPLEMENTED;
946 }
947 
948 NTSTATUS
949 NTAPI
950 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
951                           OUT EXECUTION_STATE *PreviousFlags)
952 {
953     PKTHREAD Thread = KeGetCurrentThread();
954     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
955     EXECUTION_STATE PreviousState;
956     PAGED_CODE();
957 
958     /* Validate flags */
959     if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
960     {
961         /* Fail the request */
962         return STATUS_INVALID_PARAMETER;
963     }
964 
965     /* Check for user parameters */
966     if (PreviousMode != KernelMode)
967     {
968         /* Protect the probes */
969         _SEH2_TRY
970         {
971             /* Check if the pointer is valid */
972             ProbeForWriteUlong(PreviousFlags);
973         }
974         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
975         {
976             /* It isn't -- fail */
977             _SEH2_YIELD(return _SEH2_GetExceptionCode());
978         }
979         _SEH2_END;
980     }
981 
982     /* Save the previous state, always masking in the continous flag */
983     PreviousState = Thread->PowerState | ES_CONTINUOUS;
984 
985     /* Check if we need to update the power state */
986     if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
987 
988     /* Protect the write back to user mode */
989     _SEH2_TRY
990     {
991         /* Return the previous flags */
992         *PreviousFlags = PreviousState;
993     }
994     _SEH2_EXCEPT(ExSystemExceptionFilter())
995     {
996         /* Something's wrong, fail */
997         _SEH2_YIELD(return _SEH2_GetExceptionCode());
998     }
999     _SEH2_END;
1000 
1001     /* All is good */
1002     return STATUS_SUCCESS;
1003 }
1004 
1005 NTSTATUS
1006 NTAPI
1007 NtSetSystemPowerState(IN POWER_ACTION SystemAction,
1008                       IN SYSTEM_POWER_STATE MinSystemState,
1009                       IN ULONG Flags)
1010 {
1011     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1012     POP_POWER_ACTION Action = {0};
1013     NTSTATUS Status;
1014     ULONG Dummy;
1015 
1016     /* Check for invalid parameter combinations */
1017     if ((MinSystemState >= PowerSystemMaximum) ||
1018         (MinSystemState <= PowerSystemUnspecified) ||
1019         (SystemAction > PowerActionWarmEject) ||
1020         (SystemAction < PowerActionReserved) ||
1021         (Flags & ~(POWER_ACTION_QUERY_ALLOWED  |
1022                    POWER_ACTION_UI_ALLOWED     |
1023                    POWER_ACTION_OVERRIDE_APPS  |
1024                    POWER_ACTION_LIGHTEST_FIRST |
1025                    POWER_ACTION_LOCK_CONSOLE   |
1026                    POWER_ACTION_DISABLE_WAKES  |
1027                    POWER_ACTION_CRITICAL)))
1028     {
1029         DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
1030         DPRINT1("                       SystemAction: 0x%x\n", SystemAction);
1031         DPRINT1("                       MinSystemState: 0x%x\n", MinSystemState);
1032         DPRINT1("                       Flags: 0x%x\n", Flags);
1033         return STATUS_INVALID_PARAMETER;
1034     }
1035 
1036     /* Check for user caller */
1037     if (PreviousMode != KernelMode)
1038     {
1039         /* Check for shutdown permission */
1040         if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
1041         {
1042             /* Not granted */
1043             DPRINT1("ERROR: Privilege not held for shutdown\n");
1044             return STATUS_PRIVILEGE_NOT_HELD;
1045         }
1046 
1047         /* Do it as a kernel-mode caller for consistency with system state */
1048         return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags);
1049     }
1050 
1051     /* Read policy settings (partial shutdown vs. full shutdown) */
1052     if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
1053 
1054     /* Disable lazy flushing of registry */
1055     DPRINT("Stopping lazy flush\n");
1056     CmSetLazyFlushState(FALSE);
1057 
1058     /* Setup the power action */
1059     Action.Action = SystemAction;
1060     Action.Flags = Flags;
1061 
1062     /* Notify callbacks */
1063     DPRINT("Notifying callbacks\n");
1064     ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
1065 
1066     /* Swap in any worker thread stacks */
1067     DPRINT("Swapping worker threads\n");
1068     ExSwapinWorkerThreads(FALSE);
1069 
1070     /* Make our action global */
1071     PopAction = Action;
1072 
1073     /* Start power loop */
1074     Status = STATUS_CANCELLED;
1075     while (TRUE)
1076     {
1077         /* Break out if there's nothing to do */
1078         if (Action.Action == PowerActionNone) break;
1079 
1080         /* Check for first-pass or restart */
1081         if (Status == STATUS_CANCELLED)
1082         {
1083             /* Check for shutdown action */
1084             if ((PopAction.Action == PowerActionShutdown) ||
1085                 (PopAction.Action == PowerActionShutdownReset) ||
1086                 (PopAction.Action == PowerActionShutdownOff))
1087             {
1088                 /* Set the action */
1089                 PopAction.Shutdown = TRUE;
1090             }
1091 
1092             /* Now we are good to go */
1093             Status = STATUS_SUCCESS;
1094         }
1095 
1096         /* Check if we're still in an invalid status */
1097         if (!NT_SUCCESS(Status)) break;
1098 
1099         /* Flush all volumes and the registry */
1100         DPRINT("Flushing volumes\n");
1101         PopFlushVolumes(PopAction.Shutdown);
1102 
1103 #ifndef NEWCC
1104         /* Flush dirty cache pages */
1105         /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */
1106         CcRosFlushDirtyPages(MAXULONG, &Dummy, TRUE, FALSE);
1107         DPRINT("Cache flushed %lu pages\n", Dummy);
1108 #else
1109         Dummy = 0;
1110 #endif
1111 
1112         /* Set IRP for drivers */
1113         PopAction.IrpMinor = IRP_MN_SET_POWER;
1114         if (PopAction.Shutdown)
1115         {
1116             DPRINT("Queueing shutdown thread\n");
1117             /* Check if we are running in the system context */
1118             if (PsGetCurrentProcess() != PsInitialSystemProcess)
1119             {
1120                 /* We're not, so use a worker thread for shutdown */
1121                 ExInitializeWorkItem(&PopShutdownWorkItem,
1122                                      &PopGracefulShutdown,
1123                                      NULL);
1124 
1125                 ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
1126 
1127                 /* Spend us -- when we wake up, the system is good to go down */
1128                 KeSuspendThread(KeGetCurrentThread());
1129                 Status = STATUS_SYSTEM_SHUTDOWN;
1130                 goto Exit;
1131 
1132             }
1133             else
1134             {
1135                 /* Do the shutdown inline */
1136                 PopGracefulShutdown(NULL);
1137             }
1138         }
1139 
1140         /* You should not have made it this far */
1141         // ASSERTMSG("System is still up and running?!\n", FALSE);
1142         DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action);
1143         break;
1144     }
1145 
1146 Exit:
1147     /* We're done, return */
1148     return Status;
1149 }
1150