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