xref: /reactos/ntoskrnl/po/poshtdwn.c (revision 434ac241)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/po/poshtdwn.c
5  * PURPOSE:         Power Manager Shutdown Code
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #ifdef NEWCC
13 #include <cache/newcc.h>
14 #endif
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #include "inbv/logo.h"
20 
21 /* GLOBALS *******************************************************************/
22 
23 ULONG PopShutdownPowerOffPolicy;
24 KEVENT PopShutdownEvent;
25 PPOP_SHUTDOWN_WAIT_ENTRY PopShutdownThreadList;
26 LIST_ENTRY PopShutdownQueue;
27 KGUARDED_MUTEX PopShutdownListMutex;
28 BOOLEAN PopShutdownListAvailable;
29 
30 
31 /* PRIVATE FUNCTIONS *********************************************************/
32 
33 VOID
34 NTAPI
PopInitShutdownList(VOID)35 PopInitShutdownList(VOID)
36 {
37     PAGED_CODE();
38 
39     /* Initialize the global shutdown event */
40     KeInitializeEvent(&PopShutdownEvent, NotificationEvent, FALSE);
41 
42     /* Initialize the shutdown lists */
43     PopShutdownThreadList = NULL;
44     InitializeListHead(&PopShutdownQueue);
45 
46     /* Initialize the shutdown list lock */
47     KeInitializeGuardedMutex(&PopShutdownListMutex);
48 
49     /* The list is available now */
50     PopShutdownListAvailable = TRUE;
51 }
52 
53 NTSTATUS
54 NTAPI
PoRequestShutdownWait(_In_ PETHREAD Thread)55 PoRequestShutdownWait(
56     _In_ PETHREAD Thread)
57 {
58     PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry;
59     NTSTATUS Status;
60     PAGED_CODE();
61 
62     /* Allocate a new shutdown wait entry */
63     ShutDownWaitEntry = ExAllocatePoolWithTag(PagedPool, sizeof(*ShutDownWaitEntry), 'LSoP');
64     if (ShutDownWaitEntry == NULL)
65     {
66         return STATUS_NO_MEMORY;
67     }
68 
69     /* Reference the thread and save it in the wait entry */
70     ObReferenceObject(Thread);
71     ShutDownWaitEntry->Thread = Thread;
72 
73     /* Acquire the shutdown list lock */
74     KeAcquireGuardedMutex(&PopShutdownListMutex);
75 
76     /* Check if the list is still available */
77     if (PopShutdownListAvailable)
78     {
79         /* Insert the item in the list */
80         ShutDownWaitEntry->NextEntry = PopShutdownThreadList;
81         PopShutdownThreadList = ShutDownWaitEntry;
82 
83         /* We are successful */
84         Status = STATUS_SUCCESS;
85     }
86     else
87     {
88         /* We cannot proceed, cleanup and return failure */
89         ObDereferenceObject(Thread);
90         ExFreePoolWithTag(ShutDownWaitEntry, 'LSoP');
91         Status = STATUS_UNSUCCESSFUL;
92     }
93 
94     /* Release the list lock */
95     KeReleaseGuardedMutex(&PopShutdownListMutex);
96 
97     /* Return the status */
98     return Status;
99 }
100 
101 VOID
102 NTAPI
PopProcessShutDownLists(VOID)103 PopProcessShutDownLists(VOID)
104 {
105     PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry;
106     PWORK_QUEUE_ITEM WorkItem;
107     PLIST_ENTRY ListEntry;
108 
109     /* First signal the shutdown event */
110     KeSetEvent(&PopShutdownEvent, IO_NO_INCREMENT, FALSE);
111 
112     /* Acquire the shutdown list lock */
113     KeAcquireGuardedMutex(&PopShutdownListMutex);
114 
115     /* Block any further attempts to register a shutdown event */
116     PopShutdownListAvailable = FALSE;
117 
118     /* Release the list lock, since we are exclusively using the lists now */
119     KeReleaseGuardedMutex(&PopShutdownListMutex);
120 
121     /* Process the shutdown queue */
122     while (!IsListEmpty(&PopShutdownQueue))
123     {
124         /* Get the head entry */
125         ListEntry = RemoveHeadList(&PopShutdownQueue);
126         WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List);
127 
128         /* Call the shutdown worker routine */
129         WorkItem->WorkerRoutine(WorkItem->Parameter);
130     }
131 
132     /* Now process the shutdown thread list */
133     while (PopShutdownThreadList != NULL)
134     {
135         /* Get the top entry and remove it from the list */
136         ShutDownWaitEntry = PopShutdownThreadList;
137         PopShutdownThreadList = PopShutdownThreadList->NextEntry;
138 
139         /* Wait for the thread to finish and dereference it */
140         KeWaitForSingleObject(ShutDownWaitEntry->Thread, 0, 0, 0, 0);
141         ObDereferenceObject(ShutDownWaitEntry->Thread);
142 
143         /* Finally free the entry */
144         ExFreePoolWithTag(ShutDownWaitEntry, 'LSoP');
145     }
146 }
147 
148 VOID
149 NTAPI
PopShutdownHandler(VOID)150 PopShutdownHandler(VOID)
151 {
152     /* Stop all interrupts */
153     KeRaiseIrqlToDpcLevel();
154     _disable();
155 
156     /* Do we have boot video */
157     if (InbvIsBootDriverInstalled())
158     {
159         /* Yes we do, cleanup for shutdown screen */
160         if (!InbvCheckDisplayOwnership()) InbvAcquireDisplayOwnership();
161         InbvResetDisplay();
162         // InbvEnableDisplayString(TRUE);
163         DisplayShutdownBitmap();
164     }
165     else
166     {
167         /* Do it in text-mode */
168         DisplayShutdownText();
169     }
170 
171     /* Hang the system */
172     for (;;) HalHaltSystem();
173 }
174 
175 VOID
176 NTAPI
PopShutdownSystem(IN POWER_ACTION SystemAction)177 PopShutdownSystem(IN POWER_ACTION SystemAction)
178 {
179     /* Note should notify caller of NtPowerInformation(PowerShutdownNotification) */
180 
181     /* Unload symbols */
182     DPRINT("It's the final countdown...%lx\n", SystemAction);
183     DbgUnLoadImageSymbols(NULL, (PVOID)-1, 0);
184 
185     /* Run the thread on the boot processor */
186     KeSetSystemAffinityThread(1);
187 
188     /* Now check what the caller wants */
189     switch (SystemAction)
190     {
191         /* Reset */
192         case PowerActionShutdownReset:
193 
194             /* Try platform driver first, then legacy */
195             //PopInvokeSystemStateHandler(PowerStateShutdownReset, NULL);
196             PopSetSystemPowerState(PowerSystemShutdown, SystemAction);
197             HalReturnToFirmware(HalRebootRoutine);
198             break;
199 
200         case PowerActionShutdown:
201 
202             /* Check for group policy that says to use "it is now safe" screen */
203             if (PopShutdownPowerOffPolicy)
204             {
205                 /* FIXFIX: Switch to legacy shutdown handler */
206                 //PopPowerStateHandlers[PowerStateShutdownOff].Handler = PopShutdownHandler;
207             }
208 
209         case PowerActionShutdownOff:
210 
211             /* Call shutdown handler */
212             //PopInvokeSystemStateHandler(PowerStateShutdownOff, NULL);
213 
214             /* ReactOS Hack */
215             PopSetSystemPowerState(PowerSystemShutdown, SystemAction);
216             PopShutdownHandler();
217 
218             /* If that didn't work, call the HAL */
219             HalReturnToFirmware(HalPowerDownRoutine);
220             break;
221 
222         default:
223             break;
224     }
225 
226     /* Anything else should not happen */
227     KeBugCheckEx(INTERNAL_POWER_ERROR, 5, 0, 0, 0);
228 }
229 
230 VOID
231 NTAPI
PopGracefulShutdown(IN PVOID Context)232 PopGracefulShutdown(IN PVOID Context)
233 {
234     PEPROCESS Process = NULL;
235 
236     /* Process the registered waits and work items */
237     PopProcessShutDownLists();
238 
239     /* Loop every process */
240     Process = PsGetNextProcess(Process);
241     while (Process)
242     {
243         /* Make sure this isn't the idle or initial process */
244         if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
245         {
246             /* Print it */
247             DPRINT1("%15s is still RUNNING (%p)\n", Process->ImageFileName, Process->UniqueProcessId);
248         }
249 
250         /* Get the next process */
251         Process = PsGetNextProcess(Process);
252     }
253 
254     /* First, the HAL handles any "end of boot" special functionality */
255     DPRINT("HAL shutting down\n");
256     HalEndOfBoot();
257 
258     /* Shut down the Shim cache if enabled */
259     ApphelpCacheShutdown();
260 
261     /* In this step, the I/O manager does first-chance shutdown notification */
262     DPRINT("I/O manager shutting down in phase 0\n");
263     IoShutdownSystem(0);
264 
265     /* In this step, all workers are killed and hives are flushed */
266     DPRINT("Configuration Manager shutting down\n");
267     CmShutdownSystem();
268 
269     /* Shut down the Executive */
270     DPRINT("Executive shutting down\n");
271     ExShutdownSystem();
272 
273     /* Note that modified pages should be written here (MiShutdownSystem) */
274     MmShutdownSystem(0);
275 
276     /* Flush all user files before we start shutting down IO */
277     /* This is where modified pages are written back by the IO manager */
278     CcShutdownSystem();
279 
280     /* In this step, the I/O manager does last-chance shutdown notification */
281     DPRINT("I/O manager shutting down in phase 1\n");
282     IoShutdownSystem(1);
283     CcWaitForCurrentLazyWriterActivity();
284 
285     /* FIXME: Calling Mm shutdown phase 1 here to get page file dereference
286      * but it shouldn't be called here. Only phase 2 should be called.
287      */
288     MmShutdownSystem(1);
289 
290     /* Note that here, we should broadcast the power IRP to devices */
291 
292     /* In this step, the HAL disables any wake timers */
293     DPRINT("Disabling wake timers\n");
294     HalSetWakeEnable(FALSE);
295 
296     /* And finally the power request is sent */
297     DPRINT("Taking the system down\n");
298     PopShutdownSystem(PopAction.Action);
299 }
300 
301 VOID
302 NTAPI
PopReadShutdownPolicy(VOID)303 PopReadShutdownPolicy(VOID)
304 {
305     UNICODE_STRING KeyString;
306     OBJECT_ATTRIBUTES ObjectAttributes;
307     NTSTATUS Status;
308     HANDLE KeyHandle;
309     ULONG Length;
310     UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
311     PKEY_VALUE_PARTIAL_INFORMATION Info = (PVOID)Buffer;
312 
313     /* Setup object attributes */
314     RtlInitUnicodeString(&KeyString,
315                          L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows NT");
316     InitializeObjectAttributes(&ObjectAttributes,
317                                &KeyString,
318                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
319                                NULL,
320                                NULL);
321 
322     /* Open the key */
323     Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
324     if (NT_SUCCESS(Status))
325     {
326         /* Open the policy value and query it */
327         RtlInitUnicodeString(&KeyString, L"DontPowerOffAfterShutdown");
328         Status = ZwQueryValueKey(KeyHandle,
329                                  &KeyString,
330                                  KeyValuePartialInformation,
331                                  &Info,
332                                  sizeof(Info),
333                                  &Length);
334         if ((NT_SUCCESS(Status)) && (Info->Type == REG_DWORD))
335         {
336             /* Read the policy */
337             PopShutdownPowerOffPolicy = *Info->Data == 1;
338         }
339 
340         /* Close the key */
341         ZwClose(KeyHandle);
342     }
343 }
344 
345 /* PUBLIC FUNCTIONS **********************************************************/
346 
347 /*
348  * @unimplemented
349  */
350 NTSTATUS
351 NTAPI
PoQueueShutdownWorkItem(_In_ PWORK_QUEUE_ITEM WorkItem)352 PoQueueShutdownWorkItem(
353     _In_ PWORK_QUEUE_ITEM WorkItem)
354 {
355     NTSTATUS Status;
356 
357     /* Acquire the shutdown list lock */
358     KeAcquireGuardedMutex(&PopShutdownListMutex);
359 
360     /* Check if the list is (already/still) available */
361     if (PopShutdownListAvailable)
362     {
363         /* Insert the item into the list */
364         InsertTailList(&PopShutdownQueue, &WorkItem->List);
365         Status = STATUS_SUCCESS;
366     }
367     else
368     {
369         /* We are already in shutdown */
370         Status = STATUS_SYSTEM_SHUTDOWN;
371     }
372 
373     /* Release the list lock */
374     KeReleaseGuardedMutex(&PopShutdownListMutex);
375 
376     return Status;
377 }
378 
379 /*
380  * @implemented
381  */
382 NTSTATUS
383 NTAPI
PoRequestShutdownEvent(OUT PVOID * Event)384 PoRequestShutdownEvent(OUT PVOID *Event)
385 {
386     NTSTATUS Status;
387     PAGED_CODE();
388 
389     /* Initialize to NULL */
390     if (Event) *Event = NULL;
391 
392     /* Request a shutdown wait */
393     Status = PoRequestShutdownWait(PsGetCurrentThread());
394     if (!NT_SUCCESS(Status))
395     {
396         return Status;
397     }
398 
399     /* Return the global shutdown event */
400     if (Event) *Event = &PopShutdownEvent;
401     return STATUS_SUCCESS;
402 }
403 
404