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