[NTOS:PO] PopShutdownHandler(): Fully screen-center images. Addendum to 55aed4f8.
[reactos.git] / ntoskrnl / po / poshtdwn.c
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