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