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 /* ROS Internal. Please deprecate */
26 HalReleaseDisplayOwnership(
30 extern FAST_MUTEX KernelAddressSpaceLock
;
32 /* GLOBALS ******************************************************************/
34 static LIST_ENTRY BugcheckCallbackListHead
= {NULL
,NULL
};
35 static LIST_ENTRY BugcheckReasonCallbackListHead
= {NULL
,NULL
};
36 static ULONG InBugCheck
;
37 static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages
;
38 static ULONG KeBugCheckCount
= 1;
40 /* FUNCTIONS *****************************************************************/
45 KiInitializeBugCheck(VOID
)
47 PRTL_MESSAGE_RESOURCE_DATA BugCheckData
;
48 LDR_RESOURCE_INFO ResourceInfo
;
49 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
52 /* Initialize Callbadk Listhead and State */
53 InitializeListHead(&BugcheckCallbackListHead
);
54 InitializeListHead(&BugcheckReasonCallbackListHead
);
57 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
58 ResourceInfo
.Type
= 11;
59 ResourceInfo
.Name
= 1;
60 ResourceInfo
.Language
= 9;
63 Status
= LdrFindResource_U((PVOID
)KERNEL_BASE
,
68 /* Make sure it worked */
69 if (NT_SUCCESS(Status
)) {
71 DPRINT("Found Bugcheck Resource Data!\n");
73 /* Now actually get a pointer to it */
74 Status
= LdrAccessResource((PVOID
)KERNEL_BASE
,
76 (PVOID
*)&BugCheckData
,
79 /* Make sure it worked */
80 if (NT_SUCCESS(Status
)) {
82 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
83 KiBugCodeMessages
= BugCheckData
;
93 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
)
96 BOOLEAN Status
= FALSE
;
98 /* Raise IRQL to High */
99 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
101 /* Check the Current State */
102 if (CallbackRecord
->State
== BufferInserted
) {
104 /* Reset state and remove from list */
105 CallbackRecord
->State
= BufferEmpty
;
106 RemoveEntryList(&CallbackRecord
->Entry
);
111 /* Lower IRQL and return */
112 KeLowerIrql(OldIrql
);
121 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
124 BOOLEAN Status
= FALSE
;
126 /* Raise IRQL to High */
127 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
129 /* Check the Current State */
130 if (CallbackRecord
->State
== BufferInserted
) {
132 /* Reset state and remove from list */
133 CallbackRecord
->State
= BufferEmpty
;
134 RemoveEntryList(&CallbackRecord
->Entry
);
139 /* Lower IRQL and return */
140 KeLowerIrql(OldIrql
);
149 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
,
150 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine
,
156 BOOLEAN Status
= FALSE
;
158 /* Raise IRQL to High */
159 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
161 /* Check the Current State first so we don't double-register */
162 if (CallbackRecord
->State
== BufferEmpty
) {
164 /* Set the Callback Settings and insert into the list */
165 CallbackRecord
->Length
= Length
;
166 CallbackRecord
->Buffer
= Buffer
;
167 CallbackRecord
->Component
= Component
;
168 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
169 CallbackRecord
->State
= BufferInserted
;
170 InsertTailList(&BugcheckCallbackListHead
, &CallbackRecord
->Entry
);
175 /* Lower IRQL and return */
176 KeLowerIrql(OldIrql
);
185 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
,
186 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine
,
187 IN KBUGCHECK_CALLBACK_REASON Reason
,
191 BOOLEAN Status
= FALSE
;
193 /* Raise IRQL to High */
194 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
196 /* Check the Current State first so we don't double-register */
197 if (CallbackRecord
->State
== BufferEmpty
) {
199 /* Set the Callback Settings and insert into the list */
200 CallbackRecord
->Component
= Component
;
201 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
202 CallbackRecord
->State
= BufferInserted
;
203 CallbackRecord
->Reason
= Reason
;
204 InsertTailList(&BugcheckReasonCallbackListHead
, &CallbackRecord
->Entry
);
209 /* Lower IRQL and return */
210 KeLowerIrql(OldIrql
);
216 KeGetBugMessageText(ULONG BugCheckCode
, PANSI_STRING OutputString
)
220 ULONG_PTR MessageEntry
;
223 /* Find the message. This code is based on RtlFindMesssage -- Alex */
224 for (i
= 0; i
< KiBugCodeMessages
->NumberOfBlocks
; i
++)
226 /* Check if the ID Matches */
227 if ((BugCheckCode
>= KiBugCodeMessages
->Blocks
[i
].LowId
) &&
228 (BugCheckCode
<= KiBugCodeMessages
->Blocks
[i
].HighId
))
230 /* Get Offset to Entry */
231 MessageEntry
= (ULONG_PTR
)KiBugCodeMessages
+ KiBugCodeMessages
->Blocks
[i
].OffsetToEntries
;
232 IdOffset
= BugCheckCode
- KiBugCodeMessages
->Blocks
[i
].LowId
;
234 /* Get offset to ID */
235 for (i
= 0; i
< IdOffset
; i
++)
237 /* Advance in the Entries */
238 MessageEntry
+= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Length
;
241 /* Get the final Code */
242 BugCode
= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Text
;
244 /* Return it in the OutputString */
247 OutputString
->Buffer
= BugCode
;
248 OutputString
->Length
= strlen(BugCode
) + 1;
249 OutputString
->MaximumLength
= strlen(BugCode
) + 1;
253 /* Direct Output to Screen */
255 sprintf(BugString
, "%s\n", BugCode
);
256 InbvDisplayString(BugString
);
265 KiDoBugCheckCallbacks(VOID
)
267 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
268 PLIST_ENTRY ListHead
;
270 /* FIXME: Check Checksum and add support for WithReason Callbacks */
272 /* First make sure that the list is Initialized... it might not be */
273 ListHead
= &BugcheckCallbackListHead
;
274 if (ListHead
->Flink
&& ListHead
->Blink
) {
277 LIST_FOR_EACH(CurrentRecord
, ListHead
, KBUGCHECK_CALLBACK_RECORD
, Entry
)
279 /* Make sure it's inserted */
280 if (CurrentRecord
->State
== BufferInserted
) {
282 /* Call the routine */
283 CurrentRecord
->State
= BufferStarted
;
284 (CurrentRecord
->CallbackRoutine
)(CurrentRecord
->Buffer
,
285 CurrentRecord
->Length
);
286 CurrentRecord
->State
= BufferFinished
;
294 KeBugCheckWithTf(ULONG BugCheckCode
,
295 ULONG BugCheckParameter1
,
296 ULONG BugCheckParameter2
,
297 ULONG BugCheckParameter3
,
298 ULONG BugCheckParameter4
,
302 BOOLEAN GotExtendedCrashInfo
= FALSE
;
304 PLDR_DATA_TABLE_ENTRY CurrentModule
= NULL
;
305 extern LIST_ENTRY ModuleListHead
;
307 CHAR PrintString
[100];
309 /* Make sure we're switching back to the blue screen and print messages on it */
310 HalReleaseDisplayOwnership();
311 if (!KdpDebugMode
.Screen
)
313 /* Enable screen debug mode */
314 KdpDebugMode
.Screen
= TRUE
;
315 InitRoutines
[0](&DispatchTable
[0], 0);
318 /* Try to find out who did this. For this, we need a Trap Frame.
319 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
320 * info so it eventually needs to be supported.
324 /* For now, get Address from EIP */
325 Address
= (PVOID
)Tf
->Eip
;
327 /* Try to get information on the module */
328 LIST_FOR_EACH(CurrentModule
, &ModuleListHead
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
)
330 /* Check if this is the right one */
331 if ((Address
!= NULL
&& (Address
>= (PVOID
)CurrentModule
->DllBase
&&
332 Address
< (PVOID
)((ULONG_PTR
)CurrentModule
->DllBase
+ CurrentModule
->SizeOfImage
))))
335 GotExtendedCrashInfo
= TRUE
;
341 /* Raise IRQL to HIGH_LEVEL */
342 Ke386DisableInterrupts();
343 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
345 /* Unload the Kernel Adress Space if we own it */
346 if (KernelAddressSpaceLock
.Owner
== KeGetCurrentThread())
347 MmUnlockAddressSpace(MmGetKernelAddressSpace());
349 /* FIXMEs: Use inbv to clear, fill and write to screen. */
351 /* Show the STOP Message */
353 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
354 "damage to your computer.\n\n");
356 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
357 "damage to your computer.\n\n");
359 /* Show the module name of who caused this */
360 if (GotExtendedCrashInfo
)
364 "The problem seems to be caused by the following file: %wZ\n\n",
365 &CurrentModule
->BaseDllName
);
366 InbvDisplayString(PrintString
);
368 DbgPrint("The problem seems to be caused by the following file: %wZ\n\n",
369 &CurrentModule
->BaseDllName
);
373 /* Find the Bug Code String */
374 KeGetBugMessageText(BugCheckCode
, NULL
);
376 /* Show the techincal Data */
379 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
381 (PVOID
)BugCheckParameter1
,
382 (PVOID
)BugCheckParameter2
,
383 (PVOID
)BugCheckParameter3
,
384 (PVOID
)BugCheckParameter4
);
385 InbvDisplayString(PrintString
);
387 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
389 (PVOID
)BugCheckParameter1
,
390 (PVOID
)BugCheckParameter2
,
391 (PVOID
)BugCheckParameter3
,
392 (PVOID
)BugCheckParameter4
);
394 /* Show the module name and more data of who caused this */
395 if (GotExtendedCrashInfo
)
399 "*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
400 &CurrentModule
->BaseDllName
,
402 (PVOID
)CurrentModule
->DllBase
,
404 InbvDisplayString(PrintString
);
406 DbgPrint("*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
407 &CurrentModule
->BaseDllName
,
409 (PVOID
)CurrentModule
->DllBase
,
414 /* There can only be one Bugcheck per Bootup */
415 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
))
419 /* Freeze the other CPUs */
420 for (i
= 0; i
< KeNumberProcessors
; i
++)
422 if (i
!= (LONG
)KeGetCurrentProcessorNumber())
424 /* Send the IPI and give them one second to catch up */
425 KiIpiSendRequest(1 << i
, IPI_FREEZE
);
426 KeStallExecutionProcessor(1000000);
430 /* Check if we got a Trap Frame */
434 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
438 /* We can only dump the frames */
439 #if defined(__GNUC__)
440 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
441 #elif defined(_MSC_VER)
443 __asm call KeDumpStackFrames
446 #error Unknown compiler for inline assembler
450 /* Call the Callbacks */;
451 KiDoBugCheckCallbacks();
453 /* Dump the BSOD to the Paging File */
454 MmDumpToPagingFile(BugCheckCode
,
461 /* Wake up the Debugger */
462 if (KdDebuggerEnabled
)
464 Ke386EnableInterrupts();
465 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
466 Ke386DisableInterrupts();
470 /* Halt this CPU now */
471 for (;;) Ke386HaltProcessor();
477 * FUNCTION: Brings the system down in a controlled manner when an
478 * inconsistency that might otherwise cause corruption has been detected
480 * BugCheckCode = Specifies the reason for the bug check
481 * BugCheckParameter[1-4] = Additional information about bug
486 KeBugCheckEx(ULONG BugCheckCode
,
487 ULONG BugCheckParameter1
,
488 ULONG BugCheckParameter2
,
489 ULONG BugCheckParameter3
,
490 ULONG BugCheckParameter4
)
492 /* Call the Trap Frame version without a Trap Frame */
493 KeBugCheckWithTf(BugCheckCode
,
504 * FUNCTION: Brings the system down in a controlled manner when an
505 * inconsistency that might otherwise cause corruption has been detected
507 * BugCheckCode = Specifies the reason for the bug check
512 KeBugCheck(ULONG BugCheckCode
)
514 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);