xref: /reactos/ntoskrnl/ke/bug.c (revision ba3f0743)
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 #define NDEBUG
13 #include <debug.h>
14 
15 #if defined (ALLOC_PRAGMA)
16 #pragma alloc_text(INIT, KiInitializeBugCheck)
17 #endif
18 
19 /* GLOBALS *******************************************************************/
20 
21 LIST_ENTRY KeBugcheckCallbackListHead;
22 LIST_ENTRY KeBugcheckReasonCallbackListHead;
23 KSPIN_LOCK BugCheckCallbackLock;
24 ULONG KeBugCheckActive, KeBugCheckOwner;
25 LONG KeBugCheckOwnerRecursionCount;
26 PMESSAGE_RESOURCE_DATA KiBugCodeMessages;
27 ULONG KeBugCheckCount = 1;
28 ULONG KiHardwareTrigger;
29 PUNICODE_STRING KiBugCheckDriver;
30 ULONG_PTR KiBugCheckData[5];
31 
32 PKNMI_HANDLER_CALLBACK KiNmiCallbackListHead = NULL;
33 KSPIN_LOCK KiNmiCallbackListLock;
34 #define TAG_KNMI 'IMNK'
35 
36 /* Bugzilla 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 VOID
298 NTAPI
299 KeRosDumpTriageForBugZillaReport(VOID)
300 {
301 #if 0
302     extern BOOLEAN KiFastSystemCallDisable, KiSMTProcessorsPresent;
303     extern ULONG KeI386MachineType, MxcsrFeatureMask;
304     extern BOOLEAN Ke386Pae, Ke386NoExecute;
305 
306     DbgPrint("ReactOS has crashed! Please go to http://jira.reactos.org/ to file a bug!\n");
307     DbgPrint("\nHardware Information\n");
308     DbgPrint("Processor Architecture: %d\n"
309              "Feature Bits: %d\n"
310              "System Call Disabled: %d\n"
311              "NPX Present: %d\n"
312              "MXCsr Mask: %d\n"
313              "MXCsr Feature Mask: %d\n"
314              "XMMI Present: %d\n"
315              "FXSR Present: %d\n"
316              "Machine Type: %d\n"
317              "PAE: %d\n"
318              "NX: %d\n"
319              "Processors: %d\n"
320              "Active Processors: %d\n"
321              "Pentium LOCK Bug: %d\n"
322              "Hyperthreading: %d\n"
323              "CPU Manufacturer: %s\n"
324              "CPU Name: %wZ\n"
325              "CPUID: %d\n"
326              "CPU Type: %d\n"
327              "CPU Stepping: %d\n"
328              "CPU Speed: %d\n"
329              "CPU L2 Cache: %d\n"
330              "BIOS Date: %wZ\n"
331              "BIOS Version: %wZ\n"
332              "Video BIOS Date: %wZ\n"
333              "Video BIOS Version: %wZ\n"
334              "Memory: %d\n",
335              KeProcessorArchitecture,
336              KeFeatureBits,
337              KiFastSystemCallDisable,
338              KeI386NpxPresent,
339              KiMXCsrMask,
340              MxcsrFeatureMask,
341              KeI386XMMIPresent,
342              KeI386FxsrPresent,
343              KeI386MachineType,
344              Ke386Pae,
345              Ke386NoExecute,
346              KeNumberProcessors,
347              KeActiveProcessors,
348              KiI386PentiumLockErrataPresent,
349              KiSMTProcessorsPresent,
350              KeGetCurrentPrcb()->VendorString,
351              &KeRosProcessorName,
352              KeGetCurrentPrcb()->CpuID,
353              KeGetCurrentPrcb()->CpuType,
354              KeGetCurrentPrcb()->CpuStep,
355              KeGetCurrentPrcb()->MHz,
356              ((PKIPCR)KeGetPcr())->SecondLevelCacheSize,
357              &KeRosBiosDate,
358              &KeRosBiosVersion,
359              &KeRosVideoBiosDate,
360              &KeRosVideoBiosVersion,
361              MmNumberOfPhysicalPages * PAGE_SIZE);
362 #endif
363 }
364 
365 VOID
366 INIT_FUNCTION
367 NTAPI
368 KiInitializeBugCheck(VOID)
369 {
370     PMESSAGE_RESOURCE_DATA BugCheckData;
371     LDR_RESOURCE_INFO ResourceInfo;
372     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
373     NTSTATUS Status;
374     PLDR_DATA_TABLE_ENTRY LdrEntry;
375 
376     /* Get the kernel entry */
377     LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
378                                  LDR_DATA_TABLE_ENTRY,
379                                  InLoadOrderLinks);
380 
381     /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */
382     ResourceInfo.Type = 11;
383     ResourceInfo.Name = 1;
384     ResourceInfo.Language = 9;
385 
386     /* Do the lookup. */
387     Status = LdrFindResource_U(LdrEntry->DllBase,
388                                &ResourceInfo,
389                                RESOURCE_DATA_LEVEL,
390                                &ResourceDataEntry);
391 
392     /* Make sure it worked */
393     if (NT_SUCCESS(Status))
394     {
395         /* Now actually get a pointer to it */
396         Status = LdrAccessResource(LdrEntry->DllBase,
397                                    ResourceDataEntry,
398                                    (PVOID*)&BugCheckData,
399                                    NULL);
400         if (NT_SUCCESS(Status)) KiBugCodeMessages = BugCheckData;
401     }
402 }
403 
404 BOOLEAN
405 NTAPI
406 KeGetBugMessageText(IN ULONG BugCheckCode,
407                     OUT PANSI_STRING OutputString OPTIONAL)
408 {
409     ULONG i, j;
410     ULONG IdOffset;
411     ULONG_PTR MessageEntry;
412     PCHAR BugCode;
413     BOOLEAN Result = FALSE;
414     USHORT Length;
415 
416     /* Make sure we're not bugchecking too early */
417     if (!KiBugCodeMessages) return Result;
418 
419     /* Find the message. This code is based on RtlFindMesssage */
420     for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++)
421     {
422         /* Check if the ID Matches */
423         if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) &&
424             (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId))
425         {
426             /* Get Offset to Entry */
427             MessageEntry = KiBugCodeMessages->Blocks[i].OffsetToEntries +
428                            (ULONG_PTR)KiBugCodeMessages;
429             IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId;
430 
431             /* Get offset to ID */
432             for (j = 0; j < IdOffset; j++)
433             {
434                 /* Advance in the Entries */
435                 MessageEntry += ((PMESSAGE_RESOURCE_ENTRY)MessageEntry)->
436                                 Length;
437             }
438 
439             /* Get the final Code */
440             BugCode = (PCHAR)((PMESSAGE_RESOURCE_ENTRY)MessageEntry)->Text;
441             Length = (USHORT)strlen(BugCode);
442 
443             /* Handle trailing newlines */
444             while ((Length > 0) && ((BugCode[Length] == '\n') ||
445                                     (BugCode[Length] == '\r') ||
446                                     (BugCode[Length] == ANSI_NULL)))
447             {
448                 /* Check if we have a string to return */
449                 if (!OutputString) BugCode[Length] = ANSI_NULL;
450                 Length--;
451             }
452 
453             /* Check if caller wants an output string */
454             if (OutputString)
455             {
456                 /* Return it in the OutputString */
457                 OutputString->Buffer = BugCode;
458                 OutputString->Length = Length + 1;
459                 OutputString->MaximumLength = Length + 1;
460             }
461             else
462             {
463                 /* Direct Output to Screen */
464                 InbvDisplayString(BugCode);
465                 InbvDisplayString("\r");
466             }
467 
468             /* We're done */
469             Result = TRUE;
470             break;
471         }
472     }
473 
474     /* Return the result */
475     return Result;
476 }
477 
478 VOID
479 NTAPI
480 KiDoBugCheckCallbacks(VOID)
481 {
482     PKBUGCHECK_CALLBACK_RECORD CurrentRecord;
483     PLIST_ENTRY ListHead, NextEntry, LastEntry;
484     ULONG_PTR Checksum;
485 
486     /* First make sure that the list is Initialized... it might not be */
487     ListHead = &KeBugcheckCallbackListHead;
488     if ((ListHead->Flink) && (ListHead->Blink))
489     {
490         /* Loop the list */
491         LastEntry = ListHead;
492         NextEntry = ListHead->Flink;
493         while (NextEntry != ListHead)
494         {
495             /* Get the reord */
496             CurrentRecord = CONTAINING_RECORD(NextEntry,
497                                               KBUGCHECK_CALLBACK_RECORD,
498                                               Entry);
499 
500             /* Validate it */
501             if (CurrentRecord->Entry.Blink != LastEntry) return;
502             Checksum = (ULONG_PTR)CurrentRecord->CallbackRoutine;
503             Checksum += (ULONG_PTR)CurrentRecord->Buffer;
504             Checksum += (ULONG_PTR)CurrentRecord->Length;
505             Checksum += (ULONG_PTR)CurrentRecord->Component;
506 
507             /* Make sure it's inserted and valitdated */
508             if ((CurrentRecord->State == BufferInserted) &&
509                 (CurrentRecord->Checksum == Checksum))
510             {
511                 /* Call the routine */
512                 CurrentRecord->State = BufferStarted;
513                 (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer,
514                                                  CurrentRecord->Length);
515                 CurrentRecord->State = BufferFinished;
516             }
517 
518             /* Go to the next entry */
519             LastEntry = NextEntry;
520             NextEntry = NextEntry->Flink;
521         }
522     }
523 }
524 
525 VOID
526 NTAPI
527 KiBugCheckDebugBreak(IN ULONG StatusCode)
528 {
529     /*
530      * Wrap this in SEH so we don't crash if
531      * there is no debugger or if it disconnected
532      */
533 DoBreak:
534     _SEH2_TRY
535     {
536         /* Breakpoint */
537         DbgBreakPointWithStatus(StatusCode);
538     }
539     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
540     {
541         /* No debugger, halt the CPU */
542         HalHaltSystem();
543     }
544     _SEH2_END;
545 
546     /* Break again if this wasn't first try */
547     if (StatusCode != DBG_STATUS_BUGCHECK_FIRST) goto DoBreak;
548 }
549 
550 PCHAR
551 NTAPI
552 KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING Unicode,
553                         OUT PCHAR Ansi,
554                         IN ULONG Length)
555 {
556     PCHAR p;
557     PWCHAR pw;
558     ULONG i;
559 
560     /* Set length and normalize it */
561     i = Unicode->Length / sizeof(WCHAR);
562     i = min(i, Length - 1);
563 
564     /* Set source and destination, and copy */
565     pw = Unicode->Buffer;
566     p = Ansi;
567     while (i--) *p++ = (CHAR)*pw++;
568 
569     /* Null terminate and return */
570     *p = ANSI_NULL;
571     return Ansi;
572 }
573 
574 VOID
575 NTAPI
576 KiDumpParameterImages(IN PCHAR Message,
577                       IN PULONG_PTR Parameters,
578                       IN ULONG ParameterCount,
579                       IN PKE_BUGCHECK_UNICODE_TO_ANSI ConversionRoutine)
580 {
581     ULONG i;
582     BOOLEAN InSystem;
583     PLDR_DATA_TABLE_ENTRY LdrEntry;
584     PVOID ImageBase;
585     PUNICODE_STRING DriverName;
586     CHAR AnsiName[32];
587     PIMAGE_NT_HEADERS NtHeader;
588     ULONG TimeStamp;
589     BOOLEAN FirstRun = TRUE;
590 
591     /* Loop parameters */
592     for (i = 0; i < ParameterCount; i++)
593     {
594         /* Get the base for this parameter */
595         ImageBase = KiPcToFileHeader((PVOID)Parameters[i],
596                                      &LdrEntry,
597                                      FALSE,
598                                      &InSystem);
599         if (!ImageBase)
600         {
601             /* FIXME: Add code to check for unloaded drivers */
602             DPRINT1("Potentially unloaded driver!\n");
603             continue;
604         }
605         else
606         {
607             /* Get the NT Headers and Timestamp */
608             NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
609             TimeStamp = NtHeader->FileHeader.TimeDateStamp;
610 
611             /* Convert the driver name */
612             DriverName = &LdrEntry->BaseDllName;
613             ConversionRoutine(&LdrEntry->BaseDllName,
614                               AnsiName,
615                               sizeof(AnsiName));
616         }
617 
618         /* Format driver name */
619         sprintf(Message,
620                 "%s**  %12s - Address %p base at %p, DateStamp %08lx\r\n",
621                 FirstRun ? "\r\n*":"*",
622                 AnsiName,
623                 (PVOID)Parameters[i],
624                 ImageBase,
625                 TimeStamp);
626 
627         /* Check if we only had one parameter */
628         if (ParameterCount <= 1)
629         {
630             /* Then just save the name */
631             KiBugCheckDriver = DriverName;
632         }
633         else
634         {
635             /* Otherwise, display the message */
636             InbvDisplayString(Message);
637         }
638 
639         /* Loop again */
640         FirstRun = FALSE;
641     }
642 }
643 
644 VOID
645 NTAPI
646 KiDisplayBlueScreen(IN ULONG MessageId,
647                     IN BOOLEAN IsHardError,
648                     IN PCHAR HardErrCaption OPTIONAL,
649                     IN PCHAR HardErrMessage OPTIONAL,
650                     IN PCHAR Message)
651 {
652     CHAR AnsiName[75];
653 
654     /* Check if bootvid is installed */
655     if (InbvIsBootDriverInstalled())
656     {
657         /* Acquire ownership and reset the display */
658         InbvAcquireDisplayOwnership();
659         InbvResetDisplay();
660 
661         /* Display blue screen */
662         InbvSolidColorFill(0, 0, 639, 479, 4);
663         InbvSetTextColor(15);
664         InbvInstallDisplayStringFilter(NULL);
665         InbvEnableDisplayString(TRUE);
666         InbvSetScrollRegion(0, 0, 639, 479);
667     }
668 
669     /* Check if this is a hard error */
670     if (IsHardError)
671     {
672         /* Display caption and message */
673         if (HardErrCaption) InbvDisplayString(HardErrCaption);
674         if (HardErrMessage) InbvDisplayString(HardErrMessage);
675     }
676 
677     /* Begin the display */
678     InbvDisplayString("\r\n");
679 
680     /* Print out initial message */
681     KeGetBugMessageText(BUGCHECK_MESSAGE_INTRO, NULL);
682     InbvDisplayString("\r\n\r\n");
683 
684     /* Check if we have a driver */
685     if (KiBugCheckDriver)
686     {
687         /* Print out into to driver name */
688         KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL);
689 
690         /* Convert and print out driver name */
691         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
692         InbvDisplayString(" ");
693         InbvDisplayString(AnsiName);
694         InbvDisplayString("\r\n\r\n");
695     }
696 
697     /* Check if this is the generic message */
698     if (MessageId == BUGCODE_PSS_MESSAGE)
699     {
700         /* It is, so get the bug code string as well */
701         KeGetBugMessageText((ULONG)KiBugCheckData[0], NULL);
702         InbvDisplayString("\r\n\r\n");
703     }
704 
705     /* Print second introduction message */
706     KeGetBugMessageText(PSS_MESSAGE_INTRO, NULL);
707     InbvDisplayString("\r\n\r\n");
708 
709     /* Get the bug code string */
710     KeGetBugMessageText(MessageId, NULL);
711     InbvDisplayString("\r\n\r\n");
712 
713     /* Print message for technical information */
714     KeGetBugMessageText(BUGCHECK_TECH_INFO, NULL);
715 
716     /* Show the technical Data */
717     sprintf(AnsiName,
718             "\r\n\r\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\r\n\r\n",
719             (ULONG)KiBugCheckData[0],
720             (PVOID)KiBugCheckData[1],
721             (PVOID)KiBugCheckData[2],
722             (PVOID)KiBugCheckData[3],
723             (PVOID)KiBugCheckData[4]);
724     InbvDisplayString(AnsiName);
725 
726     /* Check if we have a driver*/
727     if (KiBugCheckDriver)
728     {
729         /* Display technical driver data */
730         InbvDisplayString(Message);
731     }
732     else
733     {
734         /* Dump parameter information */
735         KiDumpParameterImages(Message,
736                               (PVOID)&KiBugCheckData[1],
737                               4,
738                               KeBugCheckUnicodeToAnsi);
739     }
740 }
741 
742 VOID
743 NTAPI
744 KeBugCheckWithTf(IN ULONG BugCheckCode,
745                  IN ULONG_PTR BugCheckParameter1,
746                  IN ULONG_PTR BugCheckParameter2,
747                  IN ULONG_PTR BugCheckParameter3,
748                  IN ULONG_PTR BugCheckParameter4,
749                  IN PKTRAP_FRAME TrapFrame)
750 {
751     PKPRCB Prcb = KeGetCurrentPrcb();
752     CONTEXT Context;
753     ULONG MessageId;
754     CHAR AnsiName[128];
755     BOOLEAN IsSystem, IsHardError = FALSE, Reboot = FALSE;
756     PCHAR HardErrCaption = NULL, HardErrMessage = NULL;
757     PVOID Pc = NULL, Memory;
758     PVOID DriverBase;
759     PLDR_DATA_TABLE_ENTRY LdrEntry;
760     PULONG_PTR HardErrorParameters;
761     KIRQL OldIrql;
762 #ifdef CONFIG_SMP
763     LONG i = 0;
764 #endif
765 
766     /* Set active bugcheck */
767     KeBugCheckActive = TRUE;
768     KiBugCheckDriver = NULL;
769 
770     /* Check if this is power failure simulation */
771     if (BugCheckCode == POWER_FAILURE_SIMULATE)
772     {
773         /* Call the Callbacks and reboot */
774         KiDoBugCheckCallbacks();
775         HalReturnToFirmware(HalRebootRoutine);
776     }
777 
778     /* Save the IRQL and set hardware trigger */
779     Prcb->DebuggerSavedIRQL = KeGetCurrentIrql();
780     InterlockedIncrement((PLONG)&KiHardwareTrigger);
781 
782     /* Capture the CPU Context */
783     RtlCaptureContext(&Prcb->ProcessorState.ContextFrame);
784     KiSaveProcessorControlState(&Prcb->ProcessorState);
785     Context = Prcb->ProcessorState.ContextFrame;
786 
787     /* FIXME: Call the Watchdog if it's registered */
788 
789     /* Check which bugcode this is */
790     switch (BugCheckCode)
791     {
792         /* These bug checks already have detailed messages, keep them */
793         case UNEXPECTED_KERNEL_MODE_TRAP:
794         case DRIVER_CORRUPTED_EXPOOL:
795         case ACPI_BIOS_ERROR:
796         case ACPI_BIOS_FATAL_ERROR:
797         case THREAD_STUCK_IN_DEVICE_DRIVER:
798         case DATA_BUS_ERROR:
799         case FAT_FILE_SYSTEM:
800         case NO_MORE_SYSTEM_PTES:
801         case INACCESSIBLE_BOOT_DEVICE:
802 
803             /* Keep the same code */
804             MessageId = BugCheckCode;
805             break;
806 
807         /* Check if this is a kernel-mode exception */
808         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
809         case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:
810         case KMODE_EXCEPTION_NOT_HANDLED:
811 
812             /* Use the generic text message */
813             MessageId = KMODE_EXCEPTION_NOT_HANDLED;
814             break;
815 
816         /* File-system errors */
817         case NTFS_FILE_SYSTEM:
818 
819             /* Use the generic message for FAT */
820             MessageId = FAT_FILE_SYSTEM;
821             break;
822 
823         /* Check if this is a coruption of the Mm's Pool */
824         case DRIVER_CORRUPTED_MMPOOL:
825 
826             /* Use generic corruption message */
827             MessageId = DRIVER_CORRUPTED_EXPOOL;
828             break;
829 
830         /* Check if this is a signature check failure */
831         case STATUS_SYSTEM_IMAGE_BAD_SIGNATURE:
832 
833             /* Use the generic corruption message */
834             MessageId = BUGCODE_PSS_MESSAGE_SIGNATURE;
835             break;
836 
837         /* All other codes */
838         default:
839 
840             /* Use the default bugcheck message */
841             MessageId = BUGCODE_PSS_MESSAGE;
842             break;
843     }
844 
845     /* Save bugcheck data */
846     KiBugCheckData[0] = BugCheckCode;
847     KiBugCheckData[1] = BugCheckParameter1;
848     KiBugCheckData[2] = BugCheckParameter2;
849     KiBugCheckData[3] = BugCheckParameter3;
850     KiBugCheckData[4] = BugCheckParameter4;
851 
852     /* Now check what bugcheck this is */
853     switch (BugCheckCode)
854     {
855         /* Invalid access to R/O memory or Unhandled KM Exception */
856         case KERNEL_MODE_EXCEPTION_NOT_HANDLED:
857         case ATTEMPTED_WRITE_TO_READONLY_MEMORY:
858         case ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY:
859 
860             /* Check if we have a trap frame */
861             if (!TrapFrame)
862             {
863                 /* Use parameter 3 as a trap frame, if it exists */
864                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
865             }
866 
867             /* Check if we got one now and if we need to get the Program Counter */
868             if ((TrapFrame) &&
869                 (BugCheckCode != KERNEL_MODE_EXCEPTION_NOT_HANDLED))
870             {
871                 /* Get the Program Counter */
872                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
873             }
874             break;
875 
876         /* Wrong IRQL */
877         case IRQL_NOT_LESS_OR_EQUAL:
878 
879             /*
880              * The NT kernel has 3 special sections:
881              * MISYSPTE, POOLMI and POOLCODE. The bug check code can
882              * determine in which of these sections this bugcode happened
883              * and provide a more detailed analysis. For now, we don't.
884              */
885 
886             /* Program Counter is in parameter 4 */
887             Pc = (PVOID)BugCheckParameter4;
888 
889             /* Get the driver base */
890             DriverBase = KiPcToFileHeader(Pc,
891                                           &LdrEntry,
892                                           FALSE,
893                                           &IsSystem);
894             if (IsSystem)
895             {
896                 /*
897                  * The error happened inside the kernel or HAL.
898                  * Get the memory address that was being referenced.
899                  */
900                 Memory = (PVOID)BugCheckParameter1;
901 
902                 /* Find to which driver it belongs */
903                 DriverBase = KiPcToFileHeader(Memory,
904                                               &LdrEntry,
905                                               TRUE,
906                                               &IsSystem);
907                 if (DriverBase)
908                 {
909                     /* Get the driver name and update the bug code */
910                     KiBugCheckDriver = &LdrEntry->BaseDllName;
911                     KiBugCheckData[0] = DRIVER_PORTION_MUST_BE_NONPAGED;
912                 }
913                 else
914                 {
915                     /* Find the driver that unloaded at this address */
916                     KiBugCheckDriver = NULL; // FIXME: ROS can't locate
917 
918                     /* Check if the cause was an unloaded driver */
919                     if (KiBugCheckDriver)
920                     {
921                         /* Update bug check code */
922                         KiBugCheckData[0] =
923                             SYSTEM_SCAN_AT_RAISED_IRQL_CAUGHT_IMPROPER_DRIVER_UNLOAD;
924                     }
925                 }
926             }
927             else
928             {
929                 /* Update the bug check code */
930                 KiBugCheckData[0] = DRIVER_IRQL_NOT_LESS_OR_EQUAL;
931             }
932 
933             /* Clear Pc so we don't look it up later */
934             Pc = NULL;
935             break;
936 
937         /* Hard error */
938         case FATAL_UNHANDLED_HARD_ERROR:
939 
940             /* Copy bug check data from hard error */
941             HardErrorParameters = (PULONG_PTR)BugCheckParameter2;
942             KiBugCheckData[0] = BugCheckParameter1;
943             KiBugCheckData[1] = HardErrorParameters[0];
944             KiBugCheckData[2] = HardErrorParameters[1];
945             KiBugCheckData[3] = HardErrorParameters[2];
946             KiBugCheckData[4] = HardErrorParameters[3];
947 
948             /* Remember that this is hard error and set the caption/message */
949             IsHardError = TRUE;
950             HardErrCaption = (PCHAR)BugCheckParameter3;
951             HardErrMessage = (PCHAR)BugCheckParameter4;
952             break;
953 
954         /* Page fault */
955         case PAGE_FAULT_IN_NONPAGED_AREA:
956 
957             /* Assume no driver */
958             DriverBase = NULL;
959 
960             /* Check if we have a trap frame */
961             if (!TrapFrame)
962             {
963                 /* We don't, use parameter 3 if possible */
964                 if (BugCheckParameter3) TrapFrame = (PVOID)BugCheckParameter3;
965             }
966 
967             /* Check if we have a frame now */
968             if (TrapFrame)
969             {
970                 /* Get the Program Counter */
971                 Pc = (PVOID)KeGetTrapFramePc(TrapFrame);
972                 KiBugCheckData[3] = (ULONG_PTR)Pc;
973 
974                 /* Find out if was in the kernel or drivers */
975                 DriverBase = KiPcToFileHeader(Pc,
976                                               &LdrEntry,
977                                               FALSE,
978                                               &IsSystem);
979             }
980             else
981             {
982                 /* Can't blame a driver, assume system */
983                 IsSystem = TRUE;
984             }
985 
986             /* FIXME: Check for session pool in addition to special pool */
987 
988             /* Special pool has its own bug check codes */
989             if (MmIsSpecialPoolAddress((PVOID)BugCheckParameter1))
990             {
991                 if (MmIsSpecialPoolAddressFree((PVOID)BugCheckParameter1))
992                 {
993                     KiBugCheckData[0] = IsSystem
994                         ? PAGE_FAULT_IN_FREED_SPECIAL_POOL
995                         : DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL;
996                 }
997                 else
998                 {
999                     KiBugCheckData[0] = IsSystem
1000                         ? PAGE_FAULT_BEYOND_END_OF_ALLOCATION
1001                         : DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION;
1002                 }
1003             }
1004             else if (!DriverBase)
1005             {
1006                 /* Find the driver that unloaded at this address */
1007                 KiBugCheckDriver = NULL; // FIXME: ROS can't locate
1008 
1009                 /* Check if the cause was an unloaded driver */
1010                 if (KiBugCheckDriver)
1011                 {
1012                     KiBugCheckData[0] =
1013                         DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS;
1014                 }
1015             }
1016             break;
1017 
1018         /* Check if the driver forgot to unlock pages */
1019         case DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS:
1020 
1021             /* Program Counter is in parameter 1 */
1022             Pc = (PVOID)BugCheckParameter1;
1023             break;
1024 
1025         /* Check if the driver consumed too many PTEs */
1026         case DRIVER_USED_EXCESSIVE_PTES:
1027 
1028             /* Loader entry is in parameter 1 */
1029             LdrEntry = (PVOID)BugCheckParameter1;
1030             KiBugCheckDriver = &LdrEntry->BaseDllName;
1031             break;
1032 
1033         /* Check if the driver has a stuck thread */
1034         case THREAD_STUCK_IN_DEVICE_DRIVER:
1035 
1036             /* The name is in Parameter 3 */
1037             KiBugCheckDriver = (PVOID)BugCheckParameter3;
1038             break;
1039 
1040         /* Anything else */
1041         default:
1042             break;
1043     }
1044 
1045     /* Do we have a driver name? */
1046     if (KiBugCheckDriver)
1047     {
1048         /* Convert it to ANSI */
1049         KeBugCheckUnicodeToAnsi(KiBugCheckDriver, AnsiName, sizeof(AnsiName));
1050     }
1051     else
1052     {
1053         /* Do we have a Program Counter? */
1054         if (Pc)
1055         {
1056             /* Dump image name */
1057             KiDumpParameterImages(AnsiName,
1058                                   (PULONG_PTR)&Pc,
1059                                   1,
1060                                   KeBugCheckUnicodeToAnsi);
1061         }
1062     }
1063 
1064     /* Check if we need to save the context for KD */
1065 #ifdef _WINKD_
1066     if (!KdPitchDebugger) KdDebuggerDataBlock.SavedContext = (ULONG_PTR)&Context;
1067 #endif
1068 
1069     /* Check if a debugger is connected */
1070     if ((BugCheckCode != MANUALLY_INITIATED_CRASH) && (KdDebuggerEnabled))
1071     {
1072         /* Crash on the debugger console */
1073         DbgPrint("\n*** Fatal System Error: 0x%08lx\n"
1074                  "                       (0x%p,0x%p,0x%p,0x%p)\n\n",
1075                  KiBugCheckData[0],
1076                  KiBugCheckData[1],
1077                  KiBugCheckData[2],
1078                  KiBugCheckData[3],
1079                  KiBugCheckData[4]);
1080 
1081         /* Check if the debugger isn't currently connected */
1082         if (!KdDebuggerNotPresent)
1083         {
1084             /* Check if we have a driver to blame */
1085             if (KiBugCheckDriver)
1086             {
1087                 /* Dump it */
1088                 DbgPrint("Driver at fault: %s.\n", AnsiName);
1089             }
1090 
1091             /* Check if this was a hard error */
1092             if (IsHardError)
1093             {
1094                 /* Print caption and message */
1095                 if (HardErrCaption) DbgPrint(HardErrCaption);
1096                 if (HardErrMessage) DbgPrint(HardErrMessage);
1097             }
1098 
1099             /* Break in the debugger */
1100             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_FIRST);
1101         }
1102         else
1103         {
1104             /*
1105              * ROS HACK.
1106              * Ok, so debugging is enabled, but KDBG isn't there.
1107              * We'll manually dump the stack for the user.
1108              */
1109             KeRosDumpStackFrames(NULL, 0);
1110 
1111             /* ROS HACK 2: Generate something useful for Bugzilla */
1112             KeRosDumpTriageForBugZillaReport();
1113         }
1114     }
1115 
1116     /* Raise IRQL to HIGH_LEVEL */
1117     _disable();
1118     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1119 
1120     /* Avoid recursion */
1121     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1122     {
1123 #ifdef CONFIG_SMP
1124         /* Set CPU that is bug checking now */
1125         KeBugCheckOwner = Prcb->Number;
1126 
1127         /* Freeze the other CPUs */
1128         for (i = 0; i < KeNumberProcessors; i++)
1129         {
1130             if (i != (LONG)KeGetCurrentProcessorNumber())
1131             {
1132                 /* Send the IPI and give them one second to catch up */
1133                 KiIpiSend(1 << i, IPI_FREEZE);
1134                 KeStallExecutionProcessor(1000000);
1135             }
1136         }
1137 #endif
1138 
1139         /* Display the BSOD */
1140         KiDisplayBlueScreen(MessageId,
1141                             IsHardError,
1142                             HardErrCaption,
1143                             HardErrMessage,
1144                             AnsiName);
1145 
1146         /* Check if the debugger is disabled but we can enable it */
1147         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1148         {
1149             /* Enable it */
1150 #ifdef _WINKD_
1151             KdEnableDebuggerWithLock(FALSE);
1152 #endif
1153         }
1154         else
1155         {
1156             /* Otherwise, print the last line */
1157             InbvDisplayString("\r\n");
1158         }
1159 
1160         /* Save the context */
1161         Prcb->ProcessorState.ContextFrame = Context;
1162 
1163         /* FIXME: Support Triage Dump */
1164 
1165         /* FIXME: Write the crash dump */
1166     }
1167     else
1168     {
1169         /* Increase recursion count */
1170         KeBugCheckOwnerRecursionCount++;
1171         if (KeBugCheckOwnerRecursionCount == 2)
1172         {
1173             /* Break in the debugger */
1174             KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1175         }
1176         else if (KeBugCheckOwnerRecursionCount > 2)
1177         {
1178             /* Halt execution */
1179             while (TRUE);
1180         }
1181     }
1182 
1183     /* Call the Callbacks */
1184     KiDoBugCheckCallbacks();
1185 
1186     /* FIXME: Call Watchdog if enabled */
1187 
1188     /* Check if we have to reboot */
1189     if (Reboot)
1190     {
1191         /* Unload symbols */
1192         DbgUnLoadImageSymbols(NULL, (PVOID)MAXULONG_PTR, 0);
1193         HalReturnToFirmware(HalRebootRoutine);
1194     }
1195 
1196     /* Attempt to break in the debugger (otherwise halt CPU) */
1197     KiBugCheckDebugBreak(DBG_STATUS_BUGCHECK_SECOND);
1198 
1199     /* Shouldn't get here */
1200     ASSERT(FALSE);
1201     while (TRUE);
1202 }
1203 
1204 BOOLEAN
1205 NTAPI
1206 KiHandleNmi(VOID)
1207 {
1208     BOOLEAN Handled = FALSE;
1209     PKNMI_HANDLER_CALLBACK NmiData;
1210 
1211     /* Parse the list of callbacks */
1212     NmiData = KiNmiCallbackListHead;
1213     while (NmiData)
1214     {
1215         /* Save if this callback has handled it -- all it takes is one */
1216         Handled |= NmiData->Callback(NmiData->Context, Handled);
1217         NmiData = NmiData->Next;
1218     }
1219 
1220     /* Has anyone handled this? */
1221     return Handled;
1222 }
1223 
1224 /* PUBLIC FUNCTIONS **********************************************************/
1225 
1226 /*
1227  * @unimplemented
1228  */
1229 NTSTATUS
1230 NTAPI
1231 KeInitializeCrashDumpHeader(IN ULONG Type,
1232                             IN ULONG Flags,
1233                             OUT PVOID Buffer,
1234                             IN ULONG BufferSize,
1235                             OUT ULONG BufferNeeded OPTIONAL)
1236 {
1237     UNIMPLEMENTED;
1238     return STATUS_UNSUCCESSFUL;
1239 }
1240 
1241 /*
1242  * @implemented
1243  */
1244 BOOLEAN
1245 NTAPI
1246 KeDeregisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord)
1247 {
1248     KIRQL OldIrql;
1249     BOOLEAN Status = FALSE;
1250 
1251     /* Raise IRQL to High */
1252     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1253 
1254     /* Check the Current State */
1255     if (CallbackRecord->State == BufferInserted)
1256     {
1257         /* Reset state and remove from list */
1258         CallbackRecord->State = BufferEmpty;
1259         RemoveEntryList(&CallbackRecord->Entry);
1260         Status = TRUE;
1261     }
1262 
1263     /* Lower IRQL and return */
1264     KeLowerIrql(OldIrql);
1265     return Status;
1266 }
1267 
1268 /*
1269  * @implemented
1270  */
1271 BOOLEAN
1272 NTAPI
1273 KeDeregisterBugCheckReasonCallback(
1274     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)
1275 {
1276     KIRQL OldIrql;
1277     BOOLEAN Status = FALSE;
1278 
1279     /* Raise IRQL to High */
1280     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1281 
1282     /* Check the Current State */
1283     if (CallbackRecord->State == BufferInserted)
1284     {
1285         /* Reset state and remove from list */
1286         CallbackRecord->State = BufferEmpty;
1287         RemoveEntryList(&CallbackRecord->Entry);
1288         Status = TRUE;
1289     }
1290 
1291     /* Lower IRQL and return */
1292     KeLowerIrql(OldIrql);
1293     return Status;
1294 }
1295 
1296 /*
1297  * @implemented
1298  */
1299 BOOLEAN
1300 NTAPI
1301 KeRegisterBugCheckCallback(IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
1302                            IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
1303                            IN PVOID Buffer,
1304                            IN ULONG Length,
1305                            IN PUCHAR Component)
1306 {
1307     KIRQL OldIrql;
1308     BOOLEAN Status = FALSE;
1309 
1310     /* Raise IRQL to High */
1311     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1312 
1313     /* Check the Current State first so we don't double-register */
1314     if (CallbackRecord->State == BufferEmpty)
1315     {
1316         /* Set the Callback Settings and insert into the list */
1317         CallbackRecord->Length = Length;
1318         CallbackRecord->Buffer = Buffer;
1319         CallbackRecord->Component = Component;
1320         CallbackRecord->CallbackRoutine = CallbackRoutine;
1321         CallbackRecord->State = BufferInserted;
1322         InsertTailList(&KeBugcheckCallbackListHead, &CallbackRecord->Entry);
1323         Status = TRUE;
1324     }
1325 
1326     /* Lower IRQL and return */
1327     KeLowerIrql(OldIrql);
1328     return Status;
1329 }
1330 
1331 /*
1332  * @implemented
1333  */
1334 BOOLEAN
1335 NTAPI
1336 KeRegisterBugCheckReasonCallback(
1337     IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
1338     IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
1339     IN KBUGCHECK_CALLBACK_REASON Reason,
1340     IN PUCHAR Component)
1341 {
1342     KIRQL OldIrql;
1343     BOOLEAN Status = FALSE;
1344 
1345     /* Raise IRQL to High */
1346     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
1347 
1348     /* Check the Current State first so we don't double-register */
1349     if (CallbackRecord->State == BufferEmpty)
1350     {
1351         /* Set the Callback Settings and insert into the list */
1352         CallbackRecord->Component = Component;
1353         CallbackRecord->CallbackRoutine = CallbackRoutine;
1354         CallbackRecord->State = BufferInserted;
1355         CallbackRecord->Reason = Reason;
1356         InsertTailList(&KeBugcheckReasonCallbackListHead,
1357                        &CallbackRecord->Entry);
1358         Status = TRUE;
1359     }
1360 
1361     /* Lower IRQL and return */
1362     KeLowerIrql(OldIrql);
1363     return Status;
1364 }
1365 
1366 /*
1367  * @implemented
1368  */
1369 PVOID
1370 NTAPI
1371 KeRegisterNmiCallback(IN PNMI_CALLBACK CallbackRoutine,
1372                       IN PVOID Context)
1373 {
1374     KIRQL OldIrql;
1375     PKNMI_HANDLER_CALLBACK NmiData, Next;
1376     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1377 
1378     /* Allocate NMI callback data */
1379     NmiData = ExAllocatePoolWithTag(NonPagedPool, sizeof(*NmiData), TAG_KNMI);
1380     if (!NmiData) return NULL;
1381 
1382     /* Fill in the information */
1383     NmiData->Callback = CallbackRoutine;
1384     NmiData->Context = Context;
1385     NmiData->Handle = NmiData;
1386 
1387     /* Insert it into NMI callback list */
1388     KiAcquireNmiListLock(&OldIrql);
1389     NmiData->Next = KiNmiCallbackListHead;
1390     Next = InterlockedCompareExchangePointer((PVOID*)&KiNmiCallbackListHead,
1391                                              NmiData,
1392                                              NmiData->Next);
1393     ASSERT(Next == NmiData->Next);
1394     KiReleaseNmiListLock(OldIrql);
1395 
1396     /* Return the opaque "handle" */
1397     return NmiData->Handle;
1398 }
1399 
1400 /*
1401  * @implemented
1402  */
1403 NTSTATUS
1404 NTAPI
1405 KeDeregisterNmiCallback(IN PVOID Handle)
1406 {
1407     KIRQL OldIrql;
1408     PKNMI_HANDLER_CALLBACK NmiData;
1409     PKNMI_HANDLER_CALLBACK* Previous;
1410     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1411 
1412     /* Find in the list the NMI callback corresponding to the handle */
1413     KiAcquireNmiListLock(&OldIrql);
1414     Previous = &KiNmiCallbackListHead;
1415     NmiData = *Previous;
1416     while (NmiData)
1417     {
1418         if (NmiData->Handle == Handle)
1419         {
1420             /* The handle is the pointer to the callback itself */
1421             ASSERT(Handle == NmiData);
1422 
1423             /* Found it, remove from the list */
1424             *Previous = NmiData->Next;
1425             break;
1426         }
1427 
1428         /* Not found; try again */
1429         Previous = &NmiData->Next;
1430         NmiData = *Previous;
1431     }
1432     KiReleaseNmiListLock(OldIrql);
1433 
1434     /* If we have found the entry, free it */
1435     if (NmiData)
1436     {
1437         ExFreePoolWithTag(NmiData, TAG_KNMI);
1438         return STATUS_SUCCESS;
1439     }
1440 
1441     return STATUS_INVALID_HANDLE;
1442 }
1443 
1444 /*
1445  * @implemented
1446  */
1447 VOID
1448 NTAPI
1449 KeBugCheckEx(IN ULONG BugCheckCode,
1450              IN ULONG_PTR BugCheckParameter1,
1451              IN ULONG_PTR BugCheckParameter2,
1452              IN ULONG_PTR BugCheckParameter3,
1453              IN ULONG_PTR BugCheckParameter4)
1454 {
1455     /* Call the internal API */
1456     KeBugCheckWithTf(BugCheckCode,
1457                      BugCheckParameter1,
1458                      BugCheckParameter2,
1459                      BugCheckParameter3,
1460                      BugCheckParameter4,
1461                      NULL);
1462 }
1463 
1464 /*
1465  * @implemented
1466  */
1467 VOID
1468 NTAPI
1469 KeBugCheck(ULONG BugCheckCode)
1470 {
1471     /* Call the internal API */
1472     KeBugCheckWithTf(BugCheckCode, 0, 0, 0, 0, NULL);
1473 }
1474 
1475 /*
1476  * @implemented
1477  */
1478 VOID
1479 NTAPI
1480 KeEnterKernelDebugger(VOID)
1481 {
1482     /* Disable interrupts */
1483     KiHardwareTrigger = 1;
1484     _disable();
1485 
1486     /* Check the bugcheck count */
1487     if (!InterlockedDecrement((PLONG)&KeBugCheckCount))
1488     {
1489         /* There was only one, is the debugger disabled? */
1490         if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
1491         {
1492             /* Enable the debugger */
1493             KdInitSystem(0, NULL);
1494         }
1495     }
1496 
1497     /* Break in the debugger */
1498     KiBugCheckDebugBreak(DBG_STATUS_FATAL);
1499 }
1500 
1501 /* EOF */
1502