xref: /reactos/ntoskrnl/po/power.c (revision 9393fc32)
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                                                 NULL,
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                                                 NULL,
425                                                 &NotificationEntry);
426         return NT_SUCCESS(Status);
427     }
428 
429     /* Initialize the power capabilities */
430     RtlZeroMemory(&PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES));
431 
432     /* Get the Command Line */
433     CommandLine = KeLoaderBlock->LoadOptions;
434 
435     /* Upcase it */
436     _strupr(CommandLine);
437 
438     /* Check for ACPI disable */
439     if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE;
440 
441     if (ForceAcpiDisable)
442     {
443         /* Set the ACPI State to False if it's been forced that way */
444         PopAcpiPresent = FALSE;
445     }
446     else
447     {
448         /* Otherwise check if the LoaderBlock has a ACPI Table  */
449         PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE;
450     }
451 
452     /* Enable shutdown by power button */
453     if (PopAcpiPresent)
454         PopCapabilities.SystemS5 = TRUE;
455 
456     /* Initialize volume support */
457     InitializeListHead(&PopVolumeDevices);
458     KeInitializeGuardedMutex(&PopVolumeLock);
459 
460     /* Initialize support for dope */
461     KeInitializeSpinLock(&PopDopeGlobalLock);
462 
463     /* Initialize support for shutdown waits and work-items */
464     PopInitShutdownList();
465 
466     return TRUE;
467 }
468 
469 VOID
470 NTAPI
471 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState)
472 {
473     DPRINT1("PerfIdle function: %p\n", PowerState);
474 }
475 
476 VOID
477 NTAPI
478 PopPerfIdleDpc(IN PKDPC Dpc,
479                IN PVOID DeferredContext,
480                IN PVOID SystemArgument1,
481                IN PVOID SystemArgument2)
482 {
483     /* Call the Perf Idle function */
484     PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState);
485 }
486 
487 VOID
488 FASTCALL
489 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
490 {
491     /* FIXME: Extremly naive implementation */
492     HalProcessorIdle();
493 }
494 
495 CODE_SEG("INIT")
496 VOID
497 NTAPI
498 PoInitializePrcb(IN PKPRCB Prcb)
499 {
500     /* Initialize the Power State */
501     RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState));
502     Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF;
503     Prcb->PowerState.CurrentThrottle = 100;
504     Prcb->PowerState.CurrentThrottleIndex = 0;
505     Prcb->PowerState.IdleFunction = PopIdle0;
506 
507     /* Initialize the Perf DPC and Timer */
508     KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb);
509     KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number);
510     KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer);
511 }
512 
513 /* PUBLIC FUNCTIONS **********************************************************/
514 
515 /*
516  * @unimplemented
517  */
518 NTSTATUS
519 NTAPI
520 PoCancelDeviceNotify(IN PVOID NotifyBlock)
521 {
522     UNIMPLEMENTED;
523     return STATUS_NOT_IMPLEMENTED;
524 }
525 
526 /*
527  * @unimplemented
528  */
529 NTSTATUS
530 NTAPI
531 PoRegisterDeviceNotify(OUT PVOID Unknown0,
532                        IN ULONG Unknown1,
533                        IN ULONG Unknown2,
534                        IN ULONG Unknown3,
535                        IN PVOID Unknown4,
536                        IN PVOID Unknown5)
537 {
538     UNIMPLEMENTED;
539     return STATUS_NOT_IMPLEMENTED;
540 }
541 
542 /*
543  * @unimplemented
544  */
545 VOID
546 NTAPI
547 PoShutdownBugCheck(IN BOOLEAN LogError,
548                    IN ULONG BugCheckCode,
549                    IN ULONG_PTR BugCheckParameter1,
550                    IN ULONG_PTR BugCheckParameter2,
551                    IN ULONG_PTR BugCheckParameter3,
552                    IN ULONG_PTR BugCheckParameter4)
553 {
554     DPRINT1("PoShutdownBugCheck called\n");
555 
556     /* FIXME: Log error if requested */
557     /* FIXME: Initiate a shutdown */
558 
559     /* Bugcheck the system */
560     KeBugCheckEx(BugCheckCode,
561                  BugCheckParameter1,
562                  BugCheckParameter2,
563                  BugCheckParameter3,
564                  BugCheckParameter4);
565 }
566 
567 /*
568  * @unimplemented
569  */
570 VOID
571 NTAPI
572 PoSetHiberRange(IN PVOID HiberContext,
573                 IN ULONG Flags,
574                 IN OUT PVOID StartPage,
575                 IN ULONG Length,
576                 IN ULONG PageTag)
577 {
578     UNIMPLEMENTED;
579     return;
580 }
581 
582 /*
583  * @implemented
584  */
585 _IRQL_requires_max_(DISPATCH_LEVEL)
586 NTSTATUS
587 NTAPI
588 PoCallDriver(
589     _In_ PDEVICE_OBJECT DeviceObject,
590     _Inout_ __drv_aliasesMem PIRP Irp)
591 {
592     PIO_STACK_LOCATION NextStack;
593 
594     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
595 
596     ASSERT(DeviceObject);
597     ASSERT(Irp);
598 
599     NextStack = IoGetNextIrpStackLocation(Irp);
600     ASSERT(NextStack->MajorFunction == IRP_MJ_POWER);
601 
602     /* Set DeviceObject for PopPresentIrp */
603     NextStack->DeviceObject = DeviceObject;
604 
605     /* Only QUERY_POWER and SET_POWER use special handling */
606     if (NextStack->MinorFunction != IRP_MN_SET_POWER &&
607         NextStack->MinorFunction != IRP_MN_QUERY_POWER)
608     {
609         return IoCallDriver(DeviceObject, Irp);
610     }
611 
612     /* Call the next driver, either directly or at PASSIVE_LEVEL */
613     return PopPresentIrp(NextStack, Irp);
614 }
615 
616 /*
617  * @unimplemented
618  */
619 PULONG
620 NTAPI
621 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject,
622                                  IN ULONG ConservationIdleTime,
623                                  IN ULONG PerformanceIdleTime,
624                                  IN DEVICE_POWER_STATE State)
625 {
626     UNIMPLEMENTED;
627     return NULL;
628 }
629 
630 /*
631  * @unimplemented
632  */
633 PVOID
634 NTAPI
635 PoRegisterSystemState(IN PVOID StateHandle,
636                       IN EXECUTION_STATE Flags)
637 {
638     UNIMPLEMENTED;
639     return NULL;
640 }
641 
642 /*
643  * @implemented
644  */
645 NTSTATUS
646 NTAPI
647 PoRequestPowerIrp(
648     _In_ PDEVICE_OBJECT DeviceObject,
649     _In_ UCHAR MinorFunction,
650     _In_ POWER_STATE PowerState,
651     _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction,
652     _In_opt_ __drv_aliasesMem PVOID Context,
653     _Outptr_opt_ PIRP *pIrp)
654 {
655     PDEVICE_OBJECT TopDeviceObject;
656     PIO_STACK_LOCATION Stack;
657     PIRP Irp;
658 
659     if (MinorFunction != IRP_MN_QUERY_POWER
660         && MinorFunction != IRP_MN_SET_POWER
661         && MinorFunction != IRP_MN_WAIT_WAKE)
662         return STATUS_INVALID_PARAMETER_2;
663 
664     /* Always call the top of the device stack */
665     TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
666 
667     Irp = IoAllocateIrp(TopDeviceObject->StackSize + 2, FALSE);
668     if (!Irp)
669     {
670         ObDereferenceObject(TopDeviceObject);
671         return STATUS_INSUFFICIENT_RESOURCES;
672     }
673 
674     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
675     Irp->IoStatus.Information = 0;
676 
677     IoSetNextIrpStackLocation(Irp);
678 
679     Stack = IoGetNextIrpStackLocation(Irp);
680     Stack->Parameters.Others.Argument1 = DeviceObject;
681     Stack->Parameters.Others.Argument2 = (PVOID)(ULONG_PTR)MinorFunction;
682     Stack->Parameters.Others.Argument3 = (PVOID)(ULONG_PTR)PowerState.DeviceState;
683     Stack->Parameters.Others.Argument4 = Context;
684     Stack->DeviceObject = TopDeviceObject;
685     IoSetNextIrpStackLocation(Irp);
686 
687     Stack = IoGetNextIrpStackLocation(Irp);
688     Stack->MajorFunction = IRP_MJ_POWER;
689     Stack->MinorFunction = MinorFunction;
690     if (MinorFunction == IRP_MN_WAIT_WAKE)
691     {
692         Stack->Parameters.WaitWake.PowerState = PowerState.SystemState;
693     }
694     else
695     {
696         Stack->Parameters.Power.Type = DevicePowerState;
697         Stack->Parameters.Power.State = PowerState;
698     }
699 
700     if (pIrp != NULL)
701         *pIrp = Irp;
702 
703     IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, CompletionFunction, TRUE, TRUE, TRUE);
704     PoCallDriver(TopDeviceObject, Irp);
705 
706     /* Always return STATUS_PENDING. The completion routine
707      * will call CompletionFunction and complete the Irp.
708      */
709     return STATUS_PENDING;
710 }
711 
712 /*
713  * @unimplemented
714  */
715 POWER_STATE
716 NTAPI
717 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject,
718                 IN POWER_STATE_TYPE Type,
719                 IN POWER_STATE State)
720 {
721     POWER_STATE ps;
722 
723     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
724 
725     ps.SystemState = PowerSystemWorking;  // Fully on
726     ps.DeviceState = PowerDeviceD0;       // Fully on
727 
728     return ps;
729 }
730 
731 /*
732  * @unimplemented
733  */
734 VOID
735 NTAPI
736 PoSetSystemState(IN EXECUTION_STATE Flags)
737 {
738     UNIMPLEMENTED;
739 }
740 
741 /*
742  * @unimplemented
743  */
744 VOID
745 NTAPI
746 PoStartNextPowerIrp(IN PIRP Irp)
747 {
748     UNIMPLEMENTED_ONCE;
749 }
750 
751 /*
752  * @unimplemented
753  */
754 VOID
755 NTAPI
756 PoUnregisterSystemState(IN PVOID StateHandle)
757 {
758     UNIMPLEMENTED;
759 }
760 
761 /*
762  * @unimplemented
763  */
764 NTSTATUS
765 NTAPI
766 NtInitiatePowerAction(IN POWER_ACTION SystemAction,
767                       IN SYSTEM_POWER_STATE MinSystemState,
768                       IN ULONG Flags,
769                       IN BOOLEAN Asynchronous)
770 {
771     UNIMPLEMENTED;
772     return STATUS_NOT_IMPLEMENTED;
773 }
774 
775 /*
776  * @unimplemented
777  */
778 NTSTATUS
779 NTAPI
780 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel,
781                    IN PVOID InputBuffer  OPTIONAL,
782                    IN ULONG InputBufferLength,
783                    OUT PVOID OutputBuffer  OPTIONAL,
784                    IN ULONG OutputBufferLength)
785 {
786     NTSTATUS Status;
787     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
788 
789     PAGED_CODE();
790 
791     DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%p, "
792            "InputBufferLength 0x%x, OutputBuffer 0x%p, OutputBufferLength 0x%x)\n",
793            PowerInformationLevel,
794            InputBuffer, InputBufferLength,
795            OutputBuffer, OutputBufferLength);
796 
797     if (PreviousMode != KernelMode)
798     {
799         _SEH2_TRY
800         {
801             ProbeForRead(InputBuffer, InputBufferLength, 1);
802             ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));
803         }
804         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
805         {
806             _SEH2_YIELD(return _SEH2_GetExceptionCode());
807         }
808         _SEH2_END;
809     }
810 
811     switch (PowerInformationLevel)
812     {
813         case SystemBatteryState:
814         {
815             PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer;
816 
817             if (InputBuffer != NULL)
818                 return STATUS_INVALID_PARAMETER;
819             if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE))
820                 return STATUS_BUFFER_TOO_SMALL;
821 
822             _SEH2_TRY
823             {
824                 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
825                 RtlZeroMemory(BatteryState, sizeof(SYSTEM_BATTERY_STATE));
826                 BatteryState->EstimatedTime = MAXULONG;
827 //                BatteryState->AcOnLine = TRUE;
828 
829                 Status = STATUS_SUCCESS;
830             }
831             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
832             {
833                 Status = _SEH2_GetExceptionCode();
834             }
835             _SEH2_END;
836 
837             break;
838         }
839 
840         case SystemPowerCapabilities:
841         {
842             PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer;
843 
844             if (InputBuffer != NULL)
845                 return STATUS_INVALID_PARAMETER;
846             if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES))
847                 return STATUS_BUFFER_TOO_SMALL;
848 
849             _SEH2_TRY
850             {
851                 RtlCopyMemory(PowerCapabilities,
852                               &PopCapabilities,
853                               sizeof(SYSTEM_POWER_CAPABILITIES));
854 
855                 Status = STATUS_SUCCESS;
856             }
857             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
858             {
859                 Status = _SEH2_GetExceptionCode();
860             }
861             _SEH2_END;
862 
863             break;
864         }
865 
866         case ProcessorInformation:
867         {
868             PPROCESSOR_POWER_INFORMATION PowerInformation = (PPROCESSOR_POWER_INFORMATION)OutputBuffer;
869 
870             if (InputBuffer != NULL)
871                 return STATUS_INVALID_PARAMETER;
872             if (OutputBufferLength < sizeof(PROCESSOR_POWER_INFORMATION))
873                 return STATUS_BUFFER_TOO_SMALL;
874 
875             /* FIXME: return structures for all processors */
876 
877             _SEH2_TRY
878             {
879                 /* FIXME: some values are hardcoded */
880                 PowerInformation->Number = 0;
881                 PowerInformation->MaxMhz = 1000;
882                 PowerInformation->CurrentMhz = KeGetCurrentPrcb()->MHz;
883                 PowerInformation->MhzLimit = 1000;
884                 PowerInformation->MaxIdleState = 0;
885                 PowerInformation->CurrentIdleState = 0;
886 
887                 Status = STATUS_SUCCESS;
888             }
889             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
890             {
891                 Status = _SEH2_GetExceptionCode();
892             }
893             _SEH2_END;
894 
895             break;
896         }
897 
898         default:
899             Status = STATUS_NOT_IMPLEMENTED;
900             DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
901                     PowerInformationLevel);
902             break;
903     }
904 
905     return Status;
906 }
907 
908 NTSTATUS
909 NTAPI
910 NtGetDevicePowerState(IN HANDLE Device,
911                       IN PDEVICE_POWER_STATE PowerState)
912 {
913     UNIMPLEMENTED;
914     return STATUS_NOT_IMPLEMENTED;
915 }
916 
917 BOOLEAN
918 NTAPI
919 NtIsSystemResumeAutomatic(VOID)
920 {
921     UNIMPLEMENTED;
922     return FALSE;
923 }
924 
925 NTSTATUS
926 NTAPI
927 NtRequestWakeupLatency(IN LATENCY_TIME Latency)
928 {
929     UNIMPLEMENTED;
930     return STATUS_NOT_IMPLEMENTED;
931 }
932 
933 NTSTATUS
934 NTAPI
935 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
936                           OUT EXECUTION_STATE *PreviousFlags)
937 {
938     PKTHREAD Thread = KeGetCurrentThread();
939     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
940     EXECUTION_STATE PreviousState;
941     PAGED_CODE();
942 
943     /* Validate flags */
944     if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
945     {
946         /* Fail the request */
947         return STATUS_INVALID_PARAMETER;
948     }
949 
950     /* Check for user parameters */
951     if (PreviousMode != KernelMode)
952     {
953         /* Protect the probes */
954         _SEH2_TRY
955         {
956             /* Check if the pointer is valid */
957             ProbeForWriteUlong(PreviousFlags);
958         }
959         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
960         {
961             /* It isn't -- fail */
962             _SEH2_YIELD(return _SEH2_GetExceptionCode());
963         }
964         _SEH2_END;
965     }
966 
967     /* Save the previous state, always masking in the continous flag */
968     PreviousState = Thread->PowerState | ES_CONTINUOUS;
969 
970     /* Check if we need to update the power state */
971     if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
972 
973     /* Protect the write back to user mode */
974     _SEH2_TRY
975     {
976         /* Return the previous flags */
977         *PreviousFlags = PreviousState;
978     }
979     _SEH2_EXCEPT(ExSystemExceptionFilter())
980     {
981         /* Something's wrong, fail */
982         _SEH2_YIELD(return _SEH2_GetExceptionCode());
983     }
984     _SEH2_END;
985 
986     /* All is good */
987     return STATUS_SUCCESS;
988 }
989 
990 NTSTATUS
991 NTAPI
992 NtSetSystemPowerState(IN POWER_ACTION SystemAction,
993                       IN SYSTEM_POWER_STATE MinSystemState,
994                       IN ULONG Flags)
995 {
996     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
997     POP_POWER_ACTION Action = {0};
998     NTSTATUS Status;
999     ULONG Dummy;
1000 
1001     /* Check for invalid parameter combinations */
1002     if ((MinSystemState >= PowerSystemMaximum) ||
1003         (MinSystemState <= PowerSystemUnspecified) ||
1004         (SystemAction > PowerActionWarmEject) ||
1005         (SystemAction < PowerActionReserved) ||
1006         (Flags & ~(POWER_ACTION_QUERY_ALLOWED  |
1007                    POWER_ACTION_UI_ALLOWED     |
1008                    POWER_ACTION_OVERRIDE_APPS  |
1009                    POWER_ACTION_LIGHTEST_FIRST |
1010                    POWER_ACTION_LOCK_CONSOLE   |
1011                    POWER_ACTION_DISABLE_WAKES  |
1012                    POWER_ACTION_CRITICAL)))
1013     {
1014         DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
1015         DPRINT1("                       SystemAction: 0x%x\n", SystemAction);
1016         DPRINT1("                       MinSystemState: 0x%x\n", MinSystemState);
1017         DPRINT1("                       Flags: 0x%x\n", Flags);
1018         return STATUS_INVALID_PARAMETER;
1019     }
1020 
1021     /* Check for user caller */
1022     if (PreviousMode != KernelMode)
1023     {
1024         /* Check for shutdown permission */
1025         if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
1026         {
1027             /* Not granted */
1028             DPRINT1("ERROR: Privilege not held for shutdown\n");
1029             return STATUS_PRIVILEGE_NOT_HELD;
1030         }
1031 
1032         /* Do it as a kernel-mode caller for consistency with system state */
1033         return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags);
1034     }
1035 
1036     /* Read policy settings (partial shutdown vs. full shutdown) */
1037     if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
1038 
1039     /* Disable lazy flushing of registry */
1040     DPRINT("Stopping lazy flush\n");
1041     CmSetLazyFlushState(FALSE);
1042 
1043     /* Setup the power action */
1044     Action.Action = SystemAction;
1045     Action.Flags = Flags;
1046 
1047     /* Notify callbacks */
1048     DPRINT("Notifying callbacks\n");
1049     ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
1050 
1051     /* Swap in any worker thread stacks */
1052     DPRINT("Swapping worker threads\n");
1053     ExSwapinWorkerThreads(FALSE);
1054 
1055     /* Make our action global */
1056     PopAction = Action;
1057 
1058     /* Start power loop */
1059     Status = STATUS_CANCELLED;
1060     while (TRUE)
1061     {
1062         /* Break out if there's nothing to do */
1063         if (Action.Action == PowerActionNone) break;
1064 
1065         /* Check for first-pass or restart */
1066         if (Status == STATUS_CANCELLED)
1067         {
1068             /* Check for shutdown action */
1069             if ((PopAction.Action == PowerActionShutdown) ||
1070                 (PopAction.Action == PowerActionShutdownReset) ||
1071                 (PopAction.Action == PowerActionShutdownOff))
1072             {
1073                 /* Set the action */
1074                 PopAction.Shutdown = TRUE;
1075             }
1076 
1077             /* Now we are good to go */
1078             Status = STATUS_SUCCESS;
1079         }
1080 
1081         /* Check if we're still in an invalid status */
1082         if (!NT_SUCCESS(Status)) break;
1083 
1084         /* Flush all volumes and the registry */
1085         DPRINT("Flushing volumes\n");
1086         PopFlushVolumes(PopAction.Shutdown);
1087 
1088 #ifndef NEWCC
1089         /* Flush dirty cache pages */
1090         /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */
1091         CcRosFlushDirtyPages(MAXULONG, &Dummy, TRUE, FALSE);
1092         DPRINT("Cache flushed %lu pages\n", Dummy);
1093 #else
1094         Dummy = 0;
1095 #endif
1096 
1097         /* Set IRP for drivers */
1098         PopAction.IrpMinor = IRP_MN_SET_POWER;
1099         if (PopAction.Shutdown)
1100         {
1101             DPRINT("Queueing shutdown thread\n");
1102             /* Check if we are running in the system context */
1103             if (PsGetCurrentProcess() != PsInitialSystemProcess)
1104             {
1105                 /* We're not, so use a worker thread for shutdown */
1106                 ExInitializeWorkItem(&PopShutdownWorkItem,
1107                                      &PopGracefulShutdown,
1108                                      NULL);
1109 
1110                 ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
1111 
1112                 /* Spend us -- when we wake up, the system is good to go down */
1113                 KeSuspendThread(KeGetCurrentThread());
1114                 Status = STATUS_SYSTEM_SHUTDOWN;
1115                 goto Exit;
1116 
1117             }
1118             else
1119             {
1120                 /* Do the shutdown inline */
1121                 PopGracefulShutdown(NULL);
1122             }
1123         }
1124 
1125         /* You should not have made it this far */
1126         // ASSERTMSG("System is still up and running?!\n", FALSE);
1127         DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action);
1128         break;
1129     }
1130 
1131 Exit:
1132     /* We're done, return */
1133     return Status;
1134 }
1135