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