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 *****************************************************************/
31 KiInitializeBugCheck(VOID
)
33 PRTL_MESSAGE_RESOURCE_DATA BugCheckData
;
34 LDR_RESOURCE_INFO ResourceInfo
;
35 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
38 /* Initialize Callbadk Listhead and State */
39 InitializeListHead(&BugcheckCallbackListHead
);
40 InitializeListHead(&BugcheckReasonCallbackListHead
);
43 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
44 ResourceInfo
.Type
= 11;
45 ResourceInfo
.Name
= 1;
46 ResourceInfo
.Language
= 9;
49 Status
= LdrFindResource_U((PVOID
)KERNEL_BASE
,
54 /* Make sure it worked */
55 if (NT_SUCCESS(Status
)) {
57 DPRINT("Found Bugcheck Resource Data!\n");
59 /* Now actually get a pointer to it */
60 Status
= LdrAccessResource((PVOID
)KERNEL_BASE
,
62 (PVOID
*)&BugCheckData
,
65 /* Make sure it worked */
66 if (NT_SUCCESS(Status
)) {
68 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
69 KiBugCodeMessages
= BugCheckData
;
79 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
)
82 BOOLEAN Status
= FALSE
;
84 /* Raise IRQL to High */
85 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
87 /* Check the Current State */
88 if (CallbackRecord
->State
== BufferInserted
) {
90 /* Reset state and remove from list */
91 CallbackRecord
->State
= BufferEmpty
;
92 RemoveEntryList(&CallbackRecord
->Entry
);
97 /* Lower IRQL and return */
107 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
110 BOOLEAN Status
= FALSE
;
112 /* Raise IRQL to High */
113 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
115 /* Check the Current State */
116 if (CallbackRecord
->State
== BufferInserted
) {
118 /* Reset state and remove from list */
119 CallbackRecord
->State
= BufferEmpty
;
120 RemoveEntryList(&CallbackRecord
->Entry
);
125 /* Lower IRQL and return */
126 KeLowerIrql(OldIrql
);
135 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
,
136 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine
,
142 BOOLEAN Status
= FALSE
;
144 /* Raise IRQL to High */
145 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
147 /* Check the Current State first so we don't double-register */
148 if (CallbackRecord
->State
== BufferEmpty
) {
150 /* Set the Callback Settings and insert into the list */
151 CallbackRecord
->Length
= Length
;
152 CallbackRecord
->Buffer
= Buffer
;
153 CallbackRecord
->Component
= Component
;
154 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
155 CallbackRecord
->State
= BufferInserted
;
156 InsertTailList(&BugcheckCallbackListHead
, &CallbackRecord
->Entry
);
161 /* Lower IRQL and return */
162 KeLowerIrql(OldIrql
);
171 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
,
172 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine
,
173 IN KBUGCHECK_CALLBACK_REASON Reason
,
177 BOOLEAN Status
= FALSE
;
179 /* Raise IRQL to High */
180 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
182 /* Check the Current State first so we don't double-register */
183 if (CallbackRecord
->State
== BufferEmpty
) {
185 /* Set the Callback Settings and insert into the list */
186 CallbackRecord
->Component
= Component
;
187 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
188 CallbackRecord
->State
= BufferInserted
;
189 CallbackRecord
->Reason
= Reason
;
190 InsertTailList(&BugcheckReasonCallbackListHead
, &CallbackRecord
->Entry
);
195 /* Lower IRQL and return */
196 KeLowerIrql(OldIrql
);
202 KeGetBugMessageText(ULONG BugCheckCode
, PANSI_STRING OutputString
)
206 ULONG_PTR MessageEntry
;
209 /* Find the message. This code is based on RtlFindMesssage -- Alex */
210 for (i
= 0; i
< KiBugCodeMessages
->NumberOfBlocks
; i
++)
212 /* Check if the ID Matches */
213 if ((BugCheckCode
>= KiBugCodeMessages
->Blocks
[i
].LowId
) &&
214 (BugCheckCode
<= KiBugCodeMessages
->Blocks
[i
].HighId
))
216 /* Get Offset to Entry */
217 MessageEntry
= (ULONG_PTR
)KiBugCodeMessages
+ KiBugCodeMessages
->Blocks
[i
].OffsetToEntries
;
218 IdOffset
= BugCheckCode
- KiBugCodeMessages
->Blocks
[i
].LowId
;
220 /* Get offset to ID */
221 for (i
= 0; i
< IdOffset
; i
++)
223 /* Advance in the Entries */
224 MessageEntry
+= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Length
;
227 /* Get the final Code */
228 BugCode
= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Text
;
230 /* Return it in the OutputString */
233 OutputString
->Buffer
= BugCode
;
234 OutputString
->Length
= strlen(BugCode
) + 1;
235 OutputString
->MaximumLength
= strlen(BugCode
) + 1;
239 /* Direct Output to Screen */
241 sprintf(BugString
, "%s\n", BugCode
);
242 InbvDisplayString(BugString
);
251 KiDoBugCheckCallbacks(VOID
)
253 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
254 PLIST_ENTRY ListHead
;
256 /* FIXME: Check Checksum and add support for WithReason Callbacks */
258 /* First make sure that the list is Initialized... it might not be */
259 ListHead
= &BugcheckCallbackListHead
;
260 if (ListHead
->Flink
&& ListHead
->Blink
) {
263 LIST_FOR_EACH(CurrentRecord
, ListHead
, KBUGCHECK_CALLBACK_RECORD
, Entry
)
265 /* Make sure it's inserted */
266 if (CurrentRecord
->State
== BufferInserted
) {
268 /* Call the routine */
269 CurrentRecord
->State
= BufferStarted
;
270 (CurrentRecord
->CallbackRoutine
)(CurrentRecord
->Buffer
,
271 CurrentRecord
->Length
);
272 CurrentRecord
->State
= BufferFinished
;
280 KeBugCheckWithTf(ULONG BugCheckCode
,
281 ULONG BugCheckParameter1
,
282 ULONG BugCheckParameter2
,
283 ULONG BugCheckParameter3
,
284 ULONG BugCheckParameter4
,
288 BOOLEAN GotExtendedCrashInfo
= FALSE
;
290 PLDR_DATA_TABLE_ENTRY CurrentModule
= NULL
;
291 extern LIST_ENTRY ModuleListHead
;
293 CHAR PrintString
[100];
295 /* Make sure we're switching back to the blue screen and print messages on it */
296 HalReleaseDisplayOwnership();
297 if (!KdpDebugMode
.Screen
)
299 /* Enable screen debug mode */
300 KdpDebugMode
.Screen
= TRUE
;
301 InitRoutines
[0](&DispatchTable
[0], 0);
304 /* Try to find out who did this. For this, we need a Trap Frame.
305 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
306 * info so it eventually needs to be supported.
310 /* For now, get Address from EIP */
311 Address
= (PVOID
)Tf
->Eip
;
313 /* Try to get information on the module */
314 LIST_FOR_EACH(CurrentModule
, &ModuleListHead
, LDR_DATA_TABLE_ENTRY
, InLoadOrderModuleList
)
316 /* Check if this is the right one */
317 if ((Address
!= NULL
&& (Address
>= (PVOID
)CurrentModule
->DllBase
&&
318 Address
< (PVOID
)((ULONG_PTR
)CurrentModule
->DllBase
+ CurrentModule
->SizeOfImage
))))
321 GotExtendedCrashInfo
= TRUE
;
327 /* Raise IRQL to HIGH_LEVEL */
328 Ke386DisableInterrupts();
329 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
331 /* Unload the Kernel Adress Space if we own it */
332 if (MmGetKernelAddressSpace()->Lock
.Owner
== KeGetCurrentThread())
333 MmUnlockAddressSpace(MmGetKernelAddressSpace());
335 /* FIXMEs: Use inbv to clear, fill and write to screen. */
337 /* Show the STOP Message */
339 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
340 "damage to your computer.\n\n");
342 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
343 "damage to your computer.\n\n");
345 /* Show the module name of who caused this */
346 if (GotExtendedCrashInfo
)
350 "The problem seems to be caused by the following file: %wZ\n\n",
351 &CurrentModule
->BaseDllName
);
352 InbvDisplayString(PrintString
);
354 DbgPrint("The problem seems to be caused by the following file: %wZ\n\n",
355 &CurrentModule
->BaseDllName
);
359 /* Find the Bug Code String */
360 KeGetBugMessageText(BugCheckCode
, NULL
);
362 /* Show the techincal Data */
365 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
367 (PVOID
)BugCheckParameter1
,
368 (PVOID
)BugCheckParameter2
,
369 (PVOID
)BugCheckParameter3
,
370 (PVOID
)BugCheckParameter4
);
371 InbvDisplayString(PrintString
);
373 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
375 (PVOID
)BugCheckParameter1
,
376 (PVOID
)BugCheckParameter2
,
377 (PVOID
)BugCheckParameter3
,
378 (PVOID
)BugCheckParameter4
);
380 /* Show the module name and more data of who caused this */
381 if (GotExtendedCrashInfo
)
385 "*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
386 &CurrentModule
->BaseDllName
,
388 (PVOID
)CurrentModule
->DllBase
,
390 InbvDisplayString(PrintString
);
392 DbgPrint("*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
393 &CurrentModule
->BaseDllName
,
395 (PVOID
)CurrentModule
->DllBase
,
400 /* There can only be one Bugcheck per Bootup */
401 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
))
405 /* Freeze the other CPUs */
406 for (i
= 0; i
< KeNumberProcessors
; i
++)
408 if (i
!= (LONG
)KeGetCurrentProcessorNumber())
410 /* Send the IPI and give them one second to catch up */
411 KiIpiSendRequest(1 << i
, IPI_FREEZE
);
412 KeStallExecutionProcessor(1000000);
416 /* Check if we got a Trap Frame */
420 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
424 /* We can only dump the frames */
425 #if defined(__GNUC__)
426 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
427 #elif defined(_MSC_VER)
429 __asm call KeDumpStackFrames
432 #error Unknown compiler for inline assembler
436 /* Call the Callbacks */;
437 KiDoBugCheckCallbacks();
439 /* Dump the BSOD to the Paging File */
440 MmDumpToPagingFile(BugCheckCode
,
447 /* Wake up the Debugger */
448 if (KdDebuggerEnabled
)
450 Ke386EnableInterrupts();
451 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
452 Ke386DisableInterrupts();
456 /* Halt this CPU now */
457 for (;;) Ke386HaltProcessor();
463 * FUNCTION: Brings the system down in a controlled manner when an
464 * inconsistency that might otherwise cause corruption has been detected
466 * BugCheckCode = Specifies the reason for the bug check
467 * BugCheckParameter[1-4] = Additional information about bug
472 KeBugCheckEx(ULONG BugCheckCode
,
473 ULONG BugCheckParameter1
,
474 ULONG BugCheckParameter2
,
475 ULONG BugCheckParameter3
,
476 ULONG BugCheckParameter4
)
478 /* Call the Trap Frame version without a Trap Frame */
479 KeBugCheckWithTf(BugCheckCode
,
490 * FUNCTION: Brings the system down in a controlled manner when an
491 * inconsistency that might otherwise cause corruption has been detected
493 * BugCheckCode = Specifies the reason for the bug check
498 KeBugCheck(ULONG BugCheckCode
)
500 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);