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