KD System Rewrite:
[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 DPRINT1("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 DPRINT1("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 DbgPrint("%s\n", BugCode);
240 break;
241 }
242 }
243 }
244 }
245
246 VOID
247 STDCALL
248 KiDoBugCheckCallbacks(VOID)
249 {
250 PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
251 PLIST_ENTRY ListHead;
252 PLIST_ENTRY NextEntry;
253
254 /* FIXME: Check Checksum and add support for WithReason Callbacks */
255
256 /* First make sure that the list is Initialized... it might not be */
257 ListHead = &BugcheckCallbackListHead;
258 if (ListHead->Flink && ListHead->Blink) {
259
260 /* Loop the list */
261 NextEntry = ListHead->Flink;
262 while (NextEntry != ListHead) {
263
264 /* Get the Callback Record */
265 CurrentRecord = CONTAINING_RECORD(NextEntry,
266 KBUGCHECK_CALLBACK_RECORD,
267 Entry);
268
269 /* Make sure it's inserted */
270 if (CurrentRecord->State == BufferInserted) {
271
272 /* Call the routine */
273 CurrentRecord->State = BufferStarted;
274 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
275 CurrentRecord->Length);
276 CurrentRecord->State = BufferFinished;
277 }
278
279 /* Move to next Entry */
280 NextEntry = NextEntry->Flink;
281 }
282 }
283 }
284
285 VOID
286 STDCALL
287 KeBugCheckWithTf(ULONG BugCheckCode,
288 ULONG BugCheckParameter1,
289 ULONG BugCheckParameter2,
290 ULONG BugCheckParameter3,
291 ULONG BugCheckParameter4,
292 PKTRAP_FRAME Tf)
293 {
294 KIRQL OldIrql;
295 BOOLEAN GotExtendedCrashInfo = FALSE;
296 PVOID Address = 0;
297 PLIST_ENTRY CurrentEntry;
298 MODULE_TEXT_SECTION* CurrentSection = NULL;
299 extern LIST_ENTRY ModuleTextListHead;
300
301 /* Make sure we're switching back to the blue screen and print messages on it */
302 HalReleaseDisplayOwnership();
303 if (KdpDebugMode.Gdb) KdpDebugMode.Screen = TRUE;
304
305 /* Try to find out who did this. For this, we need a Trap Frame.
306 * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the
307 * info so it eventually needs to be supported.
308 */
309 if (Tf) {
310
311 /* For now, get Address from EIP */
312 Address = (PVOID)Tf->Eip;
313
314 /* Try to get information on the module */
315 CurrentEntry = ModuleTextListHead.Flink;
316 while (CurrentEntry != &ModuleTextListHead && CurrentEntry != NULL) {
317
318 /* Get the current Section */
319 CurrentSection = CONTAINING_RECORD(CurrentEntry,
320 MODULE_TEXT_SECTION,
321 ListEntry);
322
323 /* Check if this is the right one */
324 if ((Address != NULL && (Address >= (PVOID)CurrentSection->Base &&
325 Address < (PVOID)(CurrentSection->Base + CurrentSection->Length)))) {
326
327 /* We got it */
328 GotExtendedCrashInfo = TRUE;
329 break;
330 }
331
332 /* Loop again */
333 CurrentEntry = CurrentEntry->Flink;
334 }
335 }
336
337 /* Raise IRQL to HIGH_LEVEL */
338 Ke386DisableInterrupts();
339 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
340
341 /* Unload the Kernel Adress Space if we own it */
342 if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread())
343 MmUnlockAddressSpace(MmGetKernelAddressSpace());
344
345 /* FIXMEs: Use inbv to clear, fill and write to screen. */
346
347 /* Show the STOP Message */
348 DbgPrint("A problem has been detected and ReactOS has been shut down to prevent "
349 "damage to your computer.\n\n");
350
351 /* Show the module name of who caused this */
352 if (GotExtendedCrashInfo) {
353
354 DbgPrint("The problem seems to be caused by the following file: %S\n\n", CurrentSection->Name);
355 }
356
357 /* Find the Bug Code String */
358 KeGetBugMessageText(BugCheckCode, NULL);
359
360 /* Show the techincal Data */
361 DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n",
362 BugCheckCode,
363 BugCheckParameter1,
364 BugCheckParameter2,
365 BugCheckParameter3,
366 BugCheckParameter4);
367
368 /* Show the module name and more data of who caused this */
369 if (GotExtendedCrashInfo) {
370
371 DbgPrint("*** %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n",
372 CurrentSection->Name,
373 Address,
374 CurrentSection->Base,
375 0);
376 }
377
378 /* There can only be one Bugcheck per Bootup */
379 if (!InterlockedDecrement(&KeBugCheckCount)) {
380
381 #ifdef CONFIG_SMP
382 ULONG i;
383 /* Freeze the other CPUs */
384 for (i = 0; i < KeNumberProcessors; i++) {
385 if (i != KeGetCurrentProcessorNumber()) {
386
387 /* Send the IPI and give them one second to catch up */
388 KiIpiSendRequest(1 << i, IPI_REQUEST_FREEZE);
389 KeStallExecutionProcessor(1000000);
390 }
391 }
392 #endif
393
394 /* Check if we got a Trap Frame */
395 if (Tf) {
396
397 /* Dump it */
398 KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2);
399
400 } else {
401
402 /* We can only dump the frames */
403 #if defined(__GNUC__)
404 KeDumpStackFrames((PULONG)__builtin_frame_address(0));
405 #elif defined(_MSC_VER)
406 __asm push ebp
407 __asm call KeDumpStackFrames
408 __asm add esp, 4
409 #else
410 #error Unknown compiler for inline assembler
411 #endif
412 }
413
414 /* Call the Callbacks */;
415 KiDoBugCheckCallbacks();
416
417 /* Dump the BSOD to the Paging File */
418 MmDumpToPagingFile(BugCheckCode,
419 BugCheckParameter1,
420 BugCheckParameter2,
421 BugCheckParameter3,
422 BugCheckParameter4,
423 Tf);
424
425 /* Wake up the Debugger */
426 if (KdDebuggerEnabled) {
427 Ke386EnableInterrupts();
428 DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND);
429 Ke386DisableInterrupts();
430 }
431 }
432
433 /* Halt this CPU now */
434 for (;;) Ke386HaltProcessor();
435 }
436
437 /*
438 * @implemented
439 *
440 * FUNCTION: Brings the system down in a controlled manner when an
441 * inconsistency that might otherwise cause corruption has been detected
442 * ARGUMENTS:
443 * BugCheckCode = Specifies the reason for the bug check
444 * BugCheckParameter[1-4] = Additional information about bug
445 * RETURNS: Doesn't
446 */
447 VOID
448 STDCALL
449 KeBugCheckEx(ULONG BugCheckCode,
450 ULONG BugCheckParameter1,
451 ULONG BugCheckParameter2,
452 ULONG BugCheckParameter3,
453 ULONG BugCheckParameter4)
454 {
455 /* Call the Trap Frame version without a Trap Frame */
456 KeBugCheckWithTf(BugCheckCode,
457 BugCheckParameter1,
458 BugCheckParameter2,
459 BugCheckParameter3,
460 BugCheckParameter4,
461 NULL);
462 }
463
464 /*
465 * @implemented
466 *
467 * FUNCTION: Brings the system down in a controlled manner when an
468 * inconsistency that might otherwise cause corruption has been detected
469 * ARGUMENTS:
470 * BugCheckCode = Specifies the reason for the bug check
471 * RETURNS: Doesn't
472 */
473 VOID STDCALL
474 KeBugCheck(ULONG BugCheckCode)
475 {
476 KeBugCheckEx(BugCheckCode, 0, 0, 0, 0);
477 }
478
479 /* EOF */