xref: /reactos/hal/halx86/generic/usage.c (revision 94a413ae)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         HAL Resource Report Routines
5  * PROGRAMMERS:     Stefan Ginsberg (stefan.ginsberg@reactos.org)
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <hal.h>
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 INIT_FUNCTION
16 VOID
17 NTAPI
18 HalpGetResourceSortValue(
19     IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
20     OUT PULONG Scale,
21     OUT PLARGE_INTEGER Value
22 );
23 
24 INIT_FUNCTION
25 VOID
26 NTAPI
27 HalpBuildPartialFromIdt(
28     IN ULONG Entry,
29     IN PCM_PARTIAL_RESOURCE_DESCRIPTOR RawDescriptor,
30     IN PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedDescriptor
31 );
32 
33 INIT_FUNCTION
34 VOID
35 NTAPI
36 HalpBuildPartialFromAddress(
37     IN INTERFACE_TYPE Interface,
38     IN PADDRESS_USAGE CurrentAddress,
39     IN ULONG Element,
40     IN PCM_PARTIAL_RESOURCE_DESCRIPTOR RawDescriptor,
41     IN PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedDescriptor
42 );
43 
44 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
45 #pragma alloc_text(INIT, HalpBuildPartialFromAddress)
46 #pragma alloc_text(INIT, HalpBuildPartialFromIdt)
47 #pragma alloc_text(INIT, HalpEnableInterruptHandler)
48 #pragma alloc_text(INIT, HalpGetNMICrashFlag)
49 #pragma alloc_text(INIT, HalpGetResourceSortValue)
50 #pragma alloc_text(INIT, HalpRegisterVector)
51 #pragma alloc_text(INIT, HalpReportResourceUsage)
52 #endif
53 
54 /* GLOBALS ********************************************************************/
55 
56 BOOLEAN HalpGetInfoFromACPI;
57 BOOLEAN HalpNMIDumpFlag;
58 PUCHAR KdComPortInUse;
59 PADDRESS_USAGE HalpAddressUsageList;
60 IDTUsageFlags HalpIDTUsageFlags[MAXIMUM_IDTVECTOR+1];
61 IDTUsage HalpIDTUsage[MAXIMUM_IDTVECTOR+1];
62 
63 USHORT HalpComPortIrqMapping[5][2] =
64 {
65     {0x3F8, 4},
66     {0x2F8, 3},
67     {0x3E8, 4},
68     {0x2E8, 3},
69     {0, 0}
70 };
71 
72 ADDRESS_USAGE HalpComIoSpace =
73 {
74     NULL, CmResourceTypePort, IDT_INTERNAL,
75     {
76         {0x2F8,   0x8},     /* COM 1 */
77         {0,0},
78     }
79 };
80 
81 ADDRESS_USAGE HalpDefaultIoSpace =
82 {
83     NULL, CmResourceTypePort, IDT_INTERNAL,
84     {
85 #if defined(SARCH_PC98)
86         /* PIC 1 */
87         {0x00,  1},
88         {0x02,  1},
89         /* PIC 2 */
90         {0x08,  1},
91         {0x0A,  1},
92         /* DMA */
93         {0x01,  1},
94         {0x03,  1},
95         {0x05,  1},
96         {0x07,  1},
97         {0x09,  1},
98         {0x0B,  1},
99         {0x0D,  1},
100         {0x0F,  1},
101         {0x11,  1},
102         {0x13,  1},
103         {0x15,  1},
104         {0x17,  1},
105         {0x19,  1},
106         {0x1B,  1},
107         {0x1D,  1},
108         {0x1F,  1},
109         {0x21,  1},
110         {0x23,  1},
111         {0x25,  1},
112         {0x27,  1},
113         {0x29,  1},
114         {0x2B,  1},
115         {0x2D,  1},
116         {0xE05, 1},
117         {0xE07, 1},
118         {0xE09, 1},
119         {0xE0B, 1},
120         /* RTC */
121         {0x20,  1},
122         {0x22,  1},
123         {0x128, 1},
124         /* System Control */
125         {0x33,  1},
126         {0x37,  1},
127         /* PIT */
128         {0x71,  1},
129         {0x73,  1},
130         {0x75,  1},
131         {0x77,  1},
132         {0x3FD9,1},
133         {0x3FDB,1},
134         {0x3FDD,1},
135         {0x3FDF,1},
136         /* x87 Coprocessor */
137         {0xF8,  8},
138 #else
139         {0x00,  0x20}, /* DMA 1 */
140         {0xC0,  0x20}, /* DMA 2 */
141         {0x80,  0x10}, /* DMA EPAR */
142         {0x20,  0x2},  /* PIC 1 */
143         {0xA0,  0x2},  /* PIC 2 */
144         {0x40,  0x4},  /* PIT 1 */
145         {0x48,  0x4},  /* PIT 2 */
146         {0x92,  0x1},  /* System Control Port A */
147         {0x70,  0x2},  /* CMOS  */
148         {0xF0,  0x10}, /* x87 Coprocessor */
149 #endif
150         {0xCF8, 0x8},  /* PCI 0 */
151         {0,0},
152     }
153 };
154 
155 /* FUNCTIONS ******************************************************************/
156 
157 #ifndef _MINIHAL_
158 INIT_FUNCTION
159 VOID
160 NTAPI
161 HalpGetResourceSortValue(IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
162                          OUT PULONG Scale,
163                          OUT PLARGE_INTEGER Value)
164 {
165     /* Sorting depends on resource type */
166     switch (Descriptor->Type)
167     {
168         case CmResourceTypeInterrupt:
169 
170             /* Interrupt goes by level */
171             *Scale = 0;
172             *Value = RtlConvertUlongToLargeInteger(Descriptor->u.Interrupt.Level);
173             break;
174 
175         case CmResourceTypePort:
176 
177             /* Port goes by port address */
178             *Scale = 1;
179             *Value = Descriptor->u.Port.Start;
180             break;
181 
182         case CmResourceTypeMemory:
183 
184             /* Memory goes by base address */
185             *Scale = 2;
186             *Value = Descriptor->u.Memory.Start;
187             break;
188 
189         default:
190 
191             /* Anything else */
192             *Scale = 4;
193             *Value = RtlConvertUlongToLargeInteger(0);
194             break;
195     }
196 }
197 
198 INIT_FUNCTION
199 VOID
200 NTAPI
201 HalpBuildPartialFromIdt(IN ULONG Entry,
202                         IN PCM_PARTIAL_RESOURCE_DESCRIPTOR RawDescriptor,
203                         IN PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedDescriptor)
204 {
205     /* Exclusive interrupt entry */
206     RawDescriptor->Type = CmResourceTypeInterrupt;
207     RawDescriptor->ShareDisposition = CmResourceShareDriverExclusive;
208 
209     /* Check the interrupt type */
210     if (HalpIDTUsageFlags[Entry].Flags & IDT_LATCHED)
211     {
212         /* Latched */
213         RawDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
214     }
215     else
216     {
217         /* Level */
218         RawDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
219     }
220 
221     /* Get vector and level from IDT usage */
222     RawDescriptor->u.Interrupt.Vector = HalpIDTUsage[Entry].BusReleativeVector;
223     RawDescriptor->u.Interrupt.Level = HalpIDTUsage[Entry].BusReleativeVector;
224 
225     /* Affinity is all the CPUs */
226     RawDescriptor->u.Interrupt.Affinity = HalpActiveProcessors;
227 
228     /* The translated copy is identical */
229     RtlCopyMemory(TranslatedDescriptor, RawDescriptor, sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
230 
231     /* But the vector and IRQL must be set correctly */
232     TranslatedDescriptor->u.Interrupt.Vector = Entry;
233     TranslatedDescriptor->u.Interrupt.Level = HalpIDTUsage[Entry].Irql;
234 }
235 
236 INIT_FUNCTION
237 VOID
238 NTAPI
239 HalpBuildPartialFromAddress(IN INTERFACE_TYPE Interface,
240                             IN PADDRESS_USAGE CurrentAddress,
241                             IN ULONG Element,
242                             IN PCM_PARTIAL_RESOURCE_DESCRIPTOR RawDescriptor,
243                             IN PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedDescriptor)
244 {
245     ULONG AddressSpace;
246 
247     /* Set the type and make it exclusive */
248     RawDescriptor->Type = CurrentAddress->Type;
249     RawDescriptor->ShareDisposition = CmResourceShareDriverExclusive;
250 
251     /* Check what this is */
252     if (RawDescriptor->Type == CmResourceTypePort)
253     {
254         /* Write out port data */
255         AddressSpace = 1;
256         RawDescriptor->Flags = CM_RESOURCE_PORT_IO;
257         RawDescriptor->u.Port.Start.HighPart = 0;
258         RawDescriptor->u.Port.Start.LowPart = CurrentAddress->Element[Element].Start;
259         RawDescriptor->u.Port.Length = CurrentAddress->Element[Element].Length;
260 
261         /* Determine if 16-bit port addresses are allowed */
262         RawDescriptor->Flags |= HalpIs16BitPortDecodeSupported();
263     }
264     else
265     {
266         /* Write out memory data */
267         AddressSpace = 0;
268         RawDescriptor->Flags = (CurrentAddress->Flags & IDT_READ_ONLY) ?
269                                 CM_RESOURCE_MEMORY_READ_ONLY :
270                                 CM_RESOURCE_MEMORY_READ_WRITE;
271         RawDescriptor->u.Memory.Start.HighPart = 0;
272         RawDescriptor->u.Memory.Start.LowPart = CurrentAddress->Element[Element].Start;
273         RawDescriptor->u.Memory.Length = CurrentAddress->Element[Element].Length;
274     }
275 
276     /* Make an identical copy to begin with */
277     RtlCopyMemory(TranslatedDescriptor, RawDescriptor, sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
278 
279     /* Check what this is */
280     if (RawDescriptor->Type == CmResourceTypePort)
281     {
282         /* Translate the port */
283         HalTranslateBusAddress(Interface,
284                                0,
285                                RawDescriptor->u.Port.Start,
286                                &AddressSpace,
287                                &TranslatedDescriptor->u.Port.Start);
288 
289         /* If it turns out this is memory once translated, flag it */
290         if (AddressSpace == 0) TranslatedDescriptor->Flags = CM_RESOURCE_PORT_MEMORY;
291 
292     }
293     else
294     {
295         /* Translate the memory */
296         HalTranslateBusAddress(Interface,
297                                0,
298                                RawDescriptor->u.Memory.Start,
299                                &AddressSpace,
300                                &TranslatedDescriptor->u.Memory.Start);
301     }
302 }
303 
304 INIT_FUNCTION
305 VOID
306 NTAPI
307 HalpReportResourceUsage(IN PUNICODE_STRING HalName,
308                         IN INTERFACE_TYPE InterfaceType)
309 {
310     PCM_RESOURCE_LIST RawList, TranslatedList;
311     PCM_FULL_RESOURCE_DESCRIPTOR RawFull, TranslatedFull;
312     PCM_PARTIAL_RESOURCE_DESCRIPTOR CurrentRaw, CurrentTranslated, SortedRaw, SortedTranslated;
313     CM_PARTIAL_RESOURCE_DESCRIPTOR RawPartial, TranslatedPartial;
314     PCM_PARTIAL_RESOURCE_LIST RawPartialList = NULL, TranslatedPartialList = NULL;
315     INTERFACE_TYPE Interface;
316     ULONG i, j, k, ListSize, Count, Port, Element, CurrentScale, SortScale, ReportType, FlagMatch;
317     ADDRESS_USAGE *CurrentAddress;
318     LARGE_INTEGER CurrentSortValue, SortValue;
319     DbgPrint("%wZ Detected\n", HalName);
320 
321     /* Check if KD is using a COM port */
322     if (KdComPortInUse)
323     {
324         /* Enter it into the I/O space */
325         HalpComIoSpace.Element[0].Start = PtrToUlong(KdComPortInUse);
326         HalpComIoSpace.Next = HalpAddressUsageList;
327         HalpAddressUsageList = &HalpComIoSpace;
328 
329         /* Use the debug port table if we have one */
330         HalpGetInfoFromACPI = HalpGetDebugPortTable();
331 
332         /* Check if we're using ACPI */
333         if (!HalpGetInfoFromACPI)
334         {
335             /* No, so use our local table */
336             for (i = 0, Port = HalpComPortIrqMapping[i][0];
337                  Port;
338                  i++, Port = HalpComPortIrqMapping[i][0])
339             {
340                 /* Is this the port we want? */
341                 if (Port == (ULONG_PTR)KdComPortInUse)
342                 {
343                     /* Register it */
344                     HalpRegisterVector(IDT_DEVICE | IDT_LATCHED,
345                                        HalpComPortIrqMapping[i][1],
346                                        HalpComPortIrqMapping[i][1] +
347                                        PRIMARY_VECTOR_BASE,
348                                        HIGH_LEVEL);
349                 }
350             }
351         }
352     }
353 
354     /* On non-ACPI systems, we need to build an address map */
355     HalpBuildAddressMap();
356 
357     /* Allocate the master raw and translated lists */
358     RawList = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE * 2, TAG_HAL);
359     TranslatedList = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE * 2, TAG_HAL);
360     if (!(RawList) || !(TranslatedList))
361     {
362         /* Bugcheck the system */
363         KeBugCheckEx(HAL_MEMORY_ALLOCATION,
364                      4 * PAGE_SIZE,
365                      1,
366                      (ULONG_PTR)__FILE__,
367                      __LINE__);
368     }
369 
370     /* Zero out the lists */
371     RtlZeroMemory(RawList, PAGE_SIZE * 2);
372     RtlZeroMemory(TranslatedList, PAGE_SIZE * 2);
373 
374     /* Set the interface type to begin with */
375     RawList->List[0].InterfaceType = InterfaceTypeUndefined;
376 
377     /* Loop all IDT entries that are not IRQs */
378     for (i = 0; i < PRIMARY_VECTOR_BASE; i++)
379     {
380         /* Check if the IDT isn't owned */
381         if (!(HalpIDTUsageFlags[i].Flags & IDT_REGISTERED))
382         {
383             /* Then register it for internal usage */
384             HalpIDTUsageFlags[i].Flags = IDT_INTERNAL;
385             HalpIDTUsage[i].BusReleativeVector = (UCHAR)i;
386         }
387     }
388 
389     /* Our full raw descriptors start here */
390     RawFull = RawList->List;
391 
392     /* Keep track of the current partial raw and translated descriptors */
393     CurrentRaw = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)RawList->List;
394     CurrentTranslated = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)TranslatedList->List;
395 
396     /* Do two passes */
397     for (ReportType = 0; ReportType < 2; ReportType++)
398     {
399         /* Pass 0 is for device usage */
400         if (ReportType == 0)
401         {
402             FlagMatch = IDT_DEVICE & ~IDT_REGISTERED;
403             Interface = InterfaceType;
404         }
405         else
406         {
407             /* Past 1 is for internal HAL usage */
408             FlagMatch = IDT_INTERNAL & ~IDT_REGISTERED;
409             Interface = Internal;
410         }
411 
412         /* Reset loop variables */
413         i = Element = 0;
414 
415         /* Start looping our address uage list and interrupts */
416         CurrentAddress = HalpAddressUsageList;
417         while (TRUE)
418         {
419             /* Check for valid vector number */
420             if (i <= MAXIMUM_IDTVECTOR)
421             {
422                 /* Check if this entry should be parsed */
423                 if ((HalpIDTUsageFlags[i].Flags & FlagMatch))
424                 {
425                     /* Parse it */
426                     HalpBuildPartialFromIdt(i, &RawPartial, &TranslatedPartial);
427                     i++;
428                 }
429                 else
430                 {
431                     /* Skip this entry */
432                     i++;
433                     continue;
434                 }
435             }
436             else
437             {
438                 /* This is an address instead */
439                 if (!CurrentAddress) break;
440 
441                 /* Check if the address should be reported */
442                 if (!(CurrentAddress->Flags & FlagMatch) ||
443                     !(CurrentAddress->Element[Element].Length))
444                 {
445                     /* Nope, skip it */
446                     Element = 0;
447                     CurrentAddress = CurrentAddress->Next;
448                     continue;
449                 }
450 
451                 /* Otherwise, parse the entry */
452                 HalpBuildPartialFromAddress(Interface,
453                                             CurrentAddress,
454                                             Element,
455                                             &RawPartial,
456                                             &TranslatedPartial);
457                 Element++;
458             }
459 
460             /* Check for interface change */
461             if (RawFull->InterfaceType != Interface)
462             {
463                 /* We need to add another full descriptor */
464                 RawList->Count++;
465                 TranslatedList->Count++;
466 
467                 /* The full descriptor follows wherever we were */
468                 RawFull = (PCM_FULL_RESOURCE_DESCRIPTOR)CurrentRaw;
469                 TranslatedFull = (PCM_FULL_RESOURCE_DESCRIPTOR)CurrentTranslated;
470 
471                 /* And it is of this new interface type */
472                 RawFull->InterfaceType = Interface;
473                 TranslatedFull->InterfaceType = Interface;
474 
475                 /* And its partial descriptors begin here */
476                 RawPartialList = &RawFull->PartialResourceList;
477                 TranslatedPartialList = &TranslatedFull->PartialResourceList;
478 
479                 /* And our next full descriptor should follow here */
480                 CurrentRaw = RawFull->PartialResourceList.PartialDescriptors;
481                 CurrentTranslated = TranslatedFull->PartialResourceList.PartialDescriptors;
482             }
483 
484             /* We have written a new partial descriptor */
485             RawPartialList->Count++;
486             TranslatedPartialList->Count++;
487 
488             /* Copy our local descriptors into the actual list */
489             RtlCopyMemory(CurrentRaw, &RawPartial, sizeof(RawPartial));
490             RtlCopyMemory(CurrentTranslated, &TranslatedPartial, sizeof(TranslatedPartial));
491 
492             /* Move to the next partial descriptor */
493             CurrentRaw++;
494             CurrentTranslated++;
495         }
496     }
497 
498     /* Get the final list of the size for the kernel call later */
499     ListSize = (ULONG)((ULONG_PTR)CurrentRaw - (ULONG_PTR)RawList);
500 
501     /* Now reset back to the first full descriptor */
502     RawFull = RawList->List;
503     TranslatedFull = TranslatedList->List;
504 
505     /* And loop all the full descriptors */
506     for (i = 0; i < RawList->Count; i++)
507     {
508         /* Get the first partial descriptor in this list */
509         CurrentRaw = RawFull->PartialResourceList.PartialDescriptors;
510         CurrentTranslated = TranslatedFull->PartialResourceList.PartialDescriptors;
511 
512         /* Get the count of partials in this list */
513         Count = RawFull->PartialResourceList.Count;
514 
515         /* Loop all the partials in this list */
516         for (j = 0; j < Count; j++)
517         {
518             /* Get the sort value at this point */
519             HalpGetResourceSortValue(CurrentRaw, &CurrentScale, &CurrentSortValue);
520 
521             /* Save the current sort pointer */
522             SortedRaw = CurrentRaw;
523             SortedTranslated = CurrentTranslated;
524 
525             /* Loop all descriptors starting from this one */
526             for (k = j; k < Count; k++)
527             {
528                 /* Get the sort value at the sort point */
529                 HalpGetResourceSortValue(SortedRaw, &SortScale, &SortValue);
530 
531                 /* Check if a swap needs to occur */
532                 if ((SortScale < CurrentScale) ||
533                     ((SortScale == CurrentScale) &&
534                      (SortValue.QuadPart <= CurrentSortValue.QuadPart)))
535                 {
536                     /* Swap raw partial with the sort location partial */
537                     RtlCopyMemory(&RawPartial, CurrentRaw, sizeof(RawPartial));
538                     RtlCopyMemory(CurrentRaw, SortedRaw, sizeof(RawPartial));
539                     RtlCopyMemory(SortedRaw, &RawPartial, sizeof(RawPartial));
540 
541                     /* Swap translated partial in the same way */
542                     RtlCopyMemory(&TranslatedPartial, CurrentTranslated, sizeof(TranslatedPartial));
543                     RtlCopyMemory(CurrentTranslated, SortedTranslated, sizeof(TranslatedPartial));
544                     RtlCopyMemory(SortedTranslated, &TranslatedPartial, sizeof(TranslatedPartial));
545 
546                     /* Update the sort value at this point */
547                     HalpGetResourceSortValue(CurrentRaw, &CurrentScale, &CurrentSortValue);
548                 }
549 
550                 /* The sort location has been updated */
551                 SortedRaw++;
552                 SortedTranslated++;
553             }
554 
555             /* Move to the next partial */
556             CurrentRaw++;
557             CurrentTranslated++;
558         }
559 
560         /* Move to the next full descriptor */
561         RawFull = (PCM_FULL_RESOURCE_DESCRIPTOR)CurrentRaw;
562         TranslatedFull = (PCM_FULL_RESOURCE_DESCRIPTOR)CurrentTranslated;
563     }
564 
565     /* Mark this is an ACPI system, if it is */
566     HalpMarkAcpiHal();
567 
568     /* Tell the kernel about all this */
569     IoReportHalResourceUsage(HalName,
570                              RawList,
571                              TranslatedList,
572                              ListSize);
573 
574     /* Free our lists */
575     ExFreePool(RawList);
576     ExFreePool(TranslatedList);
577 
578     /* Get the machine's serial number */
579     HalpReportSerialNumber();
580 }
581 #endif /* !_MINIHAL_ */
582 
583 INIT_FUNCTION
584 VOID
585 NTAPI
586 HalpRegisterVector(IN UCHAR Flags,
587                    IN ULONG BusVector,
588                    IN ULONG SystemVector,
589                    IN KIRQL Irql)
590 {
591     /* Save the vector flags */
592     HalpIDTUsageFlags[SystemVector].Flags = Flags;
593 
594     /* Save the vector data */
595     HalpIDTUsage[SystemVector].Irql  = Irql;
596     HalpIDTUsage[SystemVector].BusReleativeVector = (UCHAR)BusVector;
597 }
598 
599 #ifndef _MINIHAL_
600 INIT_FUNCTION
601 VOID
602 NTAPI
603 HalpEnableInterruptHandler(IN UCHAR Flags,
604                            IN ULONG BusVector,
605                            IN ULONG SystemVector,
606                            IN KIRQL Irql,
607                            IN PVOID Handler,
608                            IN KINTERRUPT_MODE Mode)
609 {
610     /* Set the IDT_LATCHED flag for latched interrupts */
611     if (Mode == Latched) Flags |= IDT_LATCHED;
612 
613     /* Register the vector */
614     HalpRegisterVector(Flags, BusVector, SystemVector, Irql);
615 
616     /* Connect the interrupt */
617     KeRegisterInterruptHandler(SystemVector, Handler);
618 
619     /* Enable the interrupt */
620     HalEnableSystemInterrupt(SystemVector, Irql, Mode);
621 }
622 
623 INIT_FUNCTION
624 VOID
625 NTAPI
626 HalpGetNMICrashFlag(VOID)
627 {
628     UNICODE_STRING ValueName;
629     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\CrashControl");
630     OBJECT_ATTRIBUTES ObjectAttributes;
631     ULONG ResultLength;
632     HANDLE Handle;
633     NTSTATUS Status;
634     KEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
635 
636     /* Set default */
637     HalpNMIDumpFlag = 0;
638 
639     /* Initialize attributes */
640     InitializeObjectAttributes(&ObjectAttributes,
641                                &KeyName,
642                                OBJ_CASE_INSENSITIVE,
643                                NULL,
644                                NULL);
645 
646     /* Open crash key */
647     Status = ZwOpenKey(&Handle, KEY_READ, &ObjectAttributes);
648     if (NT_SUCCESS(Status))
649     {
650         /* Query key value */
651         RtlInitUnicodeString(&ValueName, L"NMICrashDump");
652         Status = ZwQueryValueKey(Handle,
653                                  &ValueName,
654                                  KeyValuePartialInformation,
655                                  &KeyValueInformation,
656                                  sizeof(KeyValueInformation),
657                                  &ResultLength);
658         if (NT_SUCCESS(Status))
659         {
660             /* Check for valid data */
661             if (ResultLength == sizeof(KEY_VALUE_PARTIAL_INFORMATION))
662             {
663                 /* Read the flag */
664                 HalpNMIDumpFlag = KeyValueInformation.Data[0];
665             }
666         }
667 
668         /* We're done */
669         ZwClose(Handle);
670     }
671 }
672 #endif  /* !_MINIHAL_ */
673 
674 /* EOF */
675