Cleanup isn't necessary after calling the driver in NtQueryDirectoryFile.
[reactos.git] / reactos / ntoskrnl / ke / bug.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/bug.c
5 * PURPOSE: Graceful system shutdown if a bug is detected
6 *
7 * PROGRAMMERS: Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks.
8 * David Welch (welch@cwcom.net)
9 * Phillip Susi
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 #if defined (ALLOC_PRAGMA)
19 #pragma alloc_text(INIT, KiInitializeBugCheck)
20 #endif
21
22 /* GLOBALS ******************************************************************/
23
24 static LIST_ENTRY BugcheckCallbackListHead = {NULL,NULL};
25 static LIST_ENTRY BugcheckReasonCallbackListHead = {NULL,NULL};
26 static ULONG InBugCheck;
27 static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages;
28 static ULONG KeBugCheckCount = 1;
29
30 /* FUNCTIONS *****************************************************************/
31
32 VOID
33 INIT_FUNCTION
34 NTAPI
35 KiInitializeBugCheck(VOID)
36 {
37 PRTL_MESSAGE_RESOURCE_DATA BugCheckData;
38 LDR_RESOURCE_INFO ResourceInfo;
39 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
40 NTSTATUS Status;
41
42 /* Initialize Callbadk Listhead and State */
43 InitializeListHead(&BugcheckCallbackListHead);
44 InitializeListHead(&BugcheckReasonCallbackListHead);
45 InBugCheck = 0;
46
47 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
48 ResourceInfo.Type = 11;
49 ResourceInfo.Name = 1;
50 ResourceInfo.Language = 9;
51
52 /* Do the lookup. */
53 Status = LdrFindResource_U((PVOID)KERNEL_BASE,
54 &ResourceInfo,
55 RESOURCE_DATA_LEVEL,
56 &ResourceDataEntry);
57
58 /* Make sure it worked */
59 if (NT_SUCCESS(Status)) {
60
61 DPRINT("Found Bugcheck Resource Data!\n");
62
63 /* Now actually get a pointer to it */
64 Status = LdrAccessResource((PVOID)KERNEL_BASE,
65 ResourceDataEntry,
66 (PVOID*)&BugCheckData,
67 NULL);
68
69 /* Make sure it worked */
70 if (NT_SUCCESS(Status)) {
71
72 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
73 KiBugCodeMessages = BugCheckData;
74 }
75 }
76 }
77
78 /*
79 * @implemented
80 */
81 BOOLEAN
82 STDCALL
83 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
84 {
85 KIRQL OldIrql;
86 BOOLEAN Status = FALSE;
87
88 /* Raise IRQL to High */
89 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
90
91 /* Check the Current State */
92 if (CallbackRecord->State == BufferInserted) {
93
94 /* Reset state and remove from list */
95 CallbackRecord->State = BufferEmpty;
96 RemoveEntryList(&CallbackRecord->Entry);
97
98 Status = TRUE;
99 }
100
101 /* Lower IRQL and return */
102 KeLowerIrql(OldIrql);
103 return Status;
104 }
105
106 /*
107 * @implemented
108 */
109 BOOLEAN
110 STDCALL
111 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
112 {
113 KIRQL OldIrql;
114 BOOLEAN Status = FALSE;
115
116 /* Raise IRQL to High */
117 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
118
119 /* Check the Current State */
120 if (CallbackRecord->State == BufferInserted) {
121
122 /* Reset state and remove from list */
123 CallbackRecord->State = BufferEmpty;
124 RemoveEntryList(&CallbackRecord->Entry);
125
126 Status = TRUE;
127 }
128
129 /* Lower IRQL and return */
130 KeLowerIrql(OldIrql);
131 return Status;
132 }
133
134 /*
135 * @implemented
136 */
137 BOOLEAN
138 STDCALL
139 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
140 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
141 PVOID Buffer,
142 ULONG Length,
143 PUCHAR Component)
144 {
145 KIRQL OldIrql;
146 BOOLEAN Status = FALSE;
147
148 /* Raise IRQL to High */
149 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
150
151 /* Check the Current State first so we don't double-register */
152 if (CallbackRecord->State == BufferEmpty) {
153
154 /* Set the Callback Settings and insert into the list */
155 CallbackRecord->Length = Length;
156 CallbackRecord->Buffer = Buffer;
157 CallbackRecord->Component = Component;
158 CallbackRecord->CallbackRoutine = CallbackRoutine;
159 CallbackRecord->State = BufferInserted;
160 InsertTailList(&BugcheckCallbackListHead, &CallbackRecord->Entry);
161
162 Status = TRUE;
163 }
164
165 /* Lower IRQL and return */
166 KeLowerIrql(OldIrql);
167 return Status;
168 }
169
170 /*
171 * @implemented
172 */
173 BOOLEAN
174 STDCALL
175 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
176 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
177 IN KBUGCHECK_CALLBACK_REASON Reason,
178 IN PUCHAR Component)
179 {
180 KIRQL OldIrql;
181 BOOLEAN Status = FALSE;
182
183 /* Raise IRQL to High */
184 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
185
186 /* Check the Current State first so we don't double-register */
187 if (CallbackRecord->State == BufferEmpty) {
188
189 /* Set the Callback Settings and insert into the list */
190 CallbackRecord->Component = Component;
191 CallbackRecord->CallbackRoutine = CallbackRoutine;
192 CallbackRecord->State = BufferInserted;
193 CallbackRecord->Reason = Reason;
194 InsertTailList(&BugcheckReasonCallbackListHead, &CallbackRecord->Entry);
195
196 Status = TRUE;
197 }
198
199 /* Lower IRQL and return */
200 KeLowerIrql(OldIrql);
201 return Status;
202 }
203
204 VOID
205 STDCALL
206 KeGetBugMessageText(ULONG BugCheckCode, PANSI_STRING OutputString)
207 {
208 ULONG i;
209 ULONG IdOffset;
210 ULONG_PTR MessageEntry;
211 PCHAR BugCode;
212
213 /* Find the message. This code is based on RtlFindMesssage -- Alex */
214 for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
215 {
216 /* Check if the ID Matches */
217 if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
218 (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
219 {
220 /* Get Offset to Entry */
221 MessageEntry = (ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries;
222 IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
223
224 /* Get offset to ID */
225 for (i = 0; i < IdOffset; i++)
226 {
227 /* Advance in the Entries */
228 MessageEntry += ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Length;
229 }
230
231 /* Get the final Code */
232 BugCode = ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Text;
233
234 /* Return it in the OutputString */
235 if (OutputString)
236 {
237 OutputString->Buffer = BugCode;
238 OutputString->Length = strlen(BugCode) + 1;
239 OutputString->MaximumLength = strlen(BugCode) + 1;
240 }
241 else
242 {
243 /* Direct Output to Screen */
244 CHAR BugString[100];
245 sprintf(BugString, "%s\n", BugCode);
246 InbvDisplayString(BugString);
247 break;
248 }
249 }
250 }
251 }
252
253 VOID
254 STDCALL
255 KiDoBugCheckCallbacks(VOID)
256 {
257 PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
258 PLIST_ENTRY ListHead;
259
260 /* FIXME: Check Checksum and add support for WithReason Callbacks */
261
262 /* First make sure that the list is Initialized... it might not be */
263 ListHead = &BugcheckCallbackListHead;
264 if (ListHead->Flink && ListHead->Blink) {
265
266 /* Loop the list */
267 LIST_FOR_EACH(CurrentRecord, ListHead, KBUGCHECK_CALLBACK_RECORD, Entry)
268 {
269 /* Make sure it's inserted */
270 if (CurrentRecord->State == BufferInserted) {
271
272 /* Call the routine */
273 CurrentRecord->State = BufferStarted;
274 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
275 CurrentRecord->Length);
276 CurrentRecord->State = BufferFinished;
277 }
278 }
279 }
280 }
281
282 VOID
283 STDCALL
284 KeBugCheckWithTf(ULONG BugCheckCode,
285 ULONG BugCheckParameter1,
286 ULONG BugCheckParameter2,
287 ULONG BugCheckParameter3,
288 ULONG BugCheckParameter4,
289 PKTRAP_FRAME Tf)
290 {
291 KIRQL OldIrql;
292 BOOLEAN GotExtendedCrashInfo = FALSE;
293 PVOID Address = 0;
294 PLDR_DATA_TABLE_ENTRY CurrentModule = NULL;
295 extern LIST_ENTRY ModuleListHead;
296 #if 0
297 CHAR PrintString[100];
298 #endif
299 /* Make sure we're switching back to the blue screen and print messages on it */
300 HalReleaseDisplayOwnership();
301 if (!KdpDebugMode.Screen)
302 {
303 /* Enable screen debug mode */
304 KdpDebugMode.Screen = TRUE;
305 InitRoutines[0](&DispatchTable[0], 0);
306 }
307
308 /* Try to find out who did this. For this, we need a Trap Frame.
309 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
310 * info so it eventually needs to be supported.
311 */
312 if (Tf)
313 {
314 /* For now, get Address from EIP */
315 Address = (PVOID)Tf->Eip;
316
317 /* Try to get information on the module */
318 LIST_FOR_EACH(CurrentModule, &ModuleListHead, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList)
319 {
320 /* Check if this is the right one */
321 if ((Address != NULL && (Address >= (PVOID)CurrentModule->DllBase &&
322 Address < (PVOID)((ULONG_PTR)CurrentModule->DllBase + CurrentModule->SizeOfImage))))
323 {
324 /* We got it */
325 GotExtendedCrashInfo = TRUE;
326 break;
327 }
328 }
329 }
330
331 /* Raise IRQL to HIGH_LEVEL */
332 Ke386DisableInterrupts();
333 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
334
335 /* Unload the Kernel Adress Space if we own it */
336 if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread())
337 MmUnlockAddressSpace(MmGetKernelAddressSpace());
338
339 /* FIXMEs: Use inbv to clear, fill and write to screen. */
340
341 /* Show the STOP Message */
342 #if 0
343 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
344 "damage to your computer.\n\n");
345 #else
346 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
347 "damage to your computer.\n\n");
348 #endif
349 /* Show the module name of who caused this */
350 if (GotExtendedCrashInfo)
351 {
352 #if 0
353 sprintf(PrintString,
354 "The problem seems to be caused by the following file: %wZ\n\n",
355 &CurrentModule->BaseDllName);
356 InbvDisplayString(PrintString);
357 #else
358 DbgPrint("The problem seems to be caused by the following file: %wZ\n\n",
359 &CurrentModule->BaseDllName);
360 #endif
361 }
362
363 /* Find the Bug Code String */
364 KeGetBugMessageText(BugCheckCode, NULL);
365
366 /* Show the techincal Data */
367 #if 0
368 sprintf(PrintString,
369 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
370 BugCheckCode,
371 (PVOID)BugCheckParameter1,
372 (PVOID)BugCheckParameter2,
373 (PVOID)BugCheckParameter3,
374 (PVOID)BugCheckParameter4);
375 InbvDisplayString(PrintString);
376 #else
377 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
378 BugCheckCode,
379 (PVOID)BugCheckParameter1,
380 (PVOID)BugCheckParameter2,
381 (PVOID)BugCheckParameter3,
382 (PVOID)BugCheckParameter4);
383 #endif
384 /* Show the module name and more data of who caused this */
385 if (GotExtendedCrashInfo)
386 {
387 #if 0
388 sprintf(PrintString,
389 "*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
390 &CurrentModule->BaseDllName,
391 Address,
392 (PVOID)CurrentModule->DllBase,
393 0);
394 InbvDisplayString(PrintString);
395 #else
396 DbgPrint("*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
397 &CurrentModule->BaseDllName,
398 Address,
399 (PVOID)CurrentModule->DllBase,
400 0);
401 #endif
402 }
403
404 /* There can only be one Bugcheck per Bootup */
405 if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
406 {
407 #ifdef CONFIG_SMP
408 LONG i;
409 /* Freeze the other CPUs */
410 for (i = 0; i < KeNumberProcessors; i++)
411 {
412 if (i != (LONG)KeGetCurrentProcessorNumber())
413 {
414 /* Send the IPI and give them one second to catch up */
415 KiIpiSendRequest(1 << i, IPI_FREEZE);
416 KeStallExecutionProcessor(1000000);
417 }
418 }
419 #endif
420 /* Check if we got a Trap Frame */
421 if (Tf)
422 {
423 /* Dump it */
424 KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
425 }
426 else
427 {
428 /* We can only dump the frames */
429 #if defined(__GNUC__)
430 KeDumpStackFrames((PULONG)__builtin_frame_address(0));
431 #elif defined(_MSC_VER)
432 __asm push ebp
433 __asm call KeDumpStackFrames
434 __asm add esp, 4
435 #else
436 #error Unknown compiler for inline assembler
437 #endif
438 }
439
440 /* Call the Callbacks */;
441 KiDoBugCheckCallbacks();
442
443 /* Dump the BSOD to the Paging File */
444 MmDumpToPagingFile(BugCheckCode,
445 BugCheckParameter1,
446 BugCheckParameter2,
447 BugCheckParameter3,
448 BugCheckParameter4,
449 Tf);
450
451 /* Wake up the Debugger */
452 if (KdDebuggerEnabled)
453 {
454 Ke386EnableInterrupts();
455 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
456 Ke386DisableInterrupts();
457 }
458 }
459
460 /* Halt this CPU now */
461 for (;;) Ke386HaltProcessor();
462 }
463
464 /*
465 * @implemented
466 *
467 * FUNCTION: Brings the system down in a controlled manner when an
468 * inconsistency that might otherwise cause corruption has been detected
469 * ARGUMENTS:
470 * BugCheckCode = Specifies the reason for the bug check
471 * BugCheckParameter[1-4] = Additional information about bug
472 * RETURNS: Doesn't
473 */
474 VOID
475 STDCALL
476 KeBugCheckEx(ULONG BugCheckCode,
477 ULONG BugCheckParameter1,
478 ULONG BugCheckParameter2,
479 ULONG BugCheckParameter3,
480 ULONG BugCheckParameter4)
481 {
482 /* Call the Trap Frame version without a Trap Frame */
483 KeBugCheckWithTf(BugCheckCode,
484 BugCheckParameter1,
485 BugCheckParameter2,
486 BugCheckParameter3,
487 BugCheckParameter4,
488 NULL);
489 }
490
491 /*
492 * @implemented
493 *
494 * FUNCTION: Brings the system down in a controlled manner when an
495 * inconsistency that might otherwise cause corruption has been detected
496 * ARGUMENTS:
497 * BugCheckCode = Specifies the reason for the bug check
498 * RETURNS: Doesn't
499 */
500 VOID
501 STDCALL
502 KeBugCheck(ULONG BugCheckCode)
503 {
504 KeBugCheckEx(BugCheckCode, 0, 0, 0, 0);
505 }
506
507 /* EOF */