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