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