xref: /reactos/ntoskrnl/ke/bug.c (revision 961893a7)
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     CHAR AnsiName[107];
619 
620     /* Check if bootvid is installed */
621     if (InbvIsBootDriverInstalled())
622     {
623         /* Acquire ownership and reset the display */
624         InbvAcquireDisplayOwnership();
625         InbvResetDisplay();
626 
627         /* Display blue screen */
628         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLUE);
629         InbvSetTextColor(BV_COLOR_WHITE);
630         InbvInstallDisplayStringFilter(NULL);
631         InbvEnableDisplayString(TRUE);
632         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
633     }
634 
635     /* Check if this is a hard error */
636     if (IsHardError)
637     {
638         /* Display caption and message */
639         if (HardErrCaption) InbvDisplayString(HardErrCaption);
640         if (HardErrMessage) InbvDisplayString(HardErrMessage);
641     }
642 
643     /* Begin the display */
644     InbvDisplayString("\r\n");
645 
646     /* Print out initial message */
647     KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
648     InbvDisplayString("\r\n\r\n");
649 
650     /* Check if we have a driver */
651     if (KiBugCheckDriver)
652     {
653         /* Print out into to driver name */
654         KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
655 
656         /* Convert and print out driver name */
657         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
658         InbvDisplayString(" ");
659         InbvDisplayString(AnsiName);
660         InbvDisplayString("\r\n\r\n");
661     }
662 
663     /* Check if this is the generic message */
664     if (MessageId == BUGCODE_PSS_MESSAGE)
665     {
666         /* It is, so get the bug code string as well */
667         KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
668         InbvDisplayString("\r\n\r\n");
669     }
670 
671     /* Print second introduction message */
672     KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
673     InbvDisplayString("\r\n\r\n");
674 
675     /* Get the bug code string */
676     KeGetBugMessageText(MessageId, NULL);
677     InbvDisplayString("\r\n\r\n");
678 
679     /* Print message for technical information */
680     KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
681 
682     /* Show the technical Data */
683     RtlStringCbPrintfA(AnsiName,
684                        sizeof(AnsiName),
685                        "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
686                        (ULONG)KiBugCheckData[0],
687                        (PVOID)KiBugCheckData[1],
688                        (PVOID)KiBugCheckData[2],
689                        (PVOID)KiBugCheckData[3],
690                        (PVOID)KiBugCheckData[4]);
691     InbvDisplayString(AnsiName);
692 
693     /* Check if we have a driver*/
694     if (KiBugCheckDriver)
695     {
696         /* Display technical driver data */
697         InbvDisplayString(Message);
698     }
699     else
700     {
701         /* Dump parameter information */
702         KiDumpParameterImages(Message,
703                               (PVOID)&KiBugCheckData[1],
704                               4,
705                               KeBugCheckUnicodeToAnsi);
706     }
707 }
708 
709 DECLSPEC_NORETURN
710 VOID
711 NTAPI
712 KeBugCheckWithTf(IN ULONG BugCheckCode,
713                  IN ULONG_PTR BugCheckParameter1,
714                  IN ULONG_PTR BugCheckParameter2,
715                  IN ULONG_PTR BugCheckParameter3,
716                  IN ULONG_PTR BugCheckParameter4,
717                  IN PKTRAP_FRAME TrapFrame)
718 {
719     PKPRCB Prcb = KeGetCurrentPrcb();
720     CONTEXT Context;
721     ULONG MessageId;
722     CHAR AnsiName[128];
723     BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
724     PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
725     PVOID Pc = NULL, Memory;
726     PVOID DriverBase;
727     PLDR_DATA_TABLE_ENTRY LdrEntry;
728     PULONG_PTR HardErrorParameters;
729     KIRQL OldIrql;
730 #ifdef CONFIG_SMP
731     LONG i = 0;
732 #endif
733 
734     /* Set active bugcheck */
735     KeBugCheckActive = TRUE;
736     KiBugCheckDriver = NULL;
737 
738     /* Check if this is power failure simulation */
739     if (BugCheckCode == POWER_FAILURE_SIMULATE)
740     {
741         /* Call the Callbacks and reboot */
742         KiDoBugCheckCallbacks();
743         HalReturnToFirmware(HalRebootRoutine);
744     }
745 
746     /* Save the IRQL and set hardware trigger */
747     Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
748     InterlockedIncrement((PLONG)&KiHardwareTrigger);
749 
750     /* Capture the CPU Context */
751     RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
752     KiSaveProcessorControlState(&Prcb->ProcessorState);
753     Context = Prcb->ProcessorState.ContextFrame;
754 
755     /* FIXME: Call the Watchdog if it's registered */
756 
757     /* Check which bugcode this is */
758     switch (BugCheckCode)
759     {
760         /* These bug checks already have detailed messages, keep them */
761         case UNEXPECTED_KERNEL_MODE_TRAP:
762         case DRIVER_CORRUPTED_EXPOOL:
763         case ACPI_BIOS_ERROR:
764         case ACPI_BIOS_FATAL_ERROR:
765         case THREAD_STUCK_IN_DEVICE_DRIVER:
766         case DATA_BUS_ERROR:
767         case FAT_FILE_SYSTEM:
768         case NO_MORE_SYSTEM_PTES:
769         case INACCESSIBLE_BOOT_DEVICE:
770 
771             /* Keep the same code */
772             MessageId = BugCheckCode;
773             break;
774 
775         /* Check if this is a kernel-mode exception */
776         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
777         case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
778         case KMODE_EXCEPTION_NOT_HANDLED:
779 
780             /* Use the generic text message */
781             MessageId = KMODE_EXCEPTION_NOT_HANDLED;
782             break;
783 
784         /* File-system errors */
785         case NTFS_FILE_SYSTEM:
786 
787             /* Use the generic message for FAT */
788             MessageId = FAT_FILE_SYSTEM;
789             break;
790 
791         /* Check if this is a coruption of the Mm's Pool */
792         case DRIVER_CORRUPTED_MMPOOL:
793 
794             /* Use generic corruption message */
795             MessageId = DRIVER_CORRUPTED_EXPOOL;
796             break;
797 
798         /* Check if this is a signature check failure */
799         case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
800 
801             /* Use the generic corruption message */
802             MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
803             break;
804 
805         /* All other codes */
806         default:
807 
808             /* Use the default bugcheck message */
809             MessageId = BUGCODE_PSS_MESSAGE;
810             break;
811     }
812 
813     /* Save bugcheck data */
814     KiBugCheckData[0] = BugCheckCode;
815     KiBugCheckData[1] = BugCheckParameter1;
816     KiBugCheckData[2] = BugCheckParameter2;
817     KiBugCheckData[3] = BugCheckParameter3;
818     KiBugCheckData[4] = BugCheckParameter4;
819 
820     /* Now check what bugcheck this is */
821     switch (BugCheckCode)
822     {
823         /* Invalid access to R/O memory or Unhandled KM Exception */
824         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
825         case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
826         case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
827         {
828             /* Check if we have a trap frame */
829             if (!TrapFrame)
830             {
831                 /* Use parameter 3 as a trap frame, if it exists */
832                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
833             }
834 
835             /* Check if we got one now and if we need to get the Program Counter */
836             if ((TrapFrame) &&
837                 (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
838             {
839                 /* Get the Program Counter */
840                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
841             }
842             break;
843         }
844 
845         /* Wrong IRQL */
846         case IRQL_NOT_LESS_OR_EQUAL:
847         {
848             /*
849              * The NT kernel has 3 special sections:
850              * MISYSPTE, POOLMI and POOLCODE. The bug check code can
851              * determine in which of these sections this bugcode happened
852              * and provide a more detailed analysis. For now, we don't.
853              */
854 
855             /* Program Counter is in parameter 4 */
856             Pc = (PVOID)BugCheckParameter4;
857 
858             /* Get the driver base */
859             DriverBase = KiPcToFileHeader(Pc,
860                                           &LdrEntry,
861                                           FALSE,
862                                           &IsSystem);
863             if (IsSystem)
864             {
865                 /*
866                  * The error happened inside the kernel or HAL.
867                  * Get the memory address that was being referenced.
868                  */
869                 Memory = (PVOID)BugCheckParameter1;
870 
871                 /* Find to which driver it belongs */
872                 DriverBase = KiPcToFileHeader(Memory,
873                                               &LdrEntry,
874                                               TRUE,
875                                               &IsSystem);
876                 if (DriverBase)
877                 {
878                     /* Get the driver name and update the bug code */
879                     KiBugCheckDriver = &LdrEntry->BaseDllName;
880                     KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
881                 }
882                 else
883                 {
884                     /* Find the driver that unloaded at this address */
885                     KiBugCheckDriver = NULL; // FIXME: ROS can't locate
886 
887                     /* Check if the cause was an unloaded driver */
888                     if (KiBugCheckDriver)
889                     {
890                         /* Update bug check code */
891                         KiBugCheckData[0] =
892                             SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
893                     }
894                 }
895             }
896             else
897             {
898                 /* Update the bug check code */
899                 KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
900             }
901 
902             /* Clear Pc so we don't look it up later */
903             Pc = NULL;
904             break;
905         }
906 
907         /* Hard error */
908         case FATAL_UNHANDLED_HARD_ERROR:
909         {
910             /* Copy bug check data from hard error */
911             HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
912             KiBugCheckData[0] = BugCheckParameter1;
913             KiBugCheckData[1] = HardErrorParameters[0];
914             KiBugCheckData[2] = HardErrorParameters[1];
915             KiBugCheckData[3] = HardErrorParameters[2];
916             KiBugCheckData[4] = HardErrorParameters[3];
917 
918             /* Remember that this is hard error and set the caption/message */
919             IsHardError = TRUE;
920             HardErrCaption = (PCHAR)BugCheckParameter3;
921             HardErrMessage = (PCHAR)BugCheckParameter4;
922             break;
923         }
924 
925         /* Page fault */
926         case PAGE_FAULT_IN_NONPAGED_AREA:
927         {
928             /* Assume no driver */
929             DriverBase = NULL;
930 
931             /* Check if we have a trap frame */
932             if (!TrapFrame)
933             {
934                 /* We don't, use parameter 3 if possible */
935                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
936             }
937 
938             /* Check if we have a frame now */
939             if (TrapFrame)
940             {
941                 /* Get the Program Counter */
942                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
943                 KiBugCheckData[3] = (ULONG_PTR)Pc;
944 
945                 /* Find out if was in the kernel or drivers */
946                 DriverBase = KiPcToFileHeader(Pc,
947                                               &LdrEntry,
948                                               FALSE,
949                                               &IsSystem);
950             }
951             else
952             {
953                 /* Can't blame a driver, assume system */
954                 IsSystem = TRUE;
955             }
956 
957             /* FIXME: Check for session pool in addition to special pool */
958 
959             /* Special pool has its own bug check codes */
960             if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
961             {
962                 if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
963                 {
964                     KiBugCheckData[0] = IsSystem
965                         ? PAGE_FAULT_IN_FREED_SPECIAL_POOL
966                         : DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
967                 }
968                 else
969                 {
970                     KiBugCheckData[0] = IsSystem
971                         ? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
972                         : DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
973                 }
974             }
975             else if (!DriverBase)
976             {
977                 /* Find the driver that unloaded at this address */
978                 KiBugCheckDriver = NULL; // FIXME: ROS can't locate
979 
980                 /* Check if the cause was an unloaded driver */
981                 if (KiBugCheckDriver)
982                 {
983                     KiBugCheckData[0] =
984                         DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
985                 }
986             }
987             break;
988         }
989 
990         /* Check if the driver forgot to unlock pages */
991         case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
992 
993             /* Program Counter is in parameter 1 */
994             Pc = (PVOID)BugCheckParameter1;
995             break;
996 
997         /* Check if the driver consumed too many PTEs */
998         case DRIVER_USED_EXCESSIVE_PTES:
999 
1000             /* Loader entry is in parameter 1 */
1001             LdrEntry = (PVOID)BugCheckParameter1;
1002             KiBugCheckDriver = &LdrEntry->BaseDllName;
1003             break;
1004 
1005         /* Check if the driver has a stuck thread */
1006         case THREAD_STUCK_IN_DEVICE_DRIVER:
1007 
1008             /* The name is in Parameter 3 */
1009             KiBugCheckDriver = (PVOID)BugCheckParameter3;
1010             break;
1011 
1012         /* Anything else */
1013         default:
1014             break;
1015     }
1016 
1017     /* Do we have a driver name? */
1018     if (KiBugCheckDriver)
1019     {
1020         /* Convert it to ANSI */
1021         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
1022     }
1023     else
1024     {
1025         /* Do we have a Program Counter? */
1026         if (Pc)
1027         {
1028             /* Dump image name */
1029             KiDumpParameterImages(AnsiName,
1030                                   (PULONG_PTR)&Pc,
1031                                   1,
1032                                   KeBugCheckUnicodeToAnsi);
1033         }
1034     }
1035 
1036     /* Check if we need to save the context for KD */
1037     if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
1038 
1039     /* Check if a debugger is connected */
1040     if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
1041     {
1042         /* Crash on the debugger console */
1043         DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
1044                  "                       (0x%p,0x%p,0x%p,0x%p)\n\n",
1045                  KiBugCheckData[0],
1046                  KiBugCheckData[1],
1047                  KiBugCheckData[2],
1048                  KiBugCheckData[3],
1049                  KiBugCheckData[4]);
1050 
1051         /* Check if the debugger isn't currently connected */
1052         if (!KdDebuggerNotPresent)
1053         {
1054             /* Check if we have a driver to blame */
1055             if (KiBugCheckDriver)
1056             {
1057                 /* Dump it */
1058                 DbgPrint("Driver at fault: %s.\n", AnsiName);
1059             }
1060 
1061             /* Check if this was a hard error */
1062             if (IsHardError)
1063             {
1064                 /* Print caption and message */
1065                 if (HardErrCaption) DbgPrint(HardErrCaption);
1066                 if (HardErrMessage) DbgPrint(HardErrMessage);
1067             }
1068 
1069             /* Break in the debugger */
1070             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
1071         }
1072     }
1073 
1074     /* Raise IRQL to HIGH_LEVEL */
1075     _disable();
1076     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1077 
1078     /* Avoid recursion */
1079     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1080     {
1081 #ifdef CONFIG_SMP
1082         /* Set CPU that is bug checking now */
1083         KeBugCheckOwner = Prcb->Number;
1084 
1085         /* Freeze the other CPUs */
1086         for (i = 0; i < KeNumberProcessors; i++)
1087         {
1088             if (i != (LONG)KeGetCurrentProcessorNumber())
1089             {
1090                 /* Send the IPI and give them one second to catch up */
1091                 KiIpiSend(1 << i, IPI_FREEZE);
1092                 KeStallExecutionProcessor(1000000);
1093             }
1094         }
1095 #endif
1096 
1097         /* Display the BSOD */
1098         KiDisplayBlueScreen(MessageId,
1099                             IsHardError,
1100                             HardErrCaption,
1101                             HardErrMessage,
1102                             AnsiName);
1103 
1104         // TODO/FIXME: Run the registered reason-callbacks from
1105         // the KeBugcheckReasonCallbackListHead list with the
1106         // KbCallbackReserved1 reason.
1107 
1108         /* Check if the debugger is disabled but we can enable it */
1109         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1110         {
1111             /* Enable it */
1112             KdEnableDebuggerWithLock(FALSE);
1113         }
1114         else
1115         {
1116             /* Otherwise, print the last line */
1117             InbvDisplayString("\r\n");
1118         }
1119 
1120         /* Save the context */
1121         Prcb->ProcessorState.ContextFrame = Context;
1122 
1123         /* FIXME: Support Triage Dump */
1124 
1125         /* FIXME: Write the crash dump */
1126     }
1127     else
1128     {
1129         /* Increase recursion count */
1130         KeBugCheckOwnerRecursionCount++;
1131         if (KeBugCheckOwnerRecursionCount == 2)
1132         {
1133             /* Break in the debugger */
1134             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1135         }
1136         else if (KeBugCheckOwnerRecursionCount > 2)
1137         {
1138             /* Halt execution */
1139             while (TRUE);
1140         }
1141     }
1142 
1143     /* Call the Callbacks */
1144     KiDoBugCheckCallbacks();
1145 
1146     /* FIXME: Call Watchdog if enabled */
1147 
1148     /* Check if we have to reboot */
1149     if (Reboot)
1150     {
1151         /* Unload symbols */
1152         DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
1153         HalReturnToFirmware(HalRebootRoutine);
1154     }
1155 
1156     /* Attempt to break in the debugger (otherwise halt CPU) */
1157     KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1158 
1159     /* Shouldn't get here */
1160     ASSERT(FALSE);
1161     while (TRUE);
1162 }
1163 
1164 BOOLEAN
1165 NTAPI
1166 KiHandleNmi(VOID)
1167 {
1168     BOOLEAN Handled = FALSE;
1169     PKNMI_HANDLER_CALLBACK NmiData;
1170 
1171     /* Parse the list of callbacks */
1172     NmiData = KiNmiCallbackListHead;
1173     while (NmiData)
1174     {
1175         /* Save if this callback has handled it -- all it takes is one */
1176         Handled |= NmiData->Callback(NmiData->Context, Handled);
1177         NmiData = NmiData->Next;
1178     }
1179 
1180     /* Has anyone handled this? */
1181     return Handled;
1182 }
1183 
1184 /* PUBLIC FUNCTIONS **********************************************************/
1185 
1186 /*
1187  * @unimplemented
1188  */
1189 NTSTATUS
1190 NTAPI
1191 KeInitializeCrashDumpHeader(IN ULONG Type,
1192                             IN ULONG Flags,
1193                             OUT PVOID Buffer,
1194                             IN ULONG BufferSize,
1195                             OUT ULONG BufferNeeded OPTIONAL)
1196 {
1197     UNIMPLEMENTED;
1198     return STATUS_UNSUCCESSFUL;
1199 }
1200 
1201 /*
1202  * @implemented
1203  */
1204 BOOLEAN
1205 NTAPI
1206 KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
1207 {
1208     KIRQL OldIrql;
1209     BOOLEAN Status = FALSE;
1210 
1211     /* Raise IRQL to High */
1212     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1213 
1214     /* Check the Current State */
1215     if (CallbackRecord->State == BufferInserted)
1216     {
1217         /* Reset state and remove from list */
1218         CallbackRecord->State = BufferEmpty;
1219         RemoveEntryList(&CallbackRecord->Entry);
1220         Status = TRUE;
1221     }
1222 
1223     /* Lower IRQL and return */
1224     KeLowerIrql(OldIrql);
1225     return Status;
1226 }
1227 
1228 /*
1229  * @implemented
1230  */
1231 BOOLEAN
1232 NTAPI
1233 KeDeregisterBugCheckReasonCallback(
1234     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
1235 {
1236     KIRQL OldIrql;
1237     BOOLEAN Status = FALSE;
1238 
1239     /* Raise IRQL to High */
1240     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1241 
1242     /* Check the Current State */
1243     if (CallbackRecord->State == BufferInserted)
1244     {
1245         /* Reset state and remove from list */
1246         CallbackRecord->State = BufferEmpty;
1247         RemoveEntryList(&CallbackRecord->Entry);
1248         Status = TRUE;
1249     }
1250 
1251     /* Lower IRQL and return */
1252     KeLowerIrql(OldIrql);
1253     return Status;
1254 }
1255 
1256 /*
1257  * @implemented
1258  */
1259 BOOLEAN
1260 NTAPI
1261 KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
1262                            IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
1263                            IN PVOID Buffer,
1264                            IN ULONG Length,
1265                            IN PUCHAR Component)
1266 {
1267     KIRQL OldIrql;
1268     BOOLEAN Status = FALSE;
1269 
1270     /* Raise IRQL to High */
1271     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1272 
1273     /* Check the Current State first so we don't double-register */
1274     if (CallbackRecord->State == BufferEmpty)
1275     {
1276         /* Set the Callback Settings and insert into the list */
1277         CallbackRecord->Length = Length;
1278         CallbackRecord->Buffer = Buffer;
1279         CallbackRecord->Component = Component;
1280         CallbackRecord->CallbackRoutine = CallbackRoutine;
1281         CallbackRecord->State = BufferInserted;
1282         InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
1283         Status = TRUE;
1284     }
1285 
1286     /* Lower IRQL and return */
1287     KeLowerIrql(OldIrql);
1288     return Status;
1289 }
1290 
1291 /*
1292  * @implemented
1293  */
1294 BOOLEAN
1295 NTAPI
1296 KeRegisterBugCheckReasonCallback(
1297     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
1298     IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
1299     IN KBUGCHECK_CALLBACK_REASON Reason,
1300     IN PUCHAR Component)
1301 {
1302     KIRQL OldIrql;
1303     BOOLEAN Status = FALSE;
1304 
1305     /* Raise IRQL to High */
1306     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1307 
1308     /* Check the Current State first so we don't double-register */
1309     if (CallbackRecord->State == BufferEmpty)
1310     {
1311         /* Set the Callback Settings and insert into the list */
1312         CallbackRecord->Component = Component;
1313         CallbackRecord->CallbackRoutine = CallbackRoutine;
1314         CallbackRecord->State = BufferInserted;
1315         CallbackRecord->Reason = Reason;
1316         InsertTailList(&KeBugcheckReasonCallbackListHead,
1317                        &CallbackRecord->Entry);
1318         Status = TRUE;
1319     }
1320 
1321     /* Lower IRQL and return */
1322     KeLowerIrql(OldIrql);
1323     return Status;
1324 }
1325 
1326 /*
1327  * @implemented
1328  */
1329 PVOID
1330 NTAPI
1331 KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
1332                       IN PVOID Context)
1333 {
1334     KIRQL OldIrql;
1335     PKNMI_HANDLER_CALLBACK NmiData, Next;
1336     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1337 
1338     /* Allocate NMI callback data */
1339     NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
1340     if (!NmiData) return NULL;
1341 
1342     /* Fill in the information */
1343     NmiData->Callback = CallbackRoutine;
1344     NmiData->Context = Context;
1345     NmiData->Handle = NmiData;
1346 
1347     /* Insert it into NMI callback list */
1348     KiAcquireNmiListLock(&OldIrql);
1349     NmiData->Next = KiNmiCallbackListHead;
1350     Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
1351                                              NmiData,
1352                                              NmiData->Next);
1353     ASSERT(Next == NmiData->Next);
1354     KiReleaseNmiListLock(OldIrql);
1355 
1356     /* Return the opaque "handle" */
1357     return NmiData->Handle;
1358 }
1359 
1360 /*
1361  * @implemented
1362  */
1363 NTSTATUS
1364 NTAPI
1365 KeDeregisterNmiCallback(IN PVOID Handle)
1366 {
1367     KIRQL OldIrql;
1368     PKNMI_HANDLER_CALLBACK NmiData;
1369     PKNMI_HANDLER_CALLBACK* Previous;
1370     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1371 
1372     /* Find in the list the NMI callback corresponding to the handle */
1373     KiAcquireNmiListLock(&OldIrql);
1374     Previous = &KiNmiCallbackListHead;
1375     NmiData = *Previous;
1376     while (NmiData)
1377     {
1378         if (NmiData->Handle == Handle)
1379         {
1380             /* The handle is the pointer to the callback itself */
1381             ASSERT(Handle == NmiData);
1382 
1383             /* Found it, remove from the list */
1384             *Previous = NmiData->Next;
1385             break;
1386         }
1387 
1388         /* Not found; try again */
1389         Previous = &NmiData->Next;
1390         NmiData = *Previous;
1391     }
1392     KiReleaseNmiListLock(OldIrql);
1393 
1394     /* If we have found the entry, free it */
1395     if (NmiData)
1396     {
1397         ExFreePoolWithTag(NmiData, TAG_KNMI);
1398         return STATUS_SUCCESS;
1399     }
1400 
1401     return STATUS_INVALID_HANDLE;
1402 }
1403 
1404 /*
1405  * @implemented
1406  */
1407 DECLSPEC_NORETURN
1408 VOID
1409 NTAPI
1410 KeBugCheckEx(IN ULONG BugCheckCode,
1411              IN ULONG_PTR BugCheckParameter1,
1412              IN ULONG_PTR BugCheckParameter2,
1413              IN ULONG_PTR BugCheckParameter3,
1414              IN ULONG_PTR BugCheckParameter4)
1415 {
1416     /* Call the internal API */
1417     KeBugCheckWithTf(BugCheckCode,
1418                      BugCheckParameter1,
1419                      BugCheckParameter2,
1420                      BugCheckParameter3,
1421                      BugCheckParameter4,
1422                      NULL);
1423 }
1424 
1425 /*
1426  * @implemented
1427  */
1428 DECLSPEC_NORETURN
1429 VOID
1430 NTAPI
1431 KeBugCheck(ULONG BugCheckCode)
1432 {
1433     /* Call the internal API */
1434     KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
1435 }
1436 
1437 /*
1438  * @implemented
1439  */
1440 VOID
1441 NTAPI
1442 KeEnterKernelDebugger(VOID)
1443 {
1444     /* Disable interrupts */
1445     KiHardwareTrigger = 1;
1446     _disable();
1447 
1448     /* Check the bugcheck count */
1449     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1450     {
1451         /* There was only one, is the debugger disabled? */
1452         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1453         {
1454             /* Enable the debugger */
1455             KdInitSystem(0, NULL);
1456         }
1457     }
1458 
1459     /* Break in the debugger */
1460     KiBugCheckDebugBreak(DBG_STATUS_FATAL);
1461 }
1462 
1463 /* EOF */
1464