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 DPRINT("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 DPRINT("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 */
240 sprintf(BugString
, "%s\n", BugCode
);
241 InbvDisplayString(BugString
);
250 KiDoBugCheckCallbacks(VOID
)
252 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
253 PLIST_ENTRY ListHead
;
254 PLIST_ENTRY NextEntry
;
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 NextEntry
= ListHead
->Flink
;
264 while (NextEntry
!= ListHead
) {
266 /* Get the Callback Record */
267 CurrentRecord
= CONTAINING_RECORD(NextEntry
,
268 KBUGCHECK_CALLBACK_RECORD
,
271 /* Make sure it's inserted */
272 if (CurrentRecord
->State
== BufferInserted
) {
274 /* Call the routine */
275 CurrentRecord
->State
= BufferStarted
;
276 (CurrentRecord
->CallbackRoutine
)(CurrentRecord
->Buffer
,
277 CurrentRecord
->Length
);
278 CurrentRecord
->State
= BufferFinished
;
281 /* Move to next Entry */
282 NextEntry
= NextEntry
->Flink
;
289 KeBugCheckWithTf(ULONG BugCheckCode
,
290 ULONG BugCheckParameter1
,
291 ULONG BugCheckParameter2
,
292 ULONG BugCheckParameter3
,
293 ULONG BugCheckParameter4
,
297 BOOLEAN GotExtendedCrashInfo
= FALSE
;
299 PLIST_ENTRY CurrentEntry
;
300 MODULE_TEXT_SECTION
* CurrentSection
= NULL
;
301 extern LIST_ENTRY ModuleTextListHead
;
302 CHAR PrintString
[100];
304 /* Make sure we're switching back to the blue screen and print messages on it */
305 HalReleaseDisplayOwnership();
306 KdpDebugMode
.Screen
= TRUE
;
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 CurrentEntry
= ModuleTextListHead
.Flink
;
319 while (CurrentEntry
!= &ModuleTextListHead
&& CurrentEntry
)
321 /* Get the current Section */
322 CurrentSection
= CONTAINING_RECORD(CurrentEntry
,
326 /* Check if this is the right one */
327 if ((Address
!= NULL
&& (Address
>= (PVOID
)CurrentSection
->Base
&&
328 Address
< (PVOID
)(CurrentSection
->Base
+ CurrentSection
->Length
))))
331 GotExtendedCrashInfo
= TRUE
;
336 CurrentEntry
= CurrentEntry
->Flink
;
340 /* Raise IRQL to HIGH_LEVEL */
341 Ke386DisableInterrupts();
342 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
344 /* Unload the Kernel Adress Space if we own it */
345 if (MmGetKernelAddressSpace()->Lock
.Owner
== KeGetCurrentThread())
346 MmUnlockAddressSpace(MmGetKernelAddressSpace());
348 /* FIXMEs: Use inbv to clear, fill and write to screen. */
350 /* Show the STOP Message */
351 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
352 "damage to your computer.\n\n");
354 /* Show the module name of who caused this */
355 if (GotExtendedCrashInfo
)
358 "The problem seems to be caused by the following file: %S\n\n",
359 CurrentSection
->Name
);
360 InbvDisplayString(PrintString
);
363 /* Find the Bug Code String */
364 KeGetBugMessageText(BugCheckCode
, NULL
);
366 /* Show the techincal Data */
368 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
370 (PVOID
)BugCheckParameter1
,
371 (PVOID
)BugCheckParameter2
,
372 (PVOID
)BugCheckParameter3
,
373 (PVOID
)BugCheckParameter4
);
374 InbvDisplayString(PrintString
);
376 /* Show the module name and more data of who caused this */
377 if (GotExtendedCrashInfo
)
380 "*** %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
381 CurrentSection
->Name
,
383 (PVOID
)CurrentSection
->Base
,
385 InbvDisplayString(PrintString
);
388 /* There can only be one Bugcheck per Bootup */
389 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
))
393 /* Freeze the other CPUs */
394 for (i
= 0; i
< KeNumberProcessors
; i
++)
396 if (i
!= KeGetCurrentProcessorNumber())
398 /* Send the IPI and give them one second to catch up */
399 KiIpiSendRequest(1 << i
, IPI_REQUEST_FREEZE
);
400 KeStallExecutionProcessor(1000000);
404 /* Check if we got a Trap Frame */
408 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
412 /* We can only dump the frames */
413 #if defined(__GNUC__)
414 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
415 #elif defined(_MSC_VER)
417 __asm call KeDumpStackFrames
420 #error Unknown compiler for inline assembler
424 /* Call the Callbacks */;
425 KiDoBugCheckCallbacks();
427 /* Dump the BSOD to the Paging File */
428 MmDumpToPagingFile(BugCheckCode
,
435 /* Wake up the Debugger */
436 if (KdDebuggerEnabled
)
438 Ke386EnableInterrupts();
439 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
440 Ke386DisableInterrupts();
444 /* Halt this CPU now */
445 for (;;) Ke386HaltProcessor();
451 * FUNCTION: Brings the system down in a controlled manner when an
452 * inconsistency that might otherwise cause corruption has been detected
454 * BugCheckCode = Specifies the reason for the bug check
455 * BugCheckParameter[1-4] = Additional information about bug
460 KeBugCheckEx(ULONG BugCheckCode
,
461 ULONG BugCheckParameter1
,
462 ULONG BugCheckParameter2
,
463 ULONG BugCheckParameter3
,
464 ULONG BugCheckParameter4
)
466 /* Call the Trap Frame version without a Trap Frame */
467 KeBugCheckWithTf(BugCheckCode
,
478 * FUNCTION: Brings the system down in a controlled manner when an
479 * inconsistency that might otherwise cause corruption has been detected
481 * BugCheckCode = Specifies the reason for the bug check
486 KeBugCheck(ULONG BugCheckCode
)
488 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);