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 /* GLOBALS ******************************************************************/
20 static LIST_ENTRY BugcheckCallbackListHead
= {NULL
,NULL
};
21 static LIST_ENTRY BugcheckReasonCallbackListHead
= {NULL
,NULL
};
22 static ULONG InBugCheck
;
23 static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages
;
24 static ULONG KeBugCheckCount
= 1;
26 /* FUNCTIONS *****************************************************************/
30 KiInitializeBugCheck(VOID
)
32 PRTL_MESSAGE_RESOURCE_DATA BugCheckData
;
33 LDR_RESOURCE_INFO ResourceInfo
;
34 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
37 /* Initialize Callbadk Listhead and State */
38 InitializeListHead(&BugcheckCallbackListHead
);
39 InitializeListHead(&BugcheckReasonCallbackListHead
);
42 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
43 ResourceInfo
.Type
= 11;
44 ResourceInfo
.Name
= 1;
45 ResourceInfo
.Language
= 9;
48 Status
= LdrFindResource_U((PVOID
)KERNEL_BASE
,
53 /* Make sure it worked */
54 if (NT_SUCCESS(Status
)) {
56 DPRINT1("Found Bugcheck Resource Data!\n");
58 /* Now actually get a pointer to it */
59 Status
= LdrAccessResource((PVOID
)KERNEL_BASE
,
61 (PVOID
*)&BugCheckData
,
64 /* Make sure it worked */
65 if (NT_SUCCESS(Status
)) {
67 DPRINT1("Got Pointer to Bugcheck Resource Data!\n");
68 KiBugCodeMessages
= BugCheckData
;
78 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
)
81 BOOLEAN Status
= FALSE
;
83 /* Raise IRQL to High */
84 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
86 /* Check the Current State */
87 if (CallbackRecord
->State
== BufferInserted
) {
89 /* Reset state and remove from list */
90 CallbackRecord
->State
= BufferEmpty
;
91 RemoveEntryList(&CallbackRecord
->Entry
);
96 /* Lower IRQL and return */
106 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
109 BOOLEAN Status
= FALSE
;
111 /* Raise IRQL to High */
112 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
114 /* Check the Current State */
115 if (CallbackRecord
->State
== BufferInserted
) {
117 /* Reset state and remove from list */
118 CallbackRecord
->State
= BufferEmpty
;
119 RemoveEntryList(&CallbackRecord
->Entry
);
124 /* Lower IRQL and return */
125 KeLowerIrql(OldIrql
);
134 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
,
135 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine
,
141 BOOLEAN Status
= FALSE
;
143 /* Raise IRQL to High */
144 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
146 /* Check the Current State first so we don't double-register */
147 if (CallbackRecord
->State
== BufferEmpty
) {
149 /* Set the Callback Settings and insert into the list */
150 CallbackRecord
->Length
= Length
;
151 CallbackRecord
->Buffer
= Buffer
;
152 CallbackRecord
->Component
= Component
;
153 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
154 CallbackRecord
->State
= BufferInserted
;
155 InsertTailList(&BugcheckCallbackListHead
, &CallbackRecord
->Entry
);
160 /* Lower IRQL and return */
161 KeLowerIrql(OldIrql
);
170 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
,
171 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine
,
172 IN KBUGCHECK_CALLBACK_REASON Reason
,
176 BOOLEAN Status
= FALSE
;
178 /* Raise IRQL to High */
179 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
181 /* Check the Current State first so we don't double-register */
182 if (CallbackRecord
->State
== BufferEmpty
) {
184 /* Set the Callback Settings and insert into the list */
185 CallbackRecord
->Component
= Component
;
186 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
187 CallbackRecord
->State
= BufferInserted
;
188 CallbackRecord
->Reason
= Reason
;
189 InsertTailList(&BugcheckReasonCallbackListHead
, &CallbackRecord
->Entry
);
194 /* Lower IRQL and return */
195 KeLowerIrql(OldIrql
);
201 KeGetBugMessageText(ULONG BugCheckCode
, PANSI_STRING OutputString
)
205 ULONG_PTR MessageEntry
;
208 /* Find the message. This code is based on RtlFindMesssage -- Alex */
209 for (i
= 0; i
< KiBugCodeMessages
->NumberOfBlocks
; i
++) {
211 /* Check if the ID Matches */
212 if ((BugCheckCode
>= KiBugCodeMessages
->Blocks
[i
].LowId
) &&
213 (BugCheckCode
<= KiBugCodeMessages
->Blocks
[i
].HighId
)) {
215 /* Get Offset to Entry */
216 MessageEntry
= (ULONG_PTR
)KiBugCodeMessages
+ KiBugCodeMessages
->Blocks
[i
].OffsetToEntries
;
217 IdOffset
= BugCheckCode
- KiBugCodeMessages
->Blocks
[i
].LowId
;
219 /* Get offset to ID */
220 for (i
= 0; i
< IdOffset
; i
++) {
222 /* Advance in the Entries */
223 MessageEntry
+= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Length
;
226 /* Get the final Code */
227 BugCode
= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Text
;
229 /* Return it in the OutputString */
232 OutputString
->Buffer
= BugCode
;
233 OutputString
->Length
= strlen(BugCode
) + 1;
234 OutputString
->MaximumLength
= strlen(BugCode
) + 1;
238 /* Direct Output to Screen */
239 DbgPrint("%s\n", BugCode
);
248 KiDoBugCheckCallbacks(VOID
)
250 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
251 PLIST_ENTRY ListHead
;
252 PLIST_ENTRY NextEntry
;
254 /* FIXME: Check Checksum and add support for WithReason Callbacks */
256 /* First make sure that the list is Initialized... it might not be */
257 ListHead
= &BugcheckCallbackListHead
;
258 if (ListHead
->Flink
&& ListHead
->Blink
) {
261 NextEntry
= ListHead
->Flink
;
262 while (NextEntry
!= ListHead
) {
264 /* Get the Callback Record */
265 CurrentRecord
= CONTAINING_RECORD(NextEntry
,
266 KBUGCHECK_CALLBACK_RECORD
,
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
;
279 /* Move to next Entry */
280 NextEntry
= NextEntry
->Flink
;
287 KeBugCheckWithTf(ULONG BugCheckCode
,
288 ULONG BugCheckParameter1
,
289 ULONG BugCheckParameter2
,
290 ULONG BugCheckParameter3
,
291 ULONG BugCheckParameter4
,
295 BOOLEAN GotExtendedCrashInfo
= FALSE
;
297 PLIST_ENTRY CurrentEntry
;
298 MODULE_TEXT_SECTION
* CurrentSection
= NULL
;
299 extern LIST_ENTRY ModuleTextListHead
;
301 /* Make sure we're switching back to the blue screen and print messages on it */
302 HalReleaseDisplayOwnership();
303 if (KdpDebugMode
.Gdb
) KdpDebugMode
.Screen
= TRUE
;
305 /* Try to find out who did this. For this, we need a Trap Frame.
306 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
307 * info so it eventually needs to be supported.
311 /* For now, get Address from EIP */
312 Address
= (PVOID
)Tf
->Eip
;
314 /* Try to get information on the module */
315 CurrentEntry
= ModuleTextListHead
.Flink
;
316 while (CurrentEntry
!= &ModuleTextListHead
&& CurrentEntry
!= NULL
) {
318 /* Get the current Section */
319 CurrentSection
= CONTAINING_RECORD(CurrentEntry
,
323 /* Check if this is the right one */
324 if ((Address
!= NULL
&& (Address
>= (PVOID
)CurrentSection
->Base
&&
325 Address
< (PVOID
)(CurrentSection
->Base
+ CurrentSection
->Length
)))) {
328 GotExtendedCrashInfo
= TRUE
;
333 CurrentEntry
= CurrentEntry
->Flink
;
337 /* Raise IRQL to HIGH_LEVEL */
338 Ke386DisableInterrupts();
339 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
341 /* Unload the Kernel Adress Space if we own it */
342 if (MmGetKernelAddressSpace()->Lock
.Owner
== KeGetCurrentThread())
343 MmUnlockAddressSpace(MmGetKernelAddressSpace());
345 /* FIXMEs: Use inbv to clear, fill and write to screen. */
347 /* Show the STOP Message */
348 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
349 "damage to your computer.\n\n");
351 /* Show the module name of who caused this */
352 if (GotExtendedCrashInfo
) {
354 DbgPrint("The problem seems to be caused by the following file: %S\n\n", CurrentSection
->Name
);
357 /* Find the Bug Code String */
358 KeGetBugMessageText(BugCheckCode
, NULL
);
360 /* Show the techincal Data */
361 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
368 /* Show the module name and more data of who caused this */
369 if (GotExtendedCrashInfo
) {
371 DbgPrint("*** %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
372 CurrentSection
->Name
,
374 CurrentSection
->Base
,
378 /* There can only be one Bugcheck per Bootup */
379 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
)) {
383 /* Freeze the other CPUs */
384 for (i
= 0; i
< KeNumberProcessors
; i
++) {
385 if (i
!= KeGetCurrentProcessorNumber()) {
387 /* Send the IPI and give them one second to catch up */
388 KiIpiSendRequest(1 << i
, IPI_REQUEST_FREEZE
);
389 KeStallExecutionProcessor(1000000);
394 /* Check if we got a Trap Frame */
398 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
402 /* We can only dump the frames */
403 #if defined(__GNUC__)
404 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
405 #elif defined(_MSC_VER)
407 __asm call KeDumpStackFrames
410 #error Unknown compiler for inline assembler
414 /* Call the Callbacks */;
415 KiDoBugCheckCallbacks();
417 /* Dump the BSOD to the Paging File */
418 MmDumpToPagingFile(BugCheckCode
,
425 /* Wake up the Debugger */
426 if (KdDebuggerEnabled
) {
427 Ke386EnableInterrupts();
428 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
429 Ke386DisableInterrupts();
433 /* Halt this CPU now */
434 for (;;) Ke386HaltProcessor();
440 * FUNCTION: Brings the system down in a controlled manner when an
441 * inconsistency that might otherwise cause corruption has been detected
443 * BugCheckCode = Specifies the reason for the bug check
444 * BugCheckParameter[1-4] = Additional information about bug
449 KeBugCheckEx(ULONG BugCheckCode
,
450 ULONG BugCheckParameter1
,
451 ULONG BugCheckParameter2
,
452 ULONG BugCheckParameter3
,
453 ULONG BugCheckParameter4
)
455 /* Call the Trap Frame version without a Trap Frame */
456 KeBugCheckWithTf(BugCheckCode
,
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
474 KeBugCheck(ULONG BugCheckCode
)
476 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);