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