xref: /reactos/ntoskrnl/po/power.c (revision 3476cdae)
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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             /* FIXME: return structures for all processors */
867 
868             _SEH2_TRY
869             {
870                 /* FIXME: some values are hardcoded */
871                 PowerInformation->Number = 0;
872                 PowerInformation->MaxMhz = 1000;
873                 PowerInformation->CurrentMhz = KeGetCurrentPrcb()->MHz;
874                 PowerInformation->MhzLimit = 1000;
875                 PowerInformation->MaxIdleState = 0;
876                 PowerInformation->CurrentIdleState = 0;
877 
878                 Status = STATUS_SUCCESS;
879             }
880             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
881             {
882                 Status = _SEH2_GetExceptionCode();
883             }
884             _SEH2_END;
885 
886             break;
887         }
888 
889         default:
890             Status = STATUS_NOT_IMPLEMENTED;
891             DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
892                     PowerInformationLevel);
893             break;
894     }
895 
896     return Status;
897 }
898 
899 NTSTATUS
900 NTAPI
901 NtGetDevicePowerState(IN HANDLE Device,
902                       IN PDEVICE_POWER_STATE PowerState)
903 {
904     UNIMPLEMENTED;
905     return STATUS_NOT_IMPLEMENTED;
906 }
907 
908 BOOLEAN
909 NTAPI
910 NtIsSystemResumeAutomatic(VOID)
911 {
912     UNIMPLEMENTED;
913     return FALSE;
914 }
915 
916 NTSTATUS
917 NTAPI
918 NtRequestWakeupLatency(IN LATENCY_TIME Latency)
919 {
920     UNIMPLEMENTED;
921     return STATUS_NOT_IMPLEMENTED;
922 }
923 
924 NTSTATUS
925 NTAPI
926 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
927                           OUT EXECUTION_STATE *PreviousFlags)
928 {
929     PKTHREAD Thread = KeGetCurrentThread();
930     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
931     EXECUTION_STATE PreviousState;
932     PAGED_CODE();
933 
934     /* Validate flags */
935     if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
936     {
937         /* Fail the request */
938         return STATUS_INVALID_PARAMETER;
939     }
940 
941     /* Check for user parameters */
942     if (PreviousMode != KernelMode)
943     {
944         /* Protect the probes */
945         _SEH2_TRY
946         {
947             /* Check if the pointer is valid */
948             ProbeForWriteUlong(PreviousFlags);
949         }
950         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
951         {
952             /* It isn't -- fail */
953             _SEH2_YIELD(return _SEH2_GetExceptionCode());
954         }
955         _SEH2_END;
956     }
957 
958     /* Save the previous state, always masking in the continous flag */
959     PreviousState = Thread->PowerState | ES_CONTINUOUS;
960 
961     /* Check if we need to update the power state */
962     if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
963 
964     /* Protect the write back to user mode */
965     _SEH2_TRY
966     {
967         /* Return the previous flags */
968         *PreviousFlags = PreviousState;
969     }
970     _SEH2_EXCEPT(ExSystemExceptionFilter())
971     {
972         /* Something's wrong, fail */
973         _SEH2_YIELD(return _SEH2_GetExceptionCode());
974     }
975     _SEH2_END;
976 
977     /* All is good */
978     return STATUS_SUCCESS;
979 }
980 
981 NTSTATUS
982 NTAPI
983 NtSetSystemPowerState(IN POWER_ACTION SystemAction,
984                       IN SYSTEM_POWER_STATE MinSystemState,
985                       IN ULONG Flags)
986 {
987     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
988     POP_POWER_ACTION Action = {0};
989     NTSTATUS Status;
990     ULONG Dummy;
991 
992     /* Check for invalid parameter combinations */
993     if ((MinSystemState >= PowerSystemMaximum) ||
994         (MinSystemState <= PowerSystemUnspecified) ||
995         (SystemAction > PowerActionWarmEject) ||
996         (SystemAction < PowerActionReserved) ||
997         (Flags & ~(POWER_ACTION_QUERY_ALLOWED  |
998                    POWER_ACTION_UI_ALLOWED     |
999                    POWER_ACTION_OVERRIDE_APPS  |
1000                    POWER_ACTION_LIGHTEST_FIRST |
1001                    POWER_ACTION_LOCK_CONSOLE   |
1002                    POWER_ACTION_DISABLE_WAKES  |
1003                    POWER_ACTION_CRITICAL)))
1004     {
1005         DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
1006         DPRINT1("                       SystemAction: 0x%x\n", SystemAction);
1007         DPRINT1("                       MinSystemState: 0x%x\n", MinSystemState);
1008         DPRINT1("                       Flags: 0x%x\n", Flags);
1009         return STATUS_INVALID_PARAMETER;
1010     }
1011 
1012     /* Check for user caller */
1013     if (PreviousMode != KernelMode)
1014     {
1015         /* Check for shutdown permission */
1016         if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
1017         {
1018             /* Not granted */
1019             DPRINT1("ERROR: Privilege not held for shutdown\n");
1020             return STATUS_PRIVILEGE_NOT_HELD;
1021         }
1022 
1023         /* Do it as a kernel-mode caller for consistency with system state */
1024         return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags);
1025     }
1026 
1027     /* Read policy settings (partial shutdown vs. full shutdown) */
1028     if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
1029 
1030     /* Disable lazy flushing of registry */
1031     DPRINT("Stopping lazy flush\n");
1032     CmSetLazyFlushState(FALSE);
1033 
1034     /* Setup the power action */
1035     Action.Action = SystemAction;
1036     Action.Flags = Flags;
1037 
1038     /* Notify callbacks */
1039     DPRINT("Notifying callbacks\n");
1040     ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
1041 
1042     /* Swap in any worker thread stacks */
1043     DPRINT("Swapping worker threads\n");
1044     ExSwapinWorkerThreads(FALSE);
1045 
1046     /* Make our action global */
1047     PopAction = Action;
1048 
1049     /* Start power loop */
1050     Status = STATUS_CANCELLED;
1051     while (TRUE)
1052     {
1053         /* Break out if there's nothing to do */
1054         if (Action.Action == PowerActionNone) break;
1055 
1056         /* Check for first-pass or restart */
1057         if (Status == STATUS_CANCELLED)
1058         {
1059             /* Check for shutdown action */
1060             if ((PopAction.Action == PowerActionShutdown) ||
1061                 (PopAction.Action == PowerActionShutdownReset) ||
1062                 (PopAction.Action == PowerActionShutdownOff))
1063             {
1064                 /* Set the action */
1065                 PopAction.Shutdown = TRUE;
1066             }
1067 
1068             /* Now we are good to go */
1069             Status = STATUS_SUCCESS;
1070         }
1071 
1072         /* Check if we're still in an invalid status */
1073         if (!NT_SUCCESS(Status)) break;
1074 
1075         /* Flush all volumes and the registry */
1076         DPRINT("Flushing volumes\n");
1077         PopFlushVolumes(PopAction.Shutdown);
1078 
1079 #ifndef NEWCC
1080         /* Flush dirty cache pages */
1081         /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */
1082         CcRosFlushDirtyPages(MAXULONG, &Dummy, TRUE, FALSE);
1083         DPRINT("Cache flushed %lu pages\n", Dummy);
1084 #else
1085         Dummy = 0;
1086 #endif
1087 
1088         /* Set IRP for drivers */
1089         PopAction.IrpMinor = IRP_MN_SET_POWER;
1090         if (PopAction.Shutdown)
1091         {
1092             DPRINT("Queueing shutdown thread\n");
1093             /* Check if we are running in the system context */
1094             if (PsGetCurrentProcess() != PsInitialSystemProcess)
1095             {
1096                 /* We're not, so use a worker thread for shutdown */
1097                 ExInitializeWorkItem(&PopShutdownWorkItem,
1098                                      &PopGracefulShutdown,
1099                                      NULL);
1100 
1101                 ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
1102 
1103                 /* Spend us -- when we wake up, the system is good to go down */
1104                 KeSuspendThread(KeGetCurrentThread());
1105                 Status = STATUS_SYSTEM_SHUTDOWN;
1106                 goto Exit;
1107 
1108             }
1109             else
1110             {
1111                 /* Do the shutdown inline */
1112                 PopGracefulShutdown(NULL);
1113             }
1114         }
1115 
1116         /* You should not have made it this far */
1117         // ASSERTMSG("System is still up and running?!\n", FALSE);
1118         DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action);
1119         break;
1120     }
1121 
1122 Exit:
1123     /* We're done, return */
1124     return Status;
1125 }
1126