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