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