Add inbv header, stub InvbDisplayString, fix BSOD to always show on screen like it...
[reactos.git] / reactos / ntoskrnl / ke / bug.c
1 /*
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
6 *
7 * PROGRAMMERS: Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks.
8 * David Welch (welch@cwcom.net)
9 * Phillip Susi
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS ******************************************************************/
19
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;
25
26 /* FUNCTIONS *****************************************************************/
27
28 VOID
29 INIT_FUNCTION
30 KiInitializeBugCheck(VOID)
31 {
32 PRTL_MESSAGE_RESOURCE_DATA BugCheckData;
33 LDR_RESOURCE_INFO ResourceInfo;
34 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
35 NTSTATUS Status;
36
37 /* Initialize Callbadk Listhead and State */
38 InitializeListHead(&BugcheckCallbackListHead);
39 InitializeListHead(&BugcheckReasonCallbackListHead);
40 InBugCheck = 0;
41
42 /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
43 ResourceInfo.Type = 11;
44 ResourceInfo.Name = 1;
45 ResourceInfo.Language = 9;
46
47 /* Do the lookup. */
48 Status = LdrFindResource_U((PVOID)KERNEL_BASE,
49 &ResourceInfo,
50 RESOURCE_DATA_LEVEL,
51 &ResourceDataEntry);
52
53 /* Make sure it worked */
54 if (NT_SUCCESS(Status)) {
55
56 DPRINT("Found Bugcheck Resource Data!\n");
57
58 /* Now actually get a pointer to it */
59 Status = LdrAccessResource((PVOID)KERNEL_BASE,
60 ResourceDataEntry,
61 (PVOID*)&BugCheckData,
62 NULL);
63
64 /* Make sure it worked */
65 if (NT_SUCCESS(Status)) {
66
67 DPRINT("Got Pointer to Bugcheck Resource Data!\n");
68 KiBugCodeMessages = BugCheckData;
69 }
70 }
71 }
72
73 /*
74 * @implemented
75 */
76 BOOLEAN
77 STDCALL
78 KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
79 {
80 KIRQL OldIrql;
81 BOOLEAN Status = FALSE;
82
83 /* Raise IRQL to High */
84 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
85
86 /* Check the Current State */
87 if (CallbackRecord->State == BufferInserted) {
88
89 /* Reset state and remove from list */
90 CallbackRecord->State = BufferEmpty;
91 RemoveEntryList(&CallbackRecord->Entry);
92
93 Status = TRUE;
94 }
95
96 /* Lower IRQL and return */
97 KeLowerIrql(OldIrql);
98 return Status;
99 }
100
101 /*
102 * @implemented
103 */
104 BOOLEAN
105 STDCALL
106 KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
107 {
108 KIRQL OldIrql;
109 BOOLEAN Status = FALSE;
110
111 /* Raise IRQL to High */
112 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
113
114 /* Check the Current State */
115 if (CallbackRecord->State == BufferInserted) {
116
117 /* Reset state and remove from list */
118 CallbackRecord->State = BufferEmpty;
119 RemoveEntryList(&CallbackRecord->Entry);
120
121 Status = TRUE;
122 }
123
124 /* Lower IRQL and return */
125 KeLowerIrql(OldIrql);
126 return Status;
127 }
128
129 /*
130 * @implemented
131 */
132 BOOLEAN
133 STDCALL
134 KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
135 PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
136 PVOID Buffer,
137 ULONG Length,
138 PUCHAR Component)
139 {
140 KIRQL OldIrql;
141 BOOLEAN Status = FALSE;
142
143 /* Raise IRQL to High */
144 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
145
146 /* Check the Current State first so we don't double-register */
147 if (CallbackRecord->State == BufferEmpty) {
148
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);
156
157 Status = TRUE;
158 }
159
160 /* Lower IRQL and return */
161 KeLowerIrql(OldIrql);
162 return Status;
163 }
164
165 /*
166 * @implemented
167 */
168 BOOLEAN
169 STDCALL
170 KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
171 IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
172 IN KBUGCHECK_CALLBACK_REASON Reason,
173 IN PUCHAR Component)
174 {
175 KIRQL OldIrql;
176 BOOLEAN Status = FALSE;
177
178 /* Raise IRQL to High */
179 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
180
181 /* Check the Current State first so we don't double-register */
182 if (CallbackRecord->State == BufferEmpty) {
183
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);
190
191 Status = TRUE;
192 }
193
194 /* Lower IRQL and return */
195 KeLowerIrql(OldIrql);
196 return Status;
197 }
198
199 VOID
200 STDCALL
201 KeGetBugMessageText(ULONG BugCheckCode, PANSI_STRING OutputString)
202 {
203 ULONG i;
204 ULONG IdOffset;
205 ULONG_PTR MessageEntry;
206 PCHAR BugCode;
207
208 /* Find the message. This code is based on RtlFindMesssage -- Alex */
209 for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
210 {
211 /* Check if the ID Matches */
212 if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
213 (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
214 {
215 /* Get Offset to Entry */
216 MessageEntry = (ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries;
217 IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
218
219 /* Get offset to ID */
220 for (i = 0; i < IdOffset; i++)
221 {
222 /* Advance in the Entries */
223 MessageEntry += ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Length;
224 }
225
226 /* Get the final Code */
227 BugCode = ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Text;
228
229 /* Return it in the OutputString */
230 if (OutputString)
231 {
232 OutputString->Buffer = BugCode;
233 OutputString->Length = strlen(BugCode) + 1;
234 OutputString->MaximumLength = strlen(BugCode) + 1;
235 }
236 else
237 {
238 /* Direct Output to Screen */
239 CHAR BugString[100];
240 sprintf(BugString, "%s\n", BugCode);
241 InbvDisplayString(BugString);
242 break;
243 }
244 }
245 }
246 }
247
248 VOID
249 STDCALL
250 KiDoBugCheckCallbacks(VOID)
251 {
252 PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
253 PLIST_ENTRY ListHead;
254 PLIST_ENTRY NextEntry;
255
256 /* FIXME: Check Checksum and add support for WithReason Callbacks */
257
258 /* First make sure that the list is Initialized... it might not be */
259 ListHead = &BugcheckCallbackListHead;
260 if (ListHead->Flink && ListHead->Blink) {
261
262 /* Loop the list */
263 NextEntry = ListHead->Flink;
264 while (NextEntry != ListHead) {
265
266 /* Get the Callback Record */
267 CurrentRecord = CONTAINING_RECORD(NextEntry,
268 KBUGCHECK_CALLBACK_RECORD,
269 Entry);
270
271 /* Make sure it's inserted */
272 if (CurrentRecord->State == BufferInserted) {
273
274 /* Call the routine */
275 CurrentRecord->State = BufferStarted;
276 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
277 CurrentRecord->Length);
278 CurrentRecord->State = BufferFinished;
279 }
280
281 /* Move to next Entry */
282 NextEntry = NextEntry->Flink;
283 }
284 }
285 }
286
287 VOID
288 STDCALL
289 KeBugCheckWithTf(ULONG BugCheckCode,
290 ULONG BugCheckParameter1,
291 ULONG BugCheckParameter2,
292 ULONG BugCheckParameter3,
293 ULONG BugCheckParameter4,
294 PKTRAP_FRAME Tf)
295 {
296 KIRQL OldIrql;
297 BOOLEAN GotExtendedCrashInfo = FALSE;
298 PVOID Address = 0;
299 PLIST_ENTRY CurrentEntry;
300 MODULE_TEXT_SECTION* CurrentSection = NULL;
301 extern LIST_ENTRY ModuleTextListHead;
302 CHAR PrintString[100];
303
304 /* Make sure we're switching back to the blue screen and print messages on it */
305 HalReleaseDisplayOwnership();
306 KdpDebugMode.Screen = TRUE;
307
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.
311 */
312 if (Tf)
313 {
314 /* For now, get Address from EIP */
315 Address = (PVOID)Tf->Eip;
316
317 /* Try to get information on the module */
318 CurrentEntry = ModuleTextListHead.Flink;
319 while (CurrentEntry != &ModuleTextListHead && CurrentEntry)
320 {
321 /* Get the current Section */
322 CurrentSection = CONTAINING_RECORD(CurrentEntry,
323 MODULE_TEXT_SECTION,
324 ListEntry);
325
326 /* Check if this is the right one */
327 if ((Address != NULL && (Address >= (PVOID)CurrentSection->Base &&
328 Address < (PVOID)(CurrentSection->Base + CurrentSection->Length))))
329 {
330 /* We got it */
331 GotExtendedCrashInfo = TRUE;
332 break;
333 }
334
335 /* Loop again */
336 CurrentEntry = CurrentEntry->Flink;
337 }
338 }
339
340 /* Raise IRQL to HIGH_LEVEL */
341 Ke386DisableInterrupts();
342 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
343
344 /* Unload the Kernel Adress Space if we own it */
345 if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread())
346 MmUnlockAddressSpace(MmGetKernelAddressSpace());
347
348 /* FIXMEs: Use inbv to clear, fill and write to screen. */
349
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");
353
354 /* Show the module name of who caused this */
355 if (GotExtendedCrashInfo)
356 {
357 sprintf(PrintString,
358 "The problem seems to be caused by the following file: %S\n\n",
359 CurrentSection->Name);
360 InbvDisplayString(PrintString);
361 }
362
363 /* Find the Bug Code String */
364 KeGetBugMessageText(BugCheckCode, NULL);
365
366 /* Show the techincal Data */
367 sprintf(PrintString,
368 "Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
369 BugCheckCode,
370 (PVOID)BugCheckParameter1,
371 (PVOID)BugCheckParameter2,
372 (PVOID)BugCheckParameter3,
373 (PVOID)BugCheckParameter4);
374 InbvDisplayString(PrintString);
375
376 /* Show the module name and more data of who caused this */
377 if (GotExtendedCrashInfo)
378 {
379 sprintf(PrintString,
380 "*** %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
381 CurrentSection->Name,
382 Address,
383 (PVOID)CurrentSection->Base,
384 0);
385 InbvDisplayString(PrintString);
386 }
387
388 /* There can only be one Bugcheck per Bootup */
389 if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
390 {
391 #ifdef CONFIG_SMP
392 ULONG i;
393 /* Freeze the other CPUs */
394 for (i = 0; i < KeNumberProcessors; i++)
395 {
396 if (i != KeGetCurrentProcessorNumber())
397 {
398 /* Send the IPI and give them one second to catch up */
399 KiIpiSendRequest(1 << i, IPI_REQUEST_FREEZE);
400 KeStallExecutionProcessor(1000000);
401 }
402 }
403 #endif
404 /* Check if we got a Trap Frame */
405 if (Tf)
406 {
407 /* Dump it */
408 KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
409 }
410 else
411 {
412 /* We can only dump the frames */
413 #if defined(__GNUC__)
414 KeDumpStackFrames((PULONG)__builtin_frame_address(0));
415 #elif defined(_MSC_VER)
416 __asm push ebp
417 __asm call KeDumpStackFrames
418 __asm add esp, 4
419 #else
420 #error Unknown compiler for inline assembler
421 #endif
422 }
423
424 /* Call the Callbacks */;
425 KiDoBugCheckCallbacks();
426
427 /* Dump the BSOD to the Paging File */
428 MmDumpToPagingFile(BugCheckCode,
429 BugCheckParameter1,
430 BugCheckParameter2,
431 BugCheckParameter3,
432 BugCheckParameter4,
433 Tf);
434
435 /* Wake up the Debugger */
436 if (KdDebuggerEnabled)
437 {
438 Ke386EnableInterrupts();
439 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
440 Ke386DisableInterrupts();
441 }
442 }
443
444 /* Halt this CPU now */
445 for (;;) Ke386HaltProcessor();
446 }
447
448 /*
449 * @implemented
450 *
451 * FUNCTION: Brings the system down in a controlled manner when an
452 * inconsistency that might otherwise cause corruption has been detected
453 * ARGUMENTS:
454 * BugCheckCode = Specifies the reason for the bug check
455 * BugCheckParameter[1-4] = Additional information about bug
456 * RETURNS: Doesn't
457 */
458 VOID
459 STDCALL
460 KeBugCheckEx(ULONG BugCheckCode,
461 ULONG BugCheckParameter1,
462 ULONG BugCheckParameter2,
463 ULONG BugCheckParameter3,
464 ULONG BugCheckParameter4)
465 {
466 /* Call the Trap Frame version without a Trap Frame */
467 KeBugCheckWithTf(BugCheckCode,
468 BugCheckParameter1,
469 BugCheckParameter2,
470 BugCheckParameter3,
471 BugCheckParameter4,
472 NULL);
473 }
474
475 /*
476 * @implemented
477 *
478 * FUNCTION: Brings the system down in a controlled manner when an
479 * inconsistency that might otherwise cause corruption has been detected
480 * ARGUMENTS:
481 * BugCheckCode = Specifies the reason for the bug check
482 * RETURNS: Doesn't
483 */
484 VOID
485 STDCALL
486 KeBugCheck(ULONG BugCheckCode)
487 {
488 KeBugCheckEx(BugCheckCode, 0, 0, 0, 0);
489 }
490
491 /* EOF */