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
;
39 static ULONG KeBugCheckCount
= 1;
42 /* FUNCTIONS *****************************************************************/
47 KiInitializeBugCheck(VOID
)
49 PRTL_MESSAGE_RESOURCE_DATA BugCheckData
;
50 LDR_RESOURCE_INFO ResourceInfo
;
51 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
54 /* Initialize Callbadk Listhead and State */
55 InitializeListHead(&BugcheckCallbackListHead
);
56 InitializeListHead(&BugcheckReasonCallbackListHead
);
59 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
60 ResourceInfo
.Type
= 11;
61 ResourceInfo
.Name
= 1;
62 ResourceInfo
.Language
= 9;
65 Status
= LdrFindResource_U((PVOID
)KERNEL_BASE
,
70 /* Make sure it worked */
71 if (NT_SUCCESS(Status
)) {
73 DPRINT("Found Bugcheck Resource Data!\n");
75 /* Now actually get a pointer to it */
76 Status
= LdrAccessResource((PVOID
)KERNEL_BASE
,
78 (PVOID
*)&BugCheckData
,
81 /* Make sure it worked */
82 if (NT_SUCCESS(Status
)) {
84 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
85 KiBugCodeMessages
= BugCheckData
;
95 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
)
98 BOOLEAN Status
= FALSE
;
100 /* Raise IRQL to High */
101 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
103 /* Check the Current State */
104 if (CallbackRecord
->State
== BufferInserted
) {
106 /* Reset state and remove from list */
107 CallbackRecord
->State
= BufferEmpty
;
108 RemoveEntryList(&CallbackRecord
->Entry
);
113 /* Lower IRQL and return */
114 KeLowerIrql(OldIrql
);
123 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
126 BOOLEAN Status
= FALSE
;
128 /* Raise IRQL to High */
129 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
131 /* Check the Current State */
132 if (CallbackRecord
->State
== BufferInserted
) {
134 /* Reset state and remove from list */
135 CallbackRecord
->State
= BufferEmpty
;
136 RemoveEntryList(&CallbackRecord
->Entry
);
141 /* Lower IRQL and return */
142 KeLowerIrql(OldIrql
);
151 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord
,
152 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine
,
158 BOOLEAN Status
= FALSE
;
160 /* Raise IRQL to High */
161 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
163 /* Check the Current State first so we don't double-register */
164 if (CallbackRecord
->State
== BufferEmpty
) {
166 /* Set the Callback Settings and insert into the list */
167 CallbackRecord
->Length
= Length
;
168 CallbackRecord
->Buffer
= Buffer
;
169 CallbackRecord
->Component
= Component
;
170 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
171 CallbackRecord
->State
= BufferInserted
;
172 InsertTailList(&BugcheckCallbackListHead
, &CallbackRecord
->Entry
);
177 /* Lower IRQL and return */
178 KeLowerIrql(OldIrql
);
187 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
,
188 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine
,
189 IN KBUGCHECK_CALLBACK_REASON Reason
,
193 BOOLEAN Status
= FALSE
;
195 /* Raise IRQL to High */
196 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
198 /* Check the Current State first so we don't double-register */
199 if (CallbackRecord
->State
== BufferEmpty
) {
201 /* Set the Callback Settings and insert into the list */
202 CallbackRecord
->Component
= Component
;
203 CallbackRecord
->CallbackRoutine
= CallbackRoutine
;
204 CallbackRecord
->State
= BufferInserted
;
205 CallbackRecord
->Reason
= Reason
;
206 InsertTailList(&BugcheckReasonCallbackListHead
, &CallbackRecord
->Entry
);
211 /* Lower IRQL and return */
212 KeLowerIrql(OldIrql
);
218 KeGetBugMessageText(ULONG BugCheckCode
, PANSI_STRING OutputString
)
222 ULONG_PTR MessageEntry
;
225 /* Find the message. This code is based on RtlFindMesssage -- Alex */
226 for (i
= 0; i
< KiBugCodeMessages
->NumberOfBlocks
; i
++)
228 /* Check if the ID Matches */
229 if ((BugCheckCode
>= KiBugCodeMessages
->Blocks
[i
].LowId
) &&
230 (BugCheckCode
<= KiBugCodeMessages
->Blocks
[i
].HighId
))
232 /* Get Offset to Entry */
233 MessageEntry
= (ULONG_PTR
)KiBugCodeMessages
+ KiBugCodeMessages
->Blocks
[i
].OffsetToEntries
;
234 IdOffset
= BugCheckCode
- KiBugCodeMessages
->Blocks
[i
].LowId
;
236 /* Get offset to ID */
237 for (i
= 0; i
< IdOffset
; i
++)
239 /* Advance in the Entries */
240 MessageEntry
+= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Length
;
243 /* Get the final Code */
244 BugCode
= ((PRTL_MESSAGE_RESOURCE_ENTRY
)MessageEntry
)->Text
;
246 /* Return it in the OutputString */
249 OutputString
->Buffer
= BugCode
;
250 OutputString
->Length
= strlen(BugCode
) + 1;
251 OutputString
->MaximumLength
= strlen(BugCode
) + 1;
255 /* Direct Output to Screen */
257 sprintf(BugString
, "%s\n", BugCode
);
258 InbvDisplayString(BugString
);
267 KiDoBugCheckCallbacks(VOID
)
269 PKBUGCHECK_CALLBACK_RECORD CurrentRecord
;
270 PLIST_ENTRY ListHead
;
272 /* FIXME: Check Checksum and add support for WithReason Callbacks */
274 /* First make sure that the list is Initialized... it might not be */
275 ListHead
= &BugcheckCallbackListHead
;
276 if (ListHead
->Flink
&& ListHead
->Blink
) {
279 LIST_FOR_EACH(CurrentRecord
, ListHead
, KBUGCHECK_CALLBACK_RECORD
, Entry
)
281 /* Make sure it's inserted */
282 if (CurrentRecord
->State
== BufferInserted
) {
284 /* Call the routine */
285 CurrentRecord
->State
= BufferStarted
;
286 (CurrentRecord
->CallbackRoutine
)(CurrentRecord
->Buffer
,
287 CurrentRecord
->Length
);
288 CurrentRecord
->State
= BufferFinished
;
296 KeBugCheckWithTf(ULONG BugCheckCode
,
297 ULONG BugCheckParameter1
,
298 ULONG BugCheckParameter2
,
299 ULONG BugCheckParameter3
,
300 ULONG BugCheckParameter4
,
305 BOOLEAN GotExtendedCrashInfo
= FALSE
;
307 PLDR_DATA_TABLE_ENTRY CurrentModule
= NULL
;
308 extern LIST_ENTRY ModuleListHead
;
310 CHAR PrintString
[100];
312 /* Make sure we're switching back to the blue screen and print messages on it */
313 HalReleaseDisplayOwnership();
314 if (!KdpDebugMode
.Screen
)
316 /* Enable screen debug mode */
317 KdpDebugMode
.Screen
= TRUE
;
318 InitRoutines
[0](&DispatchTable
[0], 0);
321 /* Try to find out who did this. For this, we need a Trap Frame.
322 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
323 * info so it eventually needs to be supported.
327 /* For now, get Address from EIP */
328 Address
= (PVOID
)Tf
->Eip
;
330 /* Try to get information on the module */
331 LIST_FOR_EACH(CurrentModule
, &ModuleListHead
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
)
333 /* Check if this is the right one */
334 if ((Address
!= NULL
&& (Address
>= (PVOID
)CurrentModule
->DllBase
&&
335 Address
< (PVOID
)((ULONG_PTR
)CurrentModule
->DllBase
+ CurrentModule
->SizeOfImage
))))
338 GotExtendedCrashInfo
= TRUE
;
344 /* Raise IRQL to HIGH_LEVEL */
345 Ke386DisableInterrupts();
346 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
348 /* Unload the Kernel Adress Space if we own it */
349 if (KernelAddressSpaceLock
.Owner
== KeGetCurrentThread())
350 MmUnlockAddressSpace(MmGetKernelAddressSpace());
352 /* FIXMEs: Use inbv to clear, fill and write to screen. */
354 /* Show the STOP Message */
356 InbvDisplayString("A problem has been detected and ReactOS has been shut down to prevent "
357 "damage to your computer.\n\n");
359 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
360 "damage to your computer.\n\n");
362 /* Show the module name of who caused this */
363 if (GotExtendedCrashInfo
)
367 "The problem seems to be caused by the following file: %wZ\n\n",
368 &CurrentModule
->BaseDllName
);
369 InbvDisplayString(PrintString
);
371 DbgPrint("The problem seems to be caused by the following file: %wZ\n\n",
372 &CurrentModule
->BaseDllName
);
376 /* Find the Bug Code String */
377 KeGetBugMessageText(BugCheckCode
, NULL
);
379 /* Show the techincal Data */
382 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
384 (PVOID
)BugCheckParameter1
,
385 (PVOID
)BugCheckParameter2
,
386 (PVOID
)BugCheckParameter3
,
387 (PVOID
)BugCheckParameter4
);
388 InbvDisplayString(PrintString
);
390 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
392 (PVOID
)BugCheckParameter1
,
393 (PVOID
)BugCheckParameter2
,
394 (PVOID
)BugCheckParameter3
,
395 (PVOID
)BugCheckParameter4
);
397 /* Show the module name and more data of who caused this */
398 if (GotExtendedCrashInfo
)
402 "*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
403 &CurrentModule
->BaseDllName
,
405 (PVOID
)CurrentModule
->DllBase
,
407 InbvDisplayString(PrintString
);
409 DbgPrint("*** %wZ - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
410 &CurrentModule
->BaseDllName
,
412 (PVOID
)CurrentModule
->DllBase
,
417 /* There can only be one Bugcheck per Bootup */
418 if (!InterlockedDecrement((PLONG
)&KeBugCheckCount
))
422 /* Freeze the other CPUs */
423 for (i
= 0; i
< KeNumberProcessors
; i
++)
425 if (i
!= (LONG
)KeGetCurrentProcessorNumber())
427 /* Send the IPI and give them one second to catch up */
428 KiIpiSendRequest(1 << i
, IPI_FREEZE
);
429 KeStallExecutionProcessor(1000000);
433 /* Check if we got a Trap Frame */
437 KiDumpTrapFrame(Tf
, BugCheckParameter1
, BugCheckParameter2
);
441 /* We can only dump the frames */
442 #if defined(__GNUC__)
443 KeDumpStackFrames((PULONG
)__builtin_frame_address(0));
444 #elif defined(_MSC_VER)
446 __asm call KeDumpStackFrames
449 #error Unknown compiler for inline assembler
453 /* Call the Callbacks */;
454 KiDoBugCheckCallbacks();
456 /* Dump the BSOD to the Paging File */
457 MmDumpToPagingFile(BugCheckCode
,
464 /* Wake up the Debugger */
465 if (KdDebuggerEnabled
)
467 Ke386EnableInterrupts();
468 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND
);
469 Ke386DisableInterrupts();
473 /* Halt this CPU now */
474 for (;;) Ke386HaltProcessor();
475 #elif defined(_M_PPC)
483 * FUNCTION: Brings the system down in a controlled manner when an
484 * inconsistency that might otherwise cause corruption has been detected
486 * BugCheckCode = Specifies the reason for the bug check
487 * BugCheckParameter[1-4] = Additional information about bug
492 KeBugCheckEx(ULONG BugCheckCode
,
493 ULONG BugCheckParameter1
,
494 ULONG BugCheckParameter2
,
495 ULONG BugCheckParameter3
,
496 ULONG BugCheckParameter4
)
498 /* Call the Trap Frame version without a Trap Frame */
499 KeBugCheckWithTf(BugCheckCode
,
510 * FUNCTION: Brings the system down in a controlled manner when an
511 * inconsistency that might otherwise cause corruption has been detected
513 * BugCheckCode = Specifies the reason for the bug check
518 KeBugCheck(ULONG BugCheckCode
)
520 KeBugCheckEx(BugCheckCode
, 0, 0, 0, 0);