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
7 * PROGRAMMERS: Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks.
8 * David Welch (welch@cwcom.net)
12 /* INCLUDES *****************************************************************/
16 #include <internal/debug.h>
18 #if defined (ALLOC_PRAGMA)
19 #pragma alloc_text(INIT, KiInitializeBugCheck)
22 /* GLOBALS ******************************************************************/
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;
30 /* FUNCTIONS *****************************************************************/
35 KiInitializeBugCheck(VOID
)
37 PRTL_MESSAGE_RESOURCE_DATA BugCheckData
;
38 LDR_RESOURCE_INFO ResourceInfo
;
39 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
42 /* Initialize Callbadk Listhead and State */
43 InitializeListHead(&BugcheckCallbackListHead
);
44 InitializeListHead(&BugcheckReasonCallbackListHead
);
47 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
48 ResourceInfo
.Type
= 11;
49 ResourceInfo
.Name
= 1;
50 ResourceInfo
.Language
= 9;
53 Status
= LdrFindResource_U((PVOID
)KERNEL_BASE
,
58 /* Make sure it worked */
59 if (NT_SUCCESS(Status
)) {
61 DPRINT("Found Bugcheck Resource Data!\n");
63 /* Now actually get a pointer to it */
64 Status
= LdrAccessResource((PVOID
)KERNEL_BASE
,
66 (PVOID
*)&BugCheckData
,
69 /* Make sure it worked */
70 if (NT_SUCCESS(Status
)) {
72 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
73 KiBugCodeMessages
= BugCheckData
;
83 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
)
86 BOOLEAN Status
= FALSE
;
88 /* Raise IRQL to High */
89 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
91 /* Check the Current State */
92 if (CallbackRecord
->State
== BufferInserted
) {
94 /* Reset state and remove from list */
95 CallbackRecord
->State
= BufferEmpty
;
96 RemoveEntryList(&CallbackRecord
->Entry
);
101 /* Lower IRQL and return */
102 KeLowerIrql(OldIrql
);
111 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
114 BOOLEAN Status
= FALSE
;
116 /* Raise IRQL to High */
117 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
119 /* Check the Current State */
120 if (CallbackRecord
->State
== BufferInserted
) {
122 /* Reset state and remove from list */
123 CallbackRecord
->State
= BufferEmpty
;
124 RemoveEntryList(&CallbackRecord
->Entry
);
129 /* Lower IRQL and return */
130 KeLowerIrql(OldIrql
);
139 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
,
140 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine
,
146 BOOLEAN Status
= FALSE
;
148 /* Raise IRQL to High */
149 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
151 /* Check the Current State first so we don't double-register */
152 if (CallbackRecord
->State
== BufferEmpty
) {
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
);
165 /* Lower IRQL and return */
166 KeLowerIrql(OldIrql
);
175 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
,
176 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine
,
177 IN KBUGCHECK_CALLBACK_REASON Reason
,
181 BOOLEAN Status
= FALSE
;
183 /* Raise IRQL to High */
184 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
186 /* Check the Current State first so we don't double-register */
187 if (CallbackRecord
->State
== BufferEmpty
) {
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
);
199 /* Lower IRQL and return */
200 KeLowerIrql(OldIrql
);
206 KeGetBugMessageText(ULONG BugCheckCode
, PANSI_STRING OutputString
)
210 ULONG_PTR MessageEntry
;
213 /* Find the message. This code is based on RtlFindMesssage -- Alex */
214 for (i
= 0; i
< KiBugCodeMessages
->NumberOfBlocks
; i
++)
216 /* Check if the ID Matches */
217 if ((BugCheckCode
>= KiBugCodeMessages
->Blocks
[i
].LowId
) &&
218 (BugCheckCode
<= KiBugCodeMessages
->Blocks
[i
].HighId
))
220 /* Get Offset to Entry */
221 MessageEntry
= (ULONG_PTR
)KiBugCodeMessages
+ KiBugCodeMessages
->Blocks
[i
].OffsetToEntries
;
222 IdOffset
= BugCheckCode
- KiBugCodeMessages
->Blocks
[i
].LowId
;
224 /* Get offset to ID */
225 for (i
= 0; i
< IdOffset
; i
++)
227 /* Advance in the Entries */
228 MessageEntry
+= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Length
;
231 /* Get the final Code */
232 BugCode
= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Text
;
234 /* Return it in the OutputString */
237 OutputString
->Buffer
= BugCode
;
238 OutputString
->Length
= strlen(BugCode
) + 1;
239 OutputString
->MaximumLength
= strlen(BugCode
) + 1;
243 /* Direct Output to Screen */
245 sprintf(BugString
, "%s\n", BugCode
);
246 InbvDisplayString(BugString
);
255 KiDoBugCheckCallbacks(VOID
)
257 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
258 PLIST_ENTRY ListHead
;
260 /* FIXME: Check Checksum and add support for WithReason Callbacks */
262 /* First make sure that the list is Initialized... it might not be */
263 ListHead
= &BugcheckCallbackListHead
;
264 if (ListHead
->Flink
&& ListHead
->Blink
) {
267 LIST_FOR_EACH(CurrentRecord
, ListHead
, KBUGCHECK_CALLBACK_RECORD
, Entry
)
269 /* Make sure it's inserted */
270 if (CurrentRecord
->State
== BufferInserted
) {
272 /* Call the routine */
273 CurrentRecord
->State
= BufferStarted
;
274 (CurrentRecord
->CallbackRoutine
)(CurrentRecord
->Buffer
,
275 CurrentRecord
->Length
);
276 CurrentRecord
->State
= BufferFinished
;
284 KeBugCheckWithTf(ULONG BugCheckCode
,
285 ULONG BugCheckParameter1
,
286 ULONG BugCheckParameter2
,
287 ULONG BugCheckParameter3
,
288 ULONG BugCheckParameter4
,
292 BOOLEAN GotExtendedCrashInfo
= FALSE
;
294 PLDR_DATA_TABLE_ENTRY CurrentModule
= NULL
;
295 extern LIST_ENTRY ModuleListHead
;
297 CHAR PrintString
[100];
299 /* Make sure we're switching back to the blue screen and print messages on it */
300 HalReleaseDisplayOwnership();
301 if (!KdpDebugMode
.Screen
)
303 /* Enable screen debug mode */
304 KdpDebugMode
.Screen
= TRUE
;
305 InitRoutines
[0](&DispatchTable
[0], 0);
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.
314 /* For now, get Address from EIP */
315 Address
= (PVOID
)Tf
->Eip
;
317 /* Try to get information on the module */
318 LIST_FOR_EACH(CurrentModule
, &ModuleListHead
, LDR_DATA_TABLE_ENTRY
, InLoadOrderModuleList
)
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
))))
325 GotExtendedCrashInfo
= TRUE
;
331 /* Raise IRQL to HIGH_LEVEL */
332 Ke386DisableInterrupts();
333 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
335 /* Unload the Kernel Adress Space if we own it */
336 if (MmGetKernelAddressSpace()->Lock
.Owner
== KeGetCurrentThread())
337 MmUnlockAddressSpace(MmGetKernelAddressSpace());
339 /* FIXMEs: Use inbv to clear, fill and write to screen. */
341 /* Show the STOP Message */
343 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
344 "damage to your computer.\n\n");
346 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
347 "damage to your computer.\n\n");
349 /* Show the module name of who caused this */
350 if (GotExtendedCrashInfo
)
354 "The problem seems to be caused by the following file: %wZ\n\n",
355 &CurrentModule
->BaseDllName
);
356 InbvDisplayString(PrintString
);
358 DbgPrint("The problem seems to be caused by the following file: %wZ\n\n",
359 &CurrentModule
->BaseDllName
);
363 /* Find the Bug Code String */
364 KeGetBugMessageText(BugCheckCode
, NULL
);
366 /* Show the techincal Data */
369 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
371 (PVOID
)BugCheckParameter1
,
372 (PVOID
)BugCheckParameter2
,
373 (PVOID
)BugCheckParameter3
,
374 (PVOID
)BugCheckParameter4
);
375 InbvDisplayString(PrintString
);
377 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
379 (PVOID
)BugCheckParameter1
,
380 (PVOID
)BugCheckParameter2
,
381 (PVOID
)BugCheckParameter3
,
382 (PVOID
)BugCheckParameter4
);
384 /* Show the module name and more data of who caused this */
385 if (GotExtendedCrashInfo
)
389 "*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
390 &CurrentModule
->BaseDllName
,
392 (PVOID
)CurrentModule
->DllBase
,
394 InbvDisplayString(PrintString
);
396 DbgPrint("*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
397 &CurrentModule
->BaseDllName
,
399 (PVOID
)CurrentModule
->DllBase
,
404 /* There can only be one Bugcheck per Bootup */
405 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
))
409 /* Freeze the other CPUs */
410 for (i
= 0; i
< KeNumberProcessors
; i
++)
412 if (i
!= (LONG
)KeGetCurrentProcessorNumber())
414 /* Send the IPI and give them one second to catch up */
415 KiIpiSendRequest(1 << i
, IPI_FREEZE
);
416 KeStallExecutionProcessor(1000000);
420 /* Check if we got a Trap Frame */
424 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
428 /* We can only dump the frames */
429 #if defined(__GNUC__)
430 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
431 #elif defined(_MSC_VER)
433 __asm call KeDumpStackFrames
436 #error Unknown compiler for inline assembler
440 /* Call the Callbacks */;
441 KiDoBugCheckCallbacks();
443 /* Dump the BSOD to the Paging File */
444 MmDumpToPagingFile(BugCheckCode
,
451 /* Wake up the Debugger */
452 if (KdDebuggerEnabled
)
454 Ke386EnableInterrupts();
455 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
456 Ke386DisableInterrupts();
460 /* Halt this CPU now */
461 for (;;) Ke386HaltProcessor();
467 * FUNCTION: Brings the system down in a controlled manner when an
468 * inconsistency that might otherwise cause corruption has been detected
470 * BugCheckCode = Specifies the reason for the bug check
471 * BugCheckParameter[1-4] = Additional information about bug
476 KeBugCheckEx(ULONG BugCheckCode
,
477 ULONG BugCheckParameter1
,
478 ULONG BugCheckParameter2
,
479 ULONG BugCheckParameter3
,
480 ULONG BugCheckParameter4
)
482 /* Call the Trap Frame version without a Trap Frame */
483 KeBugCheckWithTf(BugCheckCode
,
494 * FUNCTION: Brings the system down in a controlled manner when an
495 * inconsistency that might otherwise cause corruption has been detected
497 * BugCheckCode = Specifies the reason for the bug check
502 KeBugCheck(ULONG BugCheckCode
)
504 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);