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