xref: /reactos/ntoskrnl/ke/bug.c (revision fe11f7a2)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/bug.c
5  * PURPOSE:         Bugcheck Support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 
13 #ifdef KDBG
14 #include <kdbg/kdb.h>
15 #endif
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 /* GLOBALS *******************************************************************/
21 
22 LIST_ENTRY KeBugcheckCallbackListHead;
23 LIST_ENTRY KeBugcheckReasonCallbackListHead;
24 KSPIN_LOCK BugCheckCallbackLock;
25 ULONG KeBugCheckActive, KeBugCheckOwner;
26 LONG KeBugCheckOwnerRecursionCount;
27 PMESSAGE_RESOURCE_DATA KiBugCodeMessages;
28 ULONG KeBugCheckCount = 1;
29 ULONG KiHardwareTrigger;
30 PUNICODE_STRING KiBugCheckDriver;
31 ULONG_PTR KiBugCheckData[5];
32 
33 PKNMI_HANDLER_CALLBACK KiNmiCallbackListHead = NULL;
34 KSPIN_LOCK KiNmiCallbackListLock;
35 
36 /* Jira Reporting */
37 UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
38 UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
39 
40 /* PRIVATE FUNCTIONS *********************************************************/
41 
42 PVOID
43 NTAPI
44 KiPcToFileHeader(IN PVOID Pc,
45                  OUT PLDR_DATA_TABLE_ENTRY *LdrEntry,
46                  IN BOOLEAN DriversOnly,
47                  OUT PBOOLEAN InKernel)
48 {
49     ULONG i = 0;
50     PVOID ImageBase, PcBase = NULL;
51     PLDR_DATA_TABLE_ENTRY Entry;
52     PLIST_ENTRY ListHead, NextEntry;
53 
54     /* Check which list we should use */
55     ListHead = (KeLoaderBlock) ? &KeLoaderBlock->LoadOrderListHead :
56                                  &PsLoadedModuleList;
57 
58     /* Assume no */
59     *InKernel = FALSE;
60 
61     /* Set list pointers and make sure it's valid */
62     NextEntry = ListHead->Flink;
63     if (NextEntry)
64     {
65         /* Start loop */
66         while (NextEntry != ListHead)
67         {
68             /* Increase entry */
69             i++;
70 
71             /* Check if this is a kernel entry and we only want drivers */
72             if ((i <= 2) && (DriversOnly != FALSE))
73             {
74                 /* Skip it */
75                 NextEntry = NextEntry->Flink;
76                 continue;
77             }
78 
79             /* Get the loader entry */
80             Entry = CONTAINING_RECORD(NextEntry,
81                                       LDR_DATA_TABLE_ENTRY,
82                                       InLoadOrderLinks);
83 
84             /* Move to the next entry */
85             NextEntry = NextEntry->Flink;
86             ImageBase = Entry->DllBase;
87 
88             /* Check if this is the right one */
89             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
90                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
91             {
92                 /* Return this entry */
93                 *LdrEntry = Entry;
94                 PcBase = ImageBase;
95 
96                 /* Check if this was a kernel or HAL entry */
97                 if (i <= 2) *InKernel = TRUE;
98                 break;
99             }
100         }
101     }
102 
103     /* Return the base address */
104     return PcBase;
105 }
106 
107 PVOID
108 NTAPI
109 KiRosPcToUserFileHeader(IN PVOID Pc,
110                         OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
111 {
112     PVOID ImageBase, PcBase = NULL;
113     PLDR_DATA_TABLE_ENTRY Entry;
114     PLIST_ENTRY ListHead, NextEntry;
115 
116     /*
117      * We know this is valid because we should only be called after a
118      * succesfull address from RtlWalkFrameChain for UserMode, which
119      * validates everything for us.
120      */
121     ListHead = &KeGetCurrentThread()->
122                Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList;
123 
124     /* Set list pointers and make sure it's valid */
125     NextEntry = ListHead->Flink;
126     if (NextEntry)
127     {
128         /* Start loop */
129         while (NextEntry != ListHead)
130         {
131             /* Get the loader entry */
132             Entry = CONTAINING_RECORD(NextEntry,
133                                       LDR_DATA_TABLE_ENTRY,
134                                       InLoadOrderLinks);
135 
136             /* Move to the next entry */
137             NextEntry = NextEntry->Flink;
138             ImageBase = Entry->DllBase;
139 
140             /* Check if this is the right one */
141             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
142                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
143             {
144                 /* Return this entry */
145                 *LdrEntry = Entry;
146                 PcBase = ImageBase;
147                 break;
148             }
149         }
150     }
151 
152     /* Return the base address */
153     return PcBase;
154 }
155 
156 USHORT
157 NTAPI
158 KeRosCaptureUserStackBackTrace(IN ULONG FramesToSkip,
159                                IN ULONG FramesToCapture,
160                                OUT PVOID *BackTrace,
161                                OUT PULONG BackTraceHash OPTIONAL)
162 {
163     PVOID Frames[2 * 64];
164     ULONG FrameCount;
165     ULONG Hash = 0, i;
166 
167     /* Skip a frame for the caller */
168     FramesToSkip++;
169 
170     /* Don't go past the limit */
171     if ((FramesToCapture + FramesToSkip) >= 128) return 0;
172 
173     /* Do the back trace */
174     FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 1);
175 
176     /* Make sure we're not skipping all of them */
177     if (FrameCount <= FramesToSkip) return 0;
178 
179     /* Loop all the frames */
180     for (i = 0; i < FramesToCapture; i++)
181     {
182         /* Don't go past the limit */
183         if ((FramesToSkip + i) >= FrameCount) break;
184 
185         /* Save this entry and hash it */
186         BackTrace[i] = Frames[FramesToSkip + i];
187         Hash += PtrToUlong(BackTrace[i]);
188     }
189 
190     /* Write the hash */
191     if (BackTraceHash) *BackTraceHash = Hash;
192 
193     /* Clear the other entries and return count */
194     RtlFillMemoryUlong(Frames, 128, 0);
195     return (USHORT)i;
196 }
197 
198 
199 VOID
200 FASTCALL
201 KeRosDumpStackFrameArray(IN PULONG_PTR Frames,
202                          IN ULONG FrameCount)
203 {
204     ULONG i;
205     ULONG_PTR Addr;
206     BOOLEAN InSystem;
207     PVOID p;
208 
209     /* GCC complaints that it may be used uninitialized */
210     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
211 
212     /* Loop them */
213     for (i = 0; i < FrameCount; i++)
214     {
215         /* Get the EIP */
216         Addr = Frames[i];
217         if (!Addr)
218         {
219         	break;
220         }
221 
222         /* Get the base for this file */
223         if (Addr > (ULONG_PTR)MmHighestUserAddress)
224         {
225             /* We are in kernel */
226             p = KiPcToFileHeader((PVOID)Addr, &LdrEntry, FALSE, &InSystem);
227         }
228         else
229         {
230             /* We are in user land */
231             p = KiRosPcToUserFileHeader((PVOID)Addr, &LdrEntry);
232         }
233         if (p)
234         {
235 #ifdef KDBG
236             if (!KdbSymPrintAddress((PVOID)Addr, NULL))
237 #endif
238             {
239                 CHAR AnsiName[64];
240 
241                 /* Convert module name to ANSI and print it */
242                 KeBugCheckUnicodeToAnsi(&LdrEntry->BaseDllName,
243                                         AnsiName,
244                                         sizeof(AnsiName));
245                 Addr -= (ULONG_PTR)LdrEntry->DllBase;
246                 DbgPrint("<%s: %p>", AnsiName, (PVOID)Addr);
247             }
248         }
249         else
250         {
251             /* Print only the address */
252             DbgPrint("<%p>", (PVOID)Addr);
253         }
254 
255         /* Go to the next frame */
256         DbgPrint("\n");
257     }
258 }
259 
260 VOID
261 NTAPI
262 KeRosDumpStackFrames(IN PULONG_PTR Frame OPTIONAL,
263                      IN ULONG FrameCount OPTIONAL)
264 {
265     ULONG_PTR Frames[32];
266     ULONG RealFrameCount;
267 
268     /* If the caller didn't ask, assume 32 frames */
269     if (!FrameCount || FrameCount > 32) FrameCount = 32;
270 
271     if (Frame)
272     {
273         /* Dump them */
274         KeRosDumpStackFrameArray(Frame, FrameCount);
275     }
276     else
277     {
278         /* Get the current frames (skip the two. One for the dumper, one for the caller) */
279         RealFrameCount = RtlCaptureStackBackTrace(2, FrameCount, (PVOID*)Frames, NULL);
280         DPRINT1("RealFrameCount =%lu\n", RealFrameCount);
281 
282         /* Dump them */
283         KeRosDumpStackFrameArray(Frames, RealFrameCount);
284 
285         /* Count left for user mode? */
286         if (FrameCount - RealFrameCount > 0)
287         {
288             /* Get the current frames */
289             RealFrameCount = KeRosCaptureUserStackBackTrace(-1, FrameCount - RealFrameCount, (PVOID*)Frames, NULL);
290 
291             /* Dump them */
292             KeRosDumpStackFrameArray(Frames, RealFrameCount);
293         }
294     }
295 }
296 
297 CODE_SEG("INIT")
298 VOID
299 NTAPI
300 KiInitializeBugCheck(VOID)
301 {
302     PMESSAGE_RESOURCE_DATA BugCheckData;
303     LDR_RESOURCE_INFO ResourceInfo;
304     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
305     NTSTATUS Status;
306     PLDR_DATA_TABLE_ENTRY LdrEntry;
307 
308     /* Get the kernel entry */
309     LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
310                                  LDR_DATA_TABLE_ENTRY,
311                                  InLoadOrderLinks);
312 
313     /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
314     ResourceInfo.Type = 11;
315     ResourceInfo.Name = 1;
316     ResourceInfo.Language = 9;
317 
318     /* Do the lookup. */
319     Status = LdrFindResource_U(LdrEntry->DllBase,
320                                &ResourceInfo,
321                                RESOURCE_DATA_LEVEL,
322                                &ResourceDataEntry);
323 
324     /* Make sure it worked */
325     if (NT_SUCCESS(Status))
326     {
327         /* Now actually get a pointer to it */
328         Status = LdrAccessResource(LdrEntry->DllBase,
329                                    ResourceDataEntry,
330                                    (PVOID*)&BugCheckData,
331                                    NULL);
332         if (NT_SUCCESS(Status)) KiBugCodeMessages = BugCheckData;
333     }
334 }
335 
336 BOOLEAN
337 NTAPI
338 KeGetBugMessageText(IN ULONG BugCheckCode,
339                     OUT PANSI_STRING OutputString OPTIONAL)
340 {
341     ULONG i;
342     ULONG IdOffset;
343     PMESSAGE_RESOURCE_ENTRY MessageEntry;
344     PCHAR BugCode;
345     USHORT Length;
346     BOOLEAN Result = FALSE;
347 
348     /* Make sure we're not bugchecking too early */
349     if (!KiBugCodeMessages) return Result;
350 
351     /*
352      * Globally protect in SEH as we are trying to access data in
353      * dire situations, and potentially going to patch it (see below).
354      */
355     _SEH2_TRY
356     {
357 
358     /*
359      * Make the kernel resource section writable, as we are going to manually
360      * trim the trailing newlines in the bugcheck resource message in place,
361      * when OutputString is NULL and before displaying it on screen.
362      */
363     MmMakeKernelResourceSectionWritable();
364 
365     /* Find the message. This code is based on RtlFindMesssage */
366     for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
367     {
368         /* Check if the ID matches */
369         if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
370             (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
371         {
372             /* Get offset to entry */
373             MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
374                 ((ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries);
375             IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
376 
377             /* Advance in the entries until finding it */
378             while (IdOffset--)
379             {
380                 MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
381                     ((ULONG_PTR)MessageEntry + MessageEntry->Length);
382             }
383 
384             /* Make sure it's not Unicode */
385             ASSERT(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE));
386 
387             /* Get the final code */
388             BugCode = (PCHAR)MessageEntry->Text;
389             Length = (USHORT)strlen(BugCode);
390 
391             /* Handle trailing newlines */
392             while ((Length > 0) && ((BugCode[Length - 1] == '\n') ||
393                                     (BugCode[Length - 1] == '\r') ||
394                                     (BugCode[Length - 1] == ANSI_NULL)))
395             {
396                 /* Directly trim the newline in place if we don't return the string */
397                 if (!OutputString) BugCode[Length - 1] = ANSI_NULL;
398 
399                 /* Skip the trailing newline */
400                 Length--;
401             }
402 
403             /* Check if caller wants an output string */
404             if (OutputString)
405             {
406                 /* Return it in the OutputString */
407                 OutputString->Buffer = BugCode;
408                 OutputString->Length = Length;
409                 OutputString->MaximumLength = Length;
410             }
411             else
412             {
413                 /* Direct output to screen */
414                 InbvDisplayString(BugCode);
415                 InbvDisplayString("\r");
416             }
417 
418             /* We're done */
419             Result = TRUE;
420             break;
421         }
422     }
423 
424     }
425     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
426     {
427     }
428     _SEH2_END;
429 
430     /* Return the result */
431     return Result;
432 }
433 
434 VOID
435 NTAPI
436 KiDoBugCheckCallbacks(VOID)
437 {
438     PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
439     PLIST_ENTRY ListHead, NextEntry, LastEntry;
440     ULONG_PTR Checksum;
441 
442     /* First make sure that the list is initialized... it might not be */
443     ListHead = &KeBugcheckCallbackListHead;
444     if ((!ListHead->Flink) || (!ListHead->Blink))
445         return;
446 
447     /* Loop the list */
448     LastEntry = ListHead;
449     NextEntry = ListHead->Flink;
450     while (NextEntry != ListHead)
451     {
452         /* Get the reord */
453         CurrentRecord = CONTAINING_RECORD(NextEntry,
454                                           KBUGCHECK_CALLBACK_RECORD,
455                                           Entry);
456 
457         /* Validate it */
458         // TODO/FIXME: Check whether the memory CurrentRecord points to
459         // is still accessible and valid!
460         if (CurrentRecord->Entry.Blink != LastEntry) return;
461         Checksum = (ULONG_PTR)CurrentRecord->CallbackRoutine;
462         Checksum += (ULONG_PTR)CurrentRecord->Buffer;
463         Checksum += (ULONG_PTR)CurrentRecord->Length;
464         Checksum += (ULONG_PTR)CurrentRecord->Component;
465 
466         /* Make sure it's inserted and validated */
467         if ((CurrentRecord->State == BufferInserted) &&
468             (CurrentRecord->Checksum == Checksum))
469         {
470             /* Call the routine */
471             CurrentRecord->State = BufferStarted;
472             _SEH2_TRY
473             {
474                 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
475                                                  CurrentRecord->Length);
476                 CurrentRecord->State = BufferFinished;
477             }
478             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
479             {
480                 CurrentRecord->State = BufferIncomplete;
481             }
482             _SEH2_END;
483         }
484 
485         /* Go to the next entry */
486         LastEntry = NextEntry;
487         NextEntry = NextEntry->Flink;
488     }
489 }
490 
491 VOID
492 NTAPI
493 KiBugCheckDebugBreak(IN ULONG StatusCode)
494 {
495     /*
496      * Wrap this in SEH so we don't crash if
497      * there is no debugger or if it disconnected
498      */
499 DoBreak:
500     _SEH2_TRY
501     {
502         /* Breakpoint */
503         DbgBreakPointWithStatus(StatusCode);
504     }
505     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
506     {
507         /* No debugger, halt the CPU */
508         HalHaltSystem();
509     }
510     _SEH2_END;
511 
512     /* Break again if this wasn't first try */
513     if (StatusCode != DBG_STATUS_BUGCHECK_FIRST) goto DoBreak;
514 }
515 
516 PCHAR
517 NTAPI
518 KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING Unicode,
519                         OUT PCHAR Ansi,
520                         IN ULONG Length)
521 {
522     PCHAR p;
523     PWCHAR pw;
524     ULONG i;
525 
526     /* Set length and normalize it */
527     i = Unicode->Length / sizeof(WCHAR);
528     i = min(i, Length - 1);
529 
530     /* Set source and destination, and copy */
531     pw = Unicode->Buffer;
532     p = Ansi;
533     while (i--) *p++ = (CHAR)*pw++;
534 
535     /* Null terminate and return */
536     *p = ANSI_NULL;
537     return Ansi;
538 }
539 
540 VOID
541 NTAPI
542 KiDumpParameterImages(IN PCHAR Message,
543                       IN PULONG_PTR Parameters,
544                       IN ULONG ParameterCount,
545                       IN PKE_BUGCHECK_UNICODE_TO_ANSI ConversionRoutine)
546 {
547     ULONG i;
548     BOOLEAN InSystem;
549     PLDR_DATA_TABLE_ENTRY LdrEntry;
550     PVOID ImageBase;
551     PUNICODE_STRING DriverName;
552     CHAR AnsiName[32];
553     PIMAGE_NT_HEADERS NtHeader;
554     ULONG TimeStamp;
555     BOOLEAN FirstRun = TRUE;
556 
557     /* Loop parameters */
558     for (i = 0; i < ParameterCount; i++)
559     {
560         /* Get the base for this parameter */
561         ImageBase = KiPcToFileHeader((PVOID)Parameters[i],
562                                      &LdrEntry,
563                                      FALSE,
564                                      &InSystem);
565         if (!ImageBase)
566         {
567             /* FIXME: Add code to check for unloaded drivers */
568             DPRINT1("Potentially unloaded driver!\n");
569             continue;
570         }
571         else
572         {
573             /* Get the NT Headers and Timestamp */
574             NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
575             TimeStamp = NtHeader->FileHeader.TimeDateStamp;
576 
577             /* Convert the driver name */
578             DriverName = &LdrEntry->BaseDllName;
579             ConversionRoutine(&LdrEntry->BaseDllName,
580                               AnsiName,
581                               sizeof(AnsiName));
582         }
583 
584         /* Format driver name */
585         sprintf(Message,
586                 "%s**  %12s - Address %p base at %p, DateStamp %08lx\r\n",
587                 FirstRun ? "\r\n*":"*",
588                 AnsiName,
589                 (PVOID)Parameters[i],
590                 ImageBase,
591                 TimeStamp);
592 
593         /* Check if we only had one parameter */
594         if (ParameterCount <= 1)
595         {
596             /* Then just save the name */
597             KiBugCheckDriver = DriverName;
598         }
599         else
600         {
601             /* Otherwise, display the message */
602             InbvDisplayString(Message);
603         }
604 
605         /* Loop again */
606         FirstRun = FALSE;
607     }
608 }
609 
610 VOID
611 NTAPI
612 KiDisplayBlueScreen(IN ULONG MessageId,
613                     IN BOOLEAN IsHardError,
614                     IN PCHAR HardErrCaption OPTIONAL,
615                     IN PCHAR HardErrMessage OPTIONAL,
616                     IN PCHAR Message)
617 {
618     ULONG BugCheckCode = (ULONG)KiBugCheckData[0];
619     BOOLEAN Enable = TRUE;
620     CHAR AnsiName[107];
621 
622     /* Enable headless support for bugcheck */
623     HeadlessDispatch(HeadlessCmdStartBugCheck,
624                      NULL, 0, NULL, NULL);
625     HeadlessDispatch(HeadlessCmdEnableTerminal,
626                      &Enable, sizeof(Enable),
627                      NULL, NULL);
628     HeadlessDispatch(HeadlessCmdSendBlueScreenData,
629                      &BugCheckCode, sizeof(BugCheckCode),
630                      NULL, NULL);
631 
632     /* Check if bootvid is installed */
633     if (InbvIsBootDriverInstalled())
634     {
635         /* Acquire ownership and reset the display */
636         InbvAcquireDisplayOwnership();
637         InbvResetDisplay();
638 
639         /* Display blue screen */
640         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLUE);
641         InbvSetTextColor(BV_COLOR_WHITE);
642         InbvInstallDisplayStringFilter(NULL);
643         InbvEnableDisplayString(TRUE);
644         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
645     }
646 
647     /* Check if this is a hard error */
648     if (IsHardError)
649     {
650         /* Display caption and message */
651         if (HardErrCaption) InbvDisplayString(HardErrCaption);
652         if (HardErrMessage) InbvDisplayString(HardErrMessage);
653     }
654 
655     /* Begin the display */
656     InbvDisplayString("\r\n");
657 
658     /* Print out initial message */
659     KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
660     InbvDisplayString("\r\n\r\n");
661 
662     /* Check if we have a driver */
663     if (KiBugCheckDriver)
664     {
665         /* Print out into to driver name */
666         KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
667 
668         /* Convert and print out driver name */
669         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
670         InbvDisplayString(" ");
671         InbvDisplayString(AnsiName);
672         InbvDisplayString("\r\n\r\n");
673     }
674 
675     /* Check if this is the generic message */
676     if (MessageId == BUGCODE_PSS_MESSAGE)
677     {
678         /* It is, so get the bug code string as well */
679         KeGetBugMessageText(BugCheckCode, NULL);
680         InbvDisplayString("\r\n\r\n");
681     }
682 
683     /* Print second introduction message */
684     KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
685     InbvDisplayString("\r\n\r\n");
686 
687     /* Get the bug code string */
688     KeGetBugMessageText(MessageId, NULL);
689     InbvDisplayString("\r\n\r\n");
690 
691     /* Print message for technical information */
692     KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
693 
694     /* Show the technical Data */
695     RtlStringCbPrintfA(AnsiName,
696                        sizeof(AnsiName),
697                        "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
698                        BugCheckCode,
699                        (PVOID)KiBugCheckData[1],
700                        (PVOID)KiBugCheckData[2],
701                        (PVOID)KiBugCheckData[3],
702                        (PVOID)KiBugCheckData[4]);
703     InbvDisplayString(AnsiName);
704 
705     /* Check if we have a driver*/
706     if (KiBugCheckDriver)
707     {
708         /* Display technical driver data */
709         InbvDisplayString(Message);
710     }
711     else
712     {
713         /* Dump parameter information */
714         KiDumpParameterImages(Message,
715                               (PVOID)&KiBugCheckData[1],
716                               4,
717                               KeBugCheckUnicodeToAnsi);
718     }
719 }
720 
721 DECLSPEC_NORETURN
722 VOID
723 NTAPI
724 KeBugCheckWithTf(IN ULONG BugCheckCode,
725                  IN ULONG_PTR BugCheckParameter1,
726                  IN ULONG_PTR BugCheckParameter2,
727                  IN ULONG_PTR BugCheckParameter3,
728                  IN ULONG_PTR BugCheckParameter4,
729                  IN PKTRAP_FRAME TrapFrame)
730 {
731     PKPRCB Prcb = KeGetCurrentPrcb();
732     CONTEXT Context;
733     ULONG MessageId;
734     CHAR AnsiName[128];
735     BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
736     PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
737     PVOID Pc = NULL, Memory;
738     PVOID DriverBase;
739     PLDR_DATA_TABLE_ENTRY LdrEntry;
740     PULONG_PTR HardErrorParameters;
741     KIRQL OldIrql;
742 
743     /* Set active bugcheck */
744     KeBugCheckActive = TRUE;
745     KiBugCheckDriver = NULL;
746 
747     /* Check if this is power failure simulation */
748     if (BugCheckCode == POWER_FAILURE_SIMULATE)
749     {
750         /* Call the Callbacks and reboot */
751         KiDoBugCheckCallbacks();
752         HalReturnToFirmware(HalRebootRoutine);
753     }
754 
755     /* Save the IRQL and set hardware trigger */
756     Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
757     InterlockedIncrement((PLONG)&KiHardwareTrigger);
758 
759     /* Capture the CPU Context */
760     RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
761     KiSaveProcessorControlState(&Prcb->ProcessorState);
762     Context = Prcb->ProcessorState.ContextFrame;
763 
764     /* FIXME: Call the Watchdog if it's registered */
765 
766     /* Check which bugcode this is */
767     switch (BugCheckCode)
768     {
769         /* These bug checks already have detailed messages, keep them */
770         case UNEXPECTED_KERNEL_MODE_TRAP:
771         case DRIVER_CORRUPTED_EXPOOL:
772         case ACPI_BIOS_ERROR:
773         case ACPI_BIOS_FATAL_ERROR:
774         case THREAD_STUCK_IN_DEVICE_DRIVER:
775         case DATA_BUS_ERROR:
776         case FAT_FILE_SYSTEM:
777         case NO_MORE_SYSTEM_PTES:
778         case INACCESSIBLE_BOOT_DEVICE:
779 
780             /* Keep the same code */
781             MessageId = BugCheckCode;
782             break;
783 
784         /* Check if this is a kernel-mode exception */
785         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
786         case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
787         case KMODE_EXCEPTION_NOT_HANDLED:
788 
789             /* Use the generic text message */
790             MessageId = KMODE_EXCEPTION_NOT_HANDLED;
791             break;
792 
793         /* File-system errors */
794         case NTFS_FILE_SYSTEM:
795 
796             /* Use the generic message for FAT */
797             MessageId = FAT_FILE_SYSTEM;
798             break;
799 
800         /* Check if this is a coruption of the Mm's Pool */
801         case DRIVER_CORRUPTED_MMPOOL:
802 
803             /* Use generic corruption message */
804             MessageId = DRIVER_CORRUPTED_EXPOOL;
805             break;
806 
807         /* Check if this is a signature check failure */
808         case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
809 
810             /* Use the generic corruption message */
811             MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
812             break;
813 
814         /* All other codes */
815         default:
816 
817             /* Use the default bugcheck message */
818             MessageId = BUGCODE_PSS_MESSAGE;
819             break;
820     }
821 
822     /* Save bugcheck data */
823     KiBugCheckData[0] = BugCheckCode;
824     KiBugCheckData[1] = BugCheckParameter1;
825     KiBugCheckData[2] = BugCheckParameter2;
826     KiBugCheckData[3] = BugCheckParameter3;
827     KiBugCheckData[4] = BugCheckParameter4;
828 
829     /* Now check what bugcheck this is */
830     switch (BugCheckCode)
831     {
832         /* Invalid access to R/O memory or Unhandled KM Exception */
833         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
834         case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
835         case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
836         {
837             /* Check if we have a trap frame */
838             if (!TrapFrame)
839             {
840                 /* Use parameter 3 as a trap frame, if it exists */
841                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
842             }
843 
844             /* Check if we got one now and if we need to get the Program Counter */
845             if ((TrapFrame) &&
846                 (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
847             {
848                 /* Get the Program Counter */
849                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
850             }
851             break;
852         }
853 
854         /* Wrong IRQL */
855         case IRQL_NOT_LESS_OR_EQUAL:
856         {
857             /*
858              * The NT kernel has 3 special sections:
859              * MISYSPTE, POOLMI and POOLCODE. The bug check code can
860              * determine in which of these sections this bugcode happened
861              * and provide a more detailed analysis. For now, we don't.
862              */
863 
864             /* Program Counter is in parameter 4 */
865             Pc = (PVOID)BugCheckParameter4;
866 
867             /* Get the driver base */
868             DriverBase = KiPcToFileHeader(Pc,
869                                           &LdrEntry,
870                                           FALSE,
871                                           &IsSystem);
872             if (IsSystem)
873             {
874                 /*
875                  * The error happened inside the kernel or HAL.
876                  * Get the memory address that was being referenced.
877                  */
878                 Memory = (PVOID)BugCheckParameter1;
879 
880                 /* Find to which driver it belongs */
881                 DriverBase = KiPcToFileHeader(Memory,
882                                               &LdrEntry,
883                                               TRUE,
884                                               &IsSystem);
885                 if (DriverBase)
886                 {
887                     /* Get the driver name and update the bug code */
888                     KiBugCheckDriver = &LdrEntry->BaseDllName;
889                     KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
890                 }
891                 else
892                 {
893                     /* Find the driver that unloaded at this address */
894                     KiBugCheckDriver = NULL; // FIXME: ROS can't locate
895 
896                     /* Check if the cause was an unloaded driver */
897                     if (KiBugCheckDriver)
898                     {
899                         /* Update bug check code */
900                         KiBugCheckData[0] =
901                             SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
902                     }
903                 }
904             }
905             else
906             {
907                 /* Update the bug check code */
908                 KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
909             }
910 
911             /* Clear Pc so we don't look it up later */
912             Pc = NULL;
913             break;
914         }
915 
916         /* Hard error */
917         case FATAL_UNHANDLED_HARD_ERROR:
918         {
919             /* Copy bug check data from hard error */
920             HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
921             KiBugCheckData[0] = BugCheckParameter1;
922             KiBugCheckData[1] = HardErrorParameters[0];
923             KiBugCheckData[2] = HardErrorParameters[1];
924             KiBugCheckData[3] = HardErrorParameters[2];
925             KiBugCheckData[4] = HardErrorParameters[3];
926 
927             /* Remember that this is hard error and set the caption/message */
928             IsHardError = TRUE;
929             HardErrCaption = (PCHAR)BugCheckParameter3;
930             HardErrMessage = (PCHAR)BugCheckParameter4;
931             break;
932         }
933 
934         /* Page fault */
935         case PAGE_FAULT_IN_NONPAGED_AREA:
936         {
937             /* Assume no driver */
938             DriverBase = NULL;
939 
940             /* Check if we have a trap frame */
941             if (!TrapFrame)
942             {
943                 /* We don't, use parameter 3 if possible */
944                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
945             }
946 
947             /* Check if we have a frame now */
948             if (TrapFrame)
949             {
950                 /* Get the Program Counter */
951                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
952                 KiBugCheckData[3] = (ULONG_PTR)Pc;
953 
954                 /* Find out if was in the kernel or drivers */
955                 DriverBase = KiPcToFileHeader(Pc,
956                                               &LdrEntry,
957                                               FALSE,
958                                               &IsSystem);
959             }
960             else
961             {
962                 /* Can't blame a driver, assume system */
963                 IsSystem = TRUE;
964             }
965 
966             /* FIXME: Check for session pool in addition to special pool */
967 
968             /* Special pool has its own bug check codes */
969             if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
970             {
971                 if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
972                 {
973                     KiBugCheckData[0] = IsSystem
974                         ? PAGE_FAULT_IN_FREED_SPECIAL_POOL
975                         : DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
976                 }
977                 else
978                 {
979                     KiBugCheckData[0] = IsSystem
980                         ? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
981                         : DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
982                 }
983             }
984             else if (!DriverBase)
985             {
986                 /* Find the driver that unloaded at this address */
987                 KiBugCheckDriver = NULL; // FIXME: ROS can't locate
988 
989                 /* Check if the cause was an unloaded driver */
990                 if (KiBugCheckDriver)
991                 {
992                     KiBugCheckData[0] =
993                         DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
994                 }
995             }
996             break;
997         }
998 
999         /* Check if the driver forgot to unlock pages */
1000         case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
1001 
1002             /* Program Counter is in parameter 1 */
1003             Pc = (PVOID)BugCheckParameter1;
1004             break;
1005 
1006         /* Check if the driver consumed too many PTEs */
1007         case DRIVER_USED_EXCESSIVE_PTES:
1008 
1009             /* Loader entry is in parameter 1 */
1010             LdrEntry = (PVOID)BugCheckParameter1;
1011             KiBugCheckDriver = &LdrEntry->BaseDllName;
1012             break;
1013 
1014         /* Check if the driver has a stuck thread */
1015         case THREAD_STUCK_IN_DEVICE_DRIVER:
1016 
1017             /* The name is in Parameter 3 */
1018             KiBugCheckDriver = (PVOID)BugCheckParameter3;
1019             break;
1020 
1021         /* Anything else */
1022         default:
1023             break;
1024     }
1025 
1026     /* Do we have a driver name? */
1027     if (KiBugCheckDriver)
1028     {
1029         /* Convert it to ANSI */
1030         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
1031     }
1032     else
1033     {
1034         /* Do we have a Program Counter? */
1035         if (Pc)
1036         {
1037             /* Dump image name */
1038             KiDumpParameterImages(AnsiName,
1039                                   (PULONG_PTR)&Pc,
1040                                   1,
1041                                   KeBugCheckUnicodeToAnsi);
1042         }
1043     }
1044 
1045     /* Check if we need to save the context for KD */
1046     if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
1047 
1048     /* Check if a debugger is connected */
1049     if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
1050     {
1051         /* Crash on the debugger console */
1052         DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
1053                  "                       (0x%p,0x%p,0x%p,0x%p)\n\n",
1054                  KiBugCheckData[0],
1055                  KiBugCheckData[1],
1056                  KiBugCheckData[2],
1057                  KiBugCheckData[3],
1058                  KiBugCheckData[4]);
1059 
1060         /* Check if the debugger isn't currently connected */
1061         if (!KdDebuggerNotPresent)
1062         {
1063             /* Check if we have a driver to blame */
1064             if (KiBugCheckDriver)
1065             {
1066                 /* Dump it */
1067                 DbgPrint("Driver at fault: %s.\n", AnsiName);
1068             }
1069 
1070             /* Check if this was a hard error */
1071             if (IsHardError)
1072             {
1073                 /* Print caption and message */
1074                 if (HardErrCaption) DbgPrint(HardErrCaption);
1075                 if (HardErrMessage) DbgPrint(HardErrMessage);
1076             }
1077 
1078             /* Break in the debugger */
1079             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
1080         }
1081     }
1082 
1083     /* Raise IRQL to HIGH_LEVEL */
1084     _disable();
1085     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1086 
1087     /* Avoid recursion */
1088     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1089     {
1090 #ifdef CONFIG_SMP
1091         /* Set CPU that is bug checking now */
1092         KeBugCheckOwner = Prcb->Number;
1093 
1094         /* Freeze the other CPUs */
1095         KxFreezeExecution();
1096 #endif
1097 
1098         /* Display the BSOD */
1099         KiDisplayBlueScreen(MessageId,
1100                             IsHardError,
1101                             HardErrCaption,
1102                             HardErrMessage,
1103                             AnsiName);
1104 
1105         // TODO/FIXME: Run the registered reason-callbacks from
1106         // the KeBugcheckReasonCallbackListHead list with the
1107         // KbCallbackReserved1 reason.
1108 
1109         /* Check if the debugger is disabled but we can enable it */
1110         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1111         {
1112             /* Enable it */
1113             KdEnableDebuggerWithLock(FALSE);
1114         }
1115         else
1116         {
1117             /* Otherwise, print the last line */
1118             InbvDisplayString("\r\n");
1119         }
1120 
1121         /* Save the context */
1122         Prcb->ProcessorState.ContextFrame = Context;
1123 
1124         /* FIXME: Support Triage Dump */
1125 
1126         /* FIXME: Write the crash dump */
1127         // TODO: The crash-dump helper must set the Reboot variable.
1128         Reboot = !!IopAutoReboot;
1129     }
1130     else
1131     {
1132         /* Increase recursion count */
1133         KeBugCheckOwnerRecursionCount++;
1134         if (KeBugCheckOwnerRecursionCount == 2)
1135         {
1136             /* Break in the debugger */
1137             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1138         }
1139         else if (KeBugCheckOwnerRecursionCount > 2)
1140         {
1141             /* Halt execution */
1142             while (TRUE);
1143         }
1144     }
1145 
1146     /* Call the Callbacks */
1147     KiDoBugCheckCallbacks();
1148 
1149     /* FIXME: Call Watchdog if enabled */
1150 
1151     /* Check if we have to reboot */
1152     if (Reboot)
1153     {
1154         /* Unload symbols */
1155         DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
1156         HalReturnToFirmware(HalRebootRoutine);
1157     }
1158 
1159     /* Attempt to break in the debugger (otherwise halt CPU) */
1160     KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1161 
1162     /* Shouldn't get here */
1163     ASSERT(FALSE);
1164     while (TRUE);
1165 }
1166 
1167 BOOLEAN
1168 NTAPI
1169 KiHandleNmi(VOID)
1170 {
1171     BOOLEAN Handled = FALSE;
1172     PKNMI_HANDLER_CALLBACK NmiData;
1173 
1174     /* Parse the list of callbacks */
1175     NmiData = KiNmiCallbackListHead;
1176     while (NmiData)
1177     {
1178         /* Save if this callback has handled it -- all it takes is one */
1179         Handled |= NmiData->Callback(NmiData->Context, Handled);
1180         NmiData = NmiData->Next;
1181     }
1182 
1183     /* Has anyone handled this? */
1184     return Handled;
1185 }
1186 
1187 /* PUBLIC FUNCTIONS **********************************************************/
1188 
1189 /*
1190  * @unimplemented
1191  */
1192 NTSTATUS
1193 NTAPI
1194 KeInitializeCrashDumpHeader(IN ULONG Type,
1195                             IN ULONG Flags,
1196                             OUT PVOID Buffer,
1197                             IN ULONG BufferSize,
1198                             OUT ULONG BufferNeeded OPTIONAL)
1199 {
1200     UNIMPLEMENTED;
1201     return STATUS_UNSUCCESSFUL;
1202 }
1203 
1204 /*
1205  * @implemented
1206  */
1207 BOOLEAN
1208 NTAPI
1209 KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
1210 {
1211     KIRQL OldIrql;
1212     BOOLEAN Status = FALSE;
1213 
1214     /* Raise IRQL to High */
1215     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1216 
1217     /* Check the Current State */
1218     if (CallbackRecord->State == BufferInserted)
1219     {
1220         /* Reset state and remove from list */
1221         CallbackRecord->State = BufferEmpty;
1222         RemoveEntryList(&CallbackRecord->Entry);
1223         Status = TRUE;
1224     }
1225 
1226     /* Lower IRQL and return */
1227     KeLowerIrql(OldIrql);
1228     return Status;
1229 }
1230 
1231 /*
1232  * @implemented
1233  */
1234 BOOLEAN
1235 NTAPI
1236 KeDeregisterBugCheckReasonCallback(
1237     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
1238 {
1239     KIRQL OldIrql;
1240     BOOLEAN Status = FALSE;
1241 
1242     /* Raise IRQL to High */
1243     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1244 
1245     /* Check the Current State */
1246     if (CallbackRecord->State == BufferInserted)
1247     {
1248         /* Reset state and remove from list */
1249         CallbackRecord->State = BufferEmpty;
1250         RemoveEntryList(&CallbackRecord->Entry);
1251         Status = TRUE;
1252     }
1253 
1254     /* Lower IRQL and return */
1255     KeLowerIrql(OldIrql);
1256     return Status;
1257 }
1258 
1259 /*
1260  * @implemented
1261  */
1262 BOOLEAN
1263 NTAPI
1264 KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
1265                            IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
1266                            IN PVOID Buffer,
1267                            IN ULONG Length,
1268                            IN PUCHAR Component)
1269 {
1270     KIRQL OldIrql;
1271     BOOLEAN Status = FALSE;
1272 
1273     /* Raise IRQL to High */
1274     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1275 
1276     /* Check the Current State first so we don't double-register */
1277     if (CallbackRecord->State == BufferEmpty)
1278     {
1279         /* Set the Callback Settings and insert into the list */
1280         CallbackRecord->Length = Length;
1281         CallbackRecord->Buffer = Buffer;
1282         CallbackRecord->Component = Component;
1283         CallbackRecord->CallbackRoutine = CallbackRoutine;
1284         CallbackRecord->State = BufferInserted;
1285         InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
1286         Status = TRUE;
1287     }
1288 
1289     /* Lower IRQL and return */
1290     KeLowerIrql(OldIrql);
1291     return Status;
1292 }
1293 
1294 /*
1295  * @implemented
1296  */
1297 BOOLEAN
1298 NTAPI
1299 KeRegisterBugCheckReasonCallback(
1300     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
1301     IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
1302     IN KBUGCHECK_CALLBACK_REASON Reason,
1303     IN PUCHAR Component)
1304 {
1305     KIRQL OldIrql;
1306     BOOLEAN Status = FALSE;
1307 
1308     /* Raise IRQL to High */
1309     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1310 
1311     /* Check the Current State first so we don't double-register */
1312     if (CallbackRecord->State == BufferEmpty)
1313     {
1314         /* Set the Callback Settings and insert into the list */
1315         CallbackRecord->Component = Component;
1316         CallbackRecord->CallbackRoutine = CallbackRoutine;
1317         CallbackRecord->State = BufferInserted;
1318         CallbackRecord->Reason = Reason;
1319         InsertTailList(&KeBugcheckReasonCallbackListHead,
1320                        &CallbackRecord->Entry);
1321         Status = TRUE;
1322     }
1323 
1324     /* Lower IRQL and return */
1325     KeLowerIrql(OldIrql);
1326     return Status;
1327 }
1328 
1329 /*
1330  * @implemented
1331  */
1332 PVOID
1333 NTAPI
1334 KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
1335                       IN PVOID Context)
1336 {
1337     KIRQL OldIrql;
1338     PKNMI_HANDLER_CALLBACK NmiData, Next;
1339     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1340 
1341     /* Allocate NMI callback data */
1342     NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
1343     if (!NmiData) return NULL;
1344 
1345     /* Fill in the information */
1346     NmiData->Callback = CallbackRoutine;
1347     NmiData->Context = Context;
1348     NmiData->Handle = NmiData;
1349 
1350     /* Insert it into NMI callback list */
1351     KiAcquireNmiListLock(&OldIrql);
1352     NmiData->Next = KiNmiCallbackListHead;
1353     Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
1354                                              NmiData,
1355                                              NmiData->Next);
1356     ASSERT(Next == NmiData->Next);
1357     KiReleaseNmiListLock(OldIrql);
1358 
1359     /* Return the opaque "handle" */
1360     return NmiData->Handle;
1361 }
1362 
1363 /*
1364  * @implemented
1365  */
1366 NTSTATUS
1367 NTAPI
1368 KeDeregisterNmiCallback(IN PVOID Handle)
1369 {
1370     KIRQL OldIrql;
1371     PKNMI_HANDLER_CALLBACK NmiData;
1372     PKNMI_HANDLER_CALLBACK* Previous;
1373     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1374 
1375     /* Find in the list the NMI callback corresponding to the handle */
1376     KiAcquireNmiListLock(&OldIrql);
1377     Previous = &KiNmiCallbackListHead;
1378     NmiData = *Previous;
1379     while (NmiData)
1380     {
1381         if (NmiData->Handle == Handle)
1382         {
1383             /* The handle is the pointer to the callback itself */
1384             ASSERT(Handle == NmiData);
1385 
1386             /* Found it, remove from the list */
1387             *Previous = NmiData->Next;
1388             break;
1389         }
1390 
1391         /* Not found; try again */
1392         Previous = &NmiData->Next;
1393         NmiData = *Previous;
1394     }
1395     KiReleaseNmiListLock(OldIrql);
1396 
1397     /* If we have found the entry, free it */
1398     if (NmiData)
1399     {
1400         ExFreePoolWithTag(NmiData, TAG_KNMI);
1401         return STATUS_SUCCESS;
1402     }
1403 
1404     return STATUS_INVALID_HANDLE;
1405 }
1406 
1407 /*
1408  * @implemented
1409  */
1410 DECLSPEC_NORETURN
1411 VOID
1412 NTAPI
1413 KeBugCheckEx(IN ULONG BugCheckCode,
1414              IN ULONG_PTR BugCheckParameter1,
1415              IN ULONG_PTR BugCheckParameter2,
1416              IN ULONG_PTR BugCheckParameter3,
1417              IN ULONG_PTR BugCheckParameter4)
1418 {
1419     /* Call the internal API */
1420     KeBugCheckWithTf(BugCheckCode,
1421                      BugCheckParameter1,
1422                      BugCheckParameter2,
1423                      BugCheckParameter3,
1424                      BugCheckParameter4,
1425                      NULL);
1426 }
1427 
1428 /*
1429  * @implemented
1430  */
1431 DECLSPEC_NORETURN
1432 VOID
1433 NTAPI
1434 KeBugCheck(ULONG BugCheckCode)
1435 {
1436     /* Call the internal API */
1437     KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
1438 }
1439 
1440 /*
1441  * @implemented
1442  */
1443 VOID
1444 NTAPI
1445 KeEnterKernelDebugger(VOID)
1446 {
1447     /* Disable interrupts */
1448     KiHardwareTrigger = 1;
1449     _disable();
1450 
1451     /* Check the bugcheck count */
1452     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1453     {
1454         /* There was only one, is the debugger disabled? */
1455         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1456         {
1457             /* Enable the debugger */
1458             KdInitSystem(0, NULL);
1459         }
1460     }
1461 
1462     /* Break in the debugger */
1463     KiBugCheckDebugBreak(DBG_STATUS_FATAL);
1464 }
1465 
1466 /* EOF */
1467