xref: /reactos/ntoskrnl/ke/bug.c (revision ede7a20a)
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 #define NDEBUG
14 #include <debug.h>
15 
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, KiInitializeBugCheck)
18 #endif
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 #define TAG_KNMI 'IMNK'
36 
37 /* Bugzilla Reporting */
38 UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
39 UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
40 
41 /* PRIVATE FUNCTIONS *********************************************************/
42 
43 PVOID
44 NTAPI
45 KiPcToFileHeader(IN PVOID Pc,
46                  OUT PLDR_DATA_TABLE_ENTRY *LdrEntry,
47                  IN BOOLEAN DriversOnly,
48                  OUT PBOOLEAN InKernel)
49 {
50     ULONG i = 0;
51     PVOID ImageBase, PcBase = NULL;
52     PLDR_DATA_TABLE_ENTRY Entry;
53     PLIST_ENTRY ListHead, NextEntry;
54 
55     /* Check which list we should use */
56     ListHead = (KeLoaderBlock) ? &KeLoaderBlock->LoadOrderListHead :
57                                  &PsLoadedModuleList;
58 
59     /* Assume no */
60     *InKernel = FALSE;
61 
62     /* Set list pointers and make sure it's valid */
63     NextEntry = ListHead->Flink;
64     if (NextEntry)
65     {
66         /* Start loop */
67         while (NextEntry != ListHead)
68         {
69             /* Increase entry */
70             i++;
71 
72             /* Check if this is a kernel entry and we only want drivers */
73             if ((i <= 2) && (DriversOnly != FALSE))
74             {
75                 /* Skip it */
76                 NextEntry = NextEntry->Flink;
77                 continue;
78             }
79 
80             /* Get the loader entry */
81             Entry = CONTAINING_RECORD(NextEntry,
82                                       LDR_DATA_TABLE_ENTRY,
83                                       InLoadOrderLinks);
84 
85             /* Move to the next entry */
86             NextEntry = NextEntry->Flink;
87             ImageBase = Entry->DllBase;
88 
89             /* Check if this is the right one */
90             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
91                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
92             {
93                 /* Return this entry */
94                 *LdrEntry = Entry;
95                 PcBase = ImageBase;
96 
97                 /* Check if this was a kernel or HAL entry */
98                 if (i <= 2) *InKernel = TRUE;
99                 break;
100             }
101         }
102     }
103 
104     /* Return the base address */
105     return PcBase;
106 }
107 
108 PVOID
109 NTAPI
110 KiRosPcToUserFileHeader(IN PVOID Pc,
111                         OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
112 {
113     PVOID ImageBase, PcBase = NULL;
114     PLDR_DATA_TABLE_ENTRY Entry;
115     PLIST_ENTRY ListHead, NextEntry;
116 
117     /*
118      * We know this is valid because we should only be called after a
119      * succesfull address from RtlWalkFrameChain for UserMode, which
120      * validates everything for us.
121      */
122     ListHead = &KeGetCurrentThread()->
123                Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList;
124 
125     /* Set list pointers and make sure it's valid */
126     NextEntry = ListHead->Flink;
127     if (NextEntry)
128     {
129         /* Start loop */
130         while (NextEntry != ListHead)
131         {
132             /* Get the loader entry */
133             Entry = CONTAINING_RECORD(NextEntry,
134                                       LDR_DATA_TABLE_ENTRY,
135                                       InLoadOrderLinks);
136 
137             /* Move to the next entry */
138             NextEntry = NextEntry->Flink;
139             ImageBase = Entry->DllBase;
140 
141             /* Check if this is the right one */
142             if (((ULONG_PTR)Pc >= (ULONG_PTR)Entry->DllBase) &&
143                 ((ULONG_PTR)Pc < ((ULONG_PTR)Entry->DllBase + Entry->SizeOfImage)))
144             {
145                 /* Return this entry */
146                 *LdrEntry = Entry;
147                 PcBase = ImageBase;
148                 break;
149             }
150         }
151     }
152 
153     /* Return the base address */
154     return PcBase;
155 }
156 
157 USHORT
158 NTAPI
159 KeRosCaptureUserStackBackTrace(IN ULONG FramesToSkip,
160                                IN ULONG FramesToCapture,
161                                OUT PVOID *BackTrace,
162                                OUT PULONG BackTraceHash OPTIONAL)
163 {
164     PVOID Frames[2 * 64];
165     ULONG FrameCount;
166     ULONG Hash = 0, i;
167 
168     /* Skip a frame for the caller */
169     FramesToSkip++;
170 
171     /* Don't go past the limit */
172     if ((FramesToCapture + FramesToSkip) >= 128) return 0;
173 
174     /* Do the back trace */
175     FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 1);
176 
177     /* Make sure we're not skipping all of them */
178     if (FrameCount <= FramesToSkip) return 0;
179 
180     /* Loop all the frames */
181     for (i = 0; i < FramesToCapture; i++)
182     {
183         /* Don't go past the limit */
184         if ((FramesToSkip + i) >= FrameCount) break;
185 
186         /* Save this entry and hash it */
187         BackTrace[i] = Frames[FramesToSkip + i];
188         Hash += PtrToUlong(BackTrace[i]);
189     }
190 
191     /* Write the hash */
192     if (BackTraceHash) *BackTraceHash = Hash;
193 
194     /* Clear the other entries and return count */
195     RtlFillMemoryUlong(Frames, 128, 0);
196     return (USHORT)i;
197 }
198 
199 
200 VOID
201 FASTCALL
202 KeRosDumpStackFrameArray(IN PULONG_PTR Frames,
203                          IN ULONG FrameCount)
204 {
205     ULONG i;
206     ULONG_PTR Addr;
207     BOOLEAN InSystem;
208     PVOID p;
209 
210     /* GCC complaints that it may be used uninitialized */
211     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
212 
213     /* Loop them */
214     for (i = 0; i < FrameCount; i++)
215     {
216         /* Get the EIP */
217         Addr = Frames[i];
218         if (!Addr)
219         {
220         	break;
221         }
222 
223         /* Get the base for this file */
224         if (Addr > (ULONG_PTR)MmHighestUserAddress)
225         {
226             /* We are in kernel */
227             p = KiPcToFileHeader((PVOID)Addr, &LdrEntry, FALSE, &InSystem);
228         }
229         else
230         {
231             /* We are in user land */
232             p = KiRosPcToUserFileHeader((PVOID)Addr, &LdrEntry);
233         }
234         if (p)
235         {
236 #ifdef KDBG
237             if (!KdbSymPrintAddress((PVOID)Addr, NULL))
238 #endif
239             {
240                 CHAR AnsiName[64];
241 
242                 /* Convert module name to ANSI and print it */
243                 KeBugCheckUnicodeToAnsi(&LdrEntry->BaseDllName,
244                                         AnsiName,
245                                         sizeof(AnsiName));
246                 Addr -= (ULONG_PTR)LdrEntry->DllBase;
247                 DbgPrint("<%s: %p>", AnsiName, (PVOID)Addr);
248             }
249         }
250         else
251         {
252             /* Print only the address */
253             DbgPrint("<%p>", (PVOID)Addr);
254         }
255 
256         /* Go to the next frame */
257         DbgPrint("\n");
258     }
259 }
260 
261 VOID
262 NTAPI
263 KeRosDumpStackFrames(IN PULONG_PTR Frame OPTIONAL,
264                      IN ULONG FrameCount OPTIONAL)
265 {
266     ULONG_PTR Frames[32];
267     ULONG RealFrameCount;
268 
269     /* If the caller didn't ask, assume 32 frames */
270     if (!FrameCount || FrameCount > 32) FrameCount = 32;
271 
272     if (Frame)
273     {
274         /* Dump them */
275         KeRosDumpStackFrameArray(Frame, FrameCount);
276     }
277     else
278     {
279         /* Get the current frames (skip the two. One for the dumper, one for the caller) */
280         RealFrameCount = RtlCaptureStackBackTrace(2, FrameCount, (PVOID*)Frames, NULL);
281         DPRINT1("RealFrameCount =%lu\n", RealFrameCount);
282 
283         /* Dump them */
284         KeRosDumpStackFrameArray(Frames, RealFrameCount);
285 
286         /* Count left for user mode? */
287         if (FrameCount - RealFrameCount > 0)
288         {
289             /* Get the current frames */
290             RealFrameCount = KeRosCaptureUserStackBackTrace(-1, FrameCount - RealFrameCount, (PVOID*)Frames, NULL);
291 
292             /* Dump them */
293             KeRosDumpStackFrameArray(Frames, RealFrameCount);
294         }
295     }
296 }
297 
298 INIT_FUNCTION
299 VOID
300 NTAPI
301 KiInitializeBugCheck(VOID)
302 {
303     PMESSAGE_RESOURCE_DATA BugCheckData;
304     LDR_RESOURCE_INFO ResourceInfo;
305     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
306     NTSTATUS Status;
307     PLDR_DATA_TABLE_ENTRY LdrEntry;
308 
309     /* Get the kernel entry */
310     LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
311                                  LDR_DATA_TABLE_ENTRY,
312                                  InLoadOrderLinks);
313 
314     /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
315     ResourceInfo.Type = 11;
316     ResourceInfo.Name = 1;
317     ResourceInfo.Language = 9;
318 
319     /* Do the lookup. */
320     Status = LdrFindResource_U(LdrEntry->DllBase,
321                                &ResourceInfo,
322                                RESOURCE_DATA_LEVEL,
323                                &ResourceDataEntry);
324 
325     /* Make sure it worked */
326     if (NT_SUCCESS(Status))
327     {
328         /* Now actually get a pointer to it */
329         Status = LdrAccessResource(LdrEntry->DllBase,
330                                    ResourceDataEntry,
331                                    (PVOID*)&BugCheckData,
332                                    NULL);
333         if (NT_SUCCESS(Status)) KiBugCodeMessages = BugCheckData;
334     }
335 }
336 
337 BOOLEAN
338 NTAPI
339 KeGetBugMessageText(IN ULONG BugCheckCode,
340                     OUT PANSI_STRING OutputString OPTIONAL)
341 {
342     ULONG i;
343     ULONG IdOffset;
344     PMESSAGE_RESOURCE_ENTRY MessageEntry;
345     PCHAR BugCode;
346     USHORT Length;
347     BOOLEAN Result = FALSE;
348 
349     /* Make sure we're not bugchecking too early */
350     if (!KiBugCodeMessages) return Result;
351 
352     /*
353      * Globally protect in SEH as we are trying to access data in
354      * dire situations, and potentially going to patch it (see below).
355      */
356     _SEH2_TRY
357     {
358 
359     /*
360      * Make the kernel resource section writable, as we are going to manually
361      * trim the trailing newlines in the bugcheck resource message in place,
362      * when OutputString is NULL and before displaying it on screen.
363      */
364     MmMakeKernelResourceSectionWritable();
365 
366     /* Find the message. This code is based on RtlFindMesssage */
367     for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
368     {
369         /* Check if the ID matches */
370         if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
371             (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
372         {
373             /* Get offset to entry */
374             MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
375                 ((ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries);
376             IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
377 
378             /* Advance in the entries until finding it */
379             while (IdOffset--)
380             {
381                 MessageEntry = (PMESSAGE_RESOURCE_ENTRY)
382                     ((ULONG_PTR)MessageEntry + MessageEntry->Length);
383             }
384 
385             /* Make sure it's not Unicode */
386             ASSERT(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE));
387 
388             /* Get the final code */
389             BugCode = (PCHAR)MessageEntry->Text;
390             Length = (USHORT)strlen(BugCode);
391 
392             /* Handle trailing newlines */
393             while ((Length > 0) && ((BugCode[Length - 1] == '\n') ||
394                                     (BugCode[Length - 1] == '\r') ||
395                                     (BugCode[Length - 1] == ANSI_NULL)))
396             {
397                 /* Directly trim the newline in place if we don't return the string */
398                 if (!OutputString) BugCode[Length - 1] = ANSI_NULL;
399 
400                 /* Skip the trailing newline */
401                 Length--;
402             }
403 
404             /* Check if caller wants an output string */
405             if (OutputString)
406             {
407                 /* Return it in the OutputString */
408                 OutputString->Buffer = BugCode;
409                 OutputString->Length = Length;
410                 OutputString->MaximumLength = Length;
411             }
412             else
413             {
414                 /* Direct output to screen */
415                 InbvDisplayString(BugCode);
416                 InbvDisplayString("\r");
417             }
418 
419             /* We're done */
420             Result = TRUE;
421             break;
422         }
423     }
424 
425     }
426     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
427     {
428     }
429     _SEH2_END;
430 
431     /* Return the result */
432     return Result;
433 }
434 
435 VOID
436 NTAPI
437 KiDoBugCheckCallbacks(VOID)
438 {
439     PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
440     PLIST_ENTRY ListHead, NextEntry, LastEntry;
441     ULONG_PTR Checksum;
442 
443     /* First make sure that the list is initialized... it might not be */
444     ListHead = &KeBugcheckCallbackListHead;
445     if ((!ListHead->Flink) || (!ListHead->Blink))
446         return;
447 
448     /* Loop the list */
449     LastEntry = ListHead;
450     NextEntry = ListHead->Flink;
451     while (NextEntry != ListHead)
452     {
453         /* Get the reord */
454         CurrentRecord = CONTAINING_RECORD(NextEntry,
455                                           KBUGCHECK_CALLBACK_RECORD,
456                                           Entry);
457 
458         /* Validate it */
459         // TODO/FIXME: Check whether the memory CurrentRecord points to
460         // is still accessible and valid!
461         if (CurrentRecord->Entry.Blink != LastEntry) return;
462         Checksum = (ULONG_PTR)CurrentRecord->CallbackRoutine;
463         Checksum += (ULONG_PTR)CurrentRecord->Buffer;
464         Checksum += (ULONG_PTR)CurrentRecord->Length;
465         Checksum += (ULONG_PTR)CurrentRecord->Component;
466 
467         /* Make sure it's inserted and validated */
468         if ((CurrentRecord->State == BufferInserted) &&
469             (CurrentRecord->Checksum == Checksum))
470         {
471             /* Call the routine */
472             CurrentRecord->State = BufferStarted;
473             _SEH2_TRY
474             {
475                 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
476                                                  CurrentRecord->Length);
477                 CurrentRecord->State = BufferFinished;
478             }
479             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
480             {
481                 CurrentRecord->State = BufferIncomplete;
482             }
483             _SEH2_END;
484         }
485 
486         /* Go to the next entry */
487         LastEntry = NextEntry;
488         NextEntry = NextEntry->Flink;
489     }
490 }
491 
492 VOID
493 NTAPI
494 KiBugCheckDebugBreak(IN ULONG StatusCode)
495 {
496     /*
497      * Wrap this in SEH so we don't crash if
498      * there is no debugger or if it disconnected
499      */
500 DoBreak:
501     _SEH2_TRY
502     {
503         /* Breakpoint */
504         DbgBreakPointWithStatus(StatusCode);
505     }
506     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
507     {
508         /* No debugger, halt the CPU */
509         HalHaltSystem();
510     }
511     _SEH2_END;
512 
513     /* Break again if this wasn't first try */
514     if (StatusCode != DBG_STATUS_BUGCHECK_FIRST) goto DoBreak;
515 }
516 
517 PCHAR
518 NTAPI
519 KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING Unicode,
520                         OUT PCHAR Ansi,
521                         IN ULONG Length)
522 {
523     PCHAR p;
524     PWCHAR pw;
525     ULONG i;
526 
527     /* Set length and normalize it */
528     i = Unicode->Length / sizeof(WCHAR);
529     i = min(i, Length - 1);
530 
531     /* Set source and destination, and copy */
532     pw = Unicode->Buffer;
533     p = Ansi;
534     while (i--) *p++ = (CHAR)*pw++;
535 
536     /* Null terminate and return */
537     *p = ANSI_NULL;
538     return Ansi;
539 }
540 
541 VOID
542 NTAPI
543 KiDumpParameterImages(IN PCHAR Message,
544                       IN PULONG_PTR Parameters,
545                       IN ULONG ParameterCount,
546                       IN PKE_BUGCHECK_UNICODE_TO_ANSI ConversionRoutine)
547 {
548     ULONG i;
549     BOOLEAN InSystem;
550     PLDR_DATA_TABLE_ENTRY LdrEntry;
551     PVOID ImageBase;
552     PUNICODE_STRING DriverName;
553     CHAR AnsiName[32];
554     PIMAGE_NT_HEADERS NtHeader;
555     ULONG TimeStamp;
556     BOOLEAN FirstRun = TRUE;
557 
558     /* Loop parameters */
559     for (i = 0; i < ParameterCount; i++)
560     {
561         /* Get the base for this parameter */
562         ImageBase = KiPcToFileHeader((PVOID)Parameters[i],
563                                      &LdrEntry,
564                                      FALSE,
565                                      &InSystem);
566         if (!ImageBase)
567         {
568             /* FIXME: Add code to check for unloaded drivers */
569             DPRINT1("Potentially unloaded driver!\n");
570             continue;
571         }
572         else
573         {
574             /* Get the NT Headers and Timestamp */
575             NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
576             TimeStamp = NtHeader->FileHeader.TimeDateStamp;
577 
578             /* Convert the driver name */
579             DriverName = &LdrEntry->BaseDllName;
580             ConversionRoutine(&LdrEntry->BaseDllName,
581                               AnsiName,
582                               sizeof(AnsiName));
583         }
584 
585         /* Format driver name */
586         sprintf(Message,
587                 "%s**  %12s - Address %p base at %p, DateStamp %08lx\r\n",
588                 FirstRun ? "\r\n*":"*",
589                 AnsiName,
590                 (PVOID)Parameters[i],
591                 ImageBase,
592                 TimeStamp);
593 
594         /* Check if we only had one parameter */
595         if (ParameterCount <= 1)
596         {
597             /* Then just save the name */
598             KiBugCheckDriver = DriverName;
599         }
600         else
601         {
602             /* Otherwise, display the message */
603             InbvDisplayString(Message);
604         }
605 
606         /* Loop again */
607         FirstRun = FALSE;
608     }
609 }
610 
611 VOID
612 NTAPI
613 KiDisplayBlueScreen(IN ULONG MessageId,
614                     IN BOOLEAN IsHardError,
615                     IN PCHAR HardErrCaption OPTIONAL,
616                     IN PCHAR HardErrMessage OPTIONAL,
617                     IN PCHAR Message)
618 {
619     CHAR AnsiName[75];
620 
621     /* Check if bootvid is installed */
622     if (InbvIsBootDriverInstalled())
623     {
624         /* Acquire ownership and reset the display */
625         InbvAcquireDisplayOwnership();
626         InbvResetDisplay();
627 
628         /* Display blue screen */
629         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLUE);
630         InbvSetTextColor(BV_COLOR_WHITE);
631         InbvInstallDisplayStringFilter(NULL);
632         InbvEnableDisplayString(TRUE);
633         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
634     }
635 
636     /* Check if this is a hard error */
637     if (IsHardError)
638     {
639         /* Display caption and message */
640         if (HardErrCaption) InbvDisplayString(HardErrCaption);
641         if (HardErrMessage) InbvDisplayString(HardErrMessage);
642     }
643 
644     /* Begin the display */
645     InbvDisplayString("\r\n");
646 
647     /* Print out initial message */
648     KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
649     InbvDisplayString("\r\n\r\n");
650 
651     /* Check if we have a driver */
652     if (KiBugCheckDriver)
653     {
654         /* Print out into to driver name */
655         KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
656 
657         /* Convert and print out driver name */
658         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
659         InbvDisplayString(" ");
660         InbvDisplayString(AnsiName);
661         InbvDisplayString("\r\n\r\n");
662     }
663 
664     /* Check if this is the generic message */
665     if (MessageId == BUGCODE_PSS_MESSAGE)
666     {
667         /* It is, so get the bug code string as well */
668         KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
669         InbvDisplayString("\r\n\r\n");
670     }
671 
672     /* Print second introduction message */
673     KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
674     InbvDisplayString("\r\n\r\n");
675 
676     /* Get the bug code string */
677     KeGetBugMessageText(MessageId, NULL);
678     InbvDisplayString("\r\n\r\n");
679 
680     /* Print message for technical information */
681     KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
682 
683     /* Show the technical Data */
684     sprintf(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