xref: /reactos/ntoskrnl/po/poshtdwn.c (revision 7115d7ba)
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
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
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
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
150 PopShutdownHandler(VOID)
151 {
152     PUCHAR Logo1, Logo2;
153     ULONG i;
154 
155     /* Stop all interrupts */
156     KeRaiseIrqlToDpcLevel();
157     _disable();
158 
159     /* Do we have boot video */
160     if (InbvIsBootDriverInstalled())
161     {
162         /* Yes we do, cleanup for shutdown screen */
163         if (!InbvCheckDisplayOwnership()) InbvAcquireDisplayOwnership();
164         InbvResetDisplay();
165         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
166         InbvEnableDisplayString(TRUE);
167         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
168 
169         /* Display shutdown logo and message */
170         Logo1 = InbvGetResourceAddress(IDB_SHUTDOWN_MSG);
171         Logo2 = InbvGetResourceAddress(IDB_LOGO_DEFAULT);
172         if ((Logo1) && (Logo2))
173         {
174             InbvBitBlt(Logo1, VID_SHUTDOWN_MSG_LEFT, VID_SHUTDOWN_MSG_TOP);
175             InbvBitBlt(Logo2, VID_SHUTDOWN_LOGO_LEFT, VID_SHUTDOWN_LOGO_TOP);
176         }
177     }
178     else
179     {
180         /* Do it in text-mode */
181         for (i = 0; i < 25; i++) InbvDisplayString("\r\n");
182         InbvDisplayString("                       ");
183         InbvDisplayString("The system may be powered off now.\r\n");
184     }
185 
186     /* Hang the system */
187     for (;;) HalHaltSystem();
188 }
189 
190 VOID
191 NTAPI
192 PopShutdownSystem(IN POWER_ACTION SystemAction)
193 {
194     /* Note should notify caller of NtPowerInformation(PowerShutdownNotification) */
195 
196     /* Unload symbols */
197     DPRINT("It's the final countdown...%lx\n", SystemAction);
198     DbgUnLoadImageSymbols(NULL, (PVOID)-1, 0);
199 
200     /* Run the thread on the boot processor */
201     KeSetSystemAffinityThread(1);
202 
203     /* Now check what the caller wants */
204     switch (SystemAction)
205     {
206         /* Reset */
207         case PowerActionShutdownReset:
208 
209             /* Try platform driver first, then legacy */
210             //PopInvokeSystemStateHandler(PowerStateShutdownReset, NULL);
211             PopSetSystemPowerState(PowerSystemShutdown, SystemAction);
212             HalReturnToFirmware(HalRebootRoutine);
213             break;
214 
215         case PowerActionShutdown:
216 
217             /* Check for group policy that says to use "it is now safe" screen */
218             if (PopShutdownPowerOffPolicy)
219             {
220                 /* FIXFIX: Switch to legacy shutdown handler */
221                 //PopPowerStateHandlers[PowerStateShutdownOff].Handler = PopShutdownHandler;
222             }
223 
224         case PowerActionShutdownOff:
225 
226             /* Call shutdown handler */
227             //PopInvokeSystemStateHandler(PowerStateShutdownOff, NULL);
228 
229             /* ReactOS Hack */
230             PopSetSystemPowerState(PowerSystemShutdown, SystemAction);
231             PopShutdownHandler();
232 
233             /* If that didn't work, call the HAL */
234             HalReturnToFirmware(HalPowerDownRoutine);
235             break;
236 
237         default:
238             break;
239     }
240 
241     /* Anything else should not happen */
242     KeBugCheckEx(INTERNAL_POWER_ERROR, 5, 0, 0, 0);
243 }
244 
245 VOID
246 NTAPI
247 PopGracefulShutdown(IN PVOID Context)
248 {
249     PEPROCESS Process = NULL;
250 
251     /* Process the registered waits and work items */
252     PopProcessShutDownLists();
253 
254     /* Loop every process */
255     Process = PsGetNextProcess(Process);
256     while (Process)
257     {
258         /* Make sure this isn't the idle or initial process */
259         if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
260         {
261             /* Print it */
262             DPRINT1("%15s is still RUNNING (%p)\n", Process->ImageFileName, Process->UniqueProcessId);
263         }
264 
265         /* Get the next process */
266         Process = PsGetNextProcess(Process);
267     }
268 
269     /* First, the HAL handles any "end of boot" special functionality */
270     DPRINT("HAL shutting down\n");
271     HalEndOfBoot();
272 
273     /* Shut down the Shim cache if enabled */
274     ApphelpCacheShutdown();
275 
276     /* In this step, the I/O manager does first-chance shutdown notification */
277     DPRINT("I/O manager shutting down in phase 0\n");
278     IoShutdownSystem(0);
279 
280     /* In this step, all workers are killed and hives are flushed */
281     DPRINT("Configuration Manager shutting down\n");
282     CmShutdownSystem();
283 
284     /* Shut down the Executive */
285     DPRINT("Executive shutting down\n");
286     ExShutdownSystem();
287 
288     /* Note that modified pages should be written here (MiShutdownSystem) */
289     MmShutdownSystem(0);
290 
291     /* Flush all user files before we start shutting down IO */
292     /* This is where modified pages are written back by the IO manager */
293     CcShutdownSystem();
294 
295     /* In this step, the I/O manager does last-chance shutdown notification */
296     DPRINT("I/O manager shutting down in phase 1\n");
297     IoShutdownSystem(1);
298     CcWaitForCurrentLazyWriterActivity();
299 
300     /* FIXME: Calling Mm shutdown phase 1 here to get page file dereference
301      * but it shouldn't be called here. Only phase 2 should be called.
302      */
303     MmShutdownSystem(1);
304 
305     /* Note that here, we should broadcast the power IRP to devices */
306 
307     /* In this step, the HAL disables any wake timers */
308     DPRINT("Disabling wake timers\n");
309     HalSetWakeEnable(FALSE);
310 
311     /* And finally the power request is sent */
312     DPRINT("Taking the system down\n");
313     PopShutdownSystem(PopAction.Action);
314 }
315 
316 VOID
317 NTAPI
318 PopReadShutdownPolicy(VOID)
319 {
320     UNICODE_STRING KeyString;
321     OBJECT_ATTRIBUTES ObjectAttributes;
322     NTSTATUS Status;
323     HANDLE KeyHandle;
324     ULONG Length;
325     UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
326     PKEY_VALUE_PARTIAL_INFORMATION Info = (PVOID)Buffer;
327 
328     /* Setup object attributes */
329     RtlInitUnicodeString(&KeyString,
330                          L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows NT");
331     InitializeObjectAttributes(&ObjectAttributes,
332                                &KeyString,
333                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
334                                NULL,
335                                NULL);
336 
337     /* Open the key */
338     Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
339     if (NT_SUCCESS(Status))
340     {
341         /* Open the policy value and query it */
342         RtlInitUnicodeString(&KeyString, L"DontPowerOffAfterShutdown");
343         Status = ZwQueryValueKey(KeyHandle,
344                                  &KeyString,
345                                  KeyValuePartialInformation,
346                                  &Info,
347                                  sizeof(Info),
348                                  &Length);
349         if ((NT_SUCCESS(Status)) && (Info->Type == REG_DWORD))
350         {
351             /* Read the policy */
352             PopShutdownPowerOffPolicy = *Info->Data == 1;
353         }
354 
355         /* Close the key */
356         ZwClose(KeyHandle);
357     }
358 }
359 
360 /* PUBLIC FUNCTIONS **********************************************************/
361 
362 /*
363  * @unimplemented
364  */
365 NTSTATUS
366 NTAPI
367 PoQueueShutdownWorkItem(
368     _In_ PWORK_QUEUE_ITEM WorkItem)
369 {
370     NTSTATUS Status;
371 
372     /* Acquire the shutdown list lock */
373     KeAcquireGuardedMutex(&PopShutdownListMutex);
374 
375     /* Check if the list is (already/still) available */
376     if (PopShutdownListAvailable)
377     {
378         /* Insert the item into the list */
379         InsertTailList(&PopShutdownQueue, &WorkItem->List);
380         Status = STATUS_SUCCESS;
381     }
382     else
383     {
384         /* We are already in shutdown */
385         Status = STATUS_SYSTEM_SHUTDOWN;
386     }
387 
388     /* Release the list lock */
389     KeReleaseGuardedMutex(&PopShutdownListMutex);
390 
391     return Status;
392 }
393 
394 /*
395  * @implemented
396  */
397 NTSTATUS
398 NTAPI
399 PoRequestShutdownEvent(OUT PVOID *Event)
400 {
401     NTSTATUS Status;
402     PAGED_CODE();
403 
404     /* Initialize to NULL */
405     if (Event) *Event = NULL;
406 
407     /* Request a shutdown wait */
408     Status = PoRequestShutdownWait(PsGetCurrentThread());
409     if (!NT_SUCCESS(Status))
410     {
411         return Status;
412     }
413 
414     /* Return the global shutdown event */
415     if (Event) *Event = &PopShutdownEvent;
416     return STATUS_SUCCESS;
417 }
418 
419