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