xref: /reactos/hal/halx86/generic/kdpci.c (revision 09dde2cf)
1 /*
2  * PROJECT:     ReactOS Hardware Abstraction Layer
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Kernel debugger PCI configurator
5  * COPYRIGHT:   Copyright 2022 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /*
9  * FIXME: We don't use a PCI resource allocator and rely on firmware to
10  * have configured PCI devices properly. The KD PCI configurator should
11  * allocate and assign PCI resources for all PCI buses
12  * before the debugging device can be enabled.
13  */
14 
15 /* INCLUDES *******************************************************************/
16 
17 #include <hal.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 #if defined(EARLY_DEBUG)
22 ULONG (*DPRINT0)(_In_ _Printf_format_string_ PCSTR Format, ...);
23 #else
24 #if defined(_MSC_VER)
25 #define DPRINT0   __noop
26 #else
27 #define DPRINT0
28 #endif
29 #endif
30 
31 PCI_TYPE1_CFG_CYCLE_BITS HalpPciDebuggingDevice[2] = {0};
32 
33 /* FUNCTIONS ******************************************************************/
34 
35 static
36 CODE_SEG("INIT")
37 ULONG
38 HalpPciBarLength(
39     _In_ ULONG CurrentBar,
40     _In_ ULONG NextBar)
41 {
42     ULONG64 Bar;
43     ULONG Length;
44 
45     Bar = CurrentBar;
46 
47     if (CurrentBar & PCI_ADDRESS_IO_SPACE)
48     {
49         Length = 1 << 2;
50     }
51     else
52     {
53         if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
54         {
55             Bar = ((ULONG64)NextBar << 32) | CurrentBar;
56         }
57 
58         Length = 1 << 4;
59     }
60 
61     while (!(Bar & Length) && Length)
62     {
63         Length <<= 1;
64     }
65 
66     return Length;
67 }
68 
69 static
70 CODE_SEG("INIT")
71 BOOLEAN
72 HalpConfigureDebuggingDevice(
73     _In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice,
74     _In_ ULONG PciBus,
75     _In_ PCI_SLOT_NUMBER PciSlot,
76     _Inout_ PPCI_COMMON_HEADER PciConfig)
77 {
78     ULONG i, Register;
79 
80     Register = PciConfig->Command & ~(PCI_ENABLE_MEMORY_SPACE |
81                                      PCI_ENABLE_IO_SPACE);
82     HalpPhase0SetPciDataByOffset(PciBus,
83                                  PciSlot,
84                                  &Register,
85                                  FIELD_OFFSET(PCI_COMMON_HEADER, Command),
86                                  sizeof(USHORT));
87 
88     /* Fill out the device descriptor */
89     for (i = 0; i < MAXIMUM_DEBUG_BARS; ++i)
90     {
91         ULONG Length, NextBar;
92         PDEBUG_DEVICE_ADDRESS DeviceAddress;
93 
94         DeviceAddress = &PciDevice->BaseAddress[i];
95         DeviceAddress->Valid = FALSE;
96 
97         Register = 0xFFFFFFFF;
98         HalpPhase0SetPciDataByOffset(PciBus,
99                                      PciSlot,
100                                      &Register,
101                                      FIELD_OFFSET(PCI_COMMON_HEADER, u.type0.BaseAddresses[i]),
102                                      sizeof(ULONG));
103         HalpPhase0GetPciDataByOffset(PciBus,
104                                      PciSlot,
105                                      &Register,
106                                      FIELD_OFFSET(PCI_COMMON_HEADER, u.type0.BaseAddresses[i]),
107                                      sizeof(ULONG));
108         HalpPhase0SetPciDataByOffset(PciBus,
109                                      PciSlot,
110                                      &PciConfig->u.type0.BaseAddresses[i],
111                                      FIELD_OFFSET(PCI_COMMON_HEADER, u.type0.BaseAddresses[i]),
112                                      sizeof(ULONG));
113 
114         if (i < MAXIMUM_DEBUG_BARS - 1)
115             NextBar = PciConfig->u.type0.BaseAddresses[i + 1];
116         else
117             NextBar = 0;
118 
119         Length = HalpPciBarLength(Register, NextBar);
120         if (Register == 0 || Length == 0)
121             continue;
122 
123         /* I/O space */
124         if (Register & PCI_ADDRESS_IO_SPACE)
125         {
126             DeviceAddress->Type = CmResourceTypePort;
127             DeviceAddress->Length = Length;
128             DeviceAddress->Valid = TRUE;
129             DeviceAddress->TranslatedAddress =
130                 UlongToPtr(PciConfig->u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_ADDRESS_MASK);
131 
132             DPRINT0("BAR[%u] IO  %lx, length 0x%lx, 0x%lx\n",
133                     i,
134                     DeviceAddress->TranslatedAddress,
135                     Length,
136                     Register);
137         }
138         else
139         {
140             PHYSICAL_ADDRESS PhysicalAddress;
141             BOOLEAN SkipBar = FALSE;
142 
143             DeviceAddress->Type = CmResourceTypeMemory;
144             DeviceAddress->Length = Length;
145             DeviceAddress->Valid = TRUE;
146 
147             /* 32-bit memory space */
148             PhysicalAddress.HighPart = 0;
149             PhysicalAddress.LowPart =
150                 PciConfig->u.type0.BaseAddresses[i] & PCI_ADDRESS_MEMORY_ADDRESS_MASK;
151 
152             /* 64-bit memory space */
153             if (((Register & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT))
154             {
155                 PhysicalAddress.HighPart = NextBar;
156                 SkipBar = TRUE;
157             }
158 
159             DPRINT0("BAR[%u] MEM %I64x, length 0x%lx, 0x%lx\n",
160                     i,
161                     PhysicalAddress.QuadPart,
162                     Length,
163                     Register);
164 
165             if (SkipBar)
166             {
167                 ++i;
168             }
169 
170             DeviceAddress->TranslatedAddress =
171                 HalpMapPhysicalMemory64(PhysicalAddress, BYTES_TO_PAGES(Length));
172         }
173     }
174     PciDevice->Bus = PciBus;
175     PciDevice->Slot = PciSlot.u.AsULONG;
176     PciDevice->VendorID = PciConfig->VendorID;
177     PciDevice->DeviceID = PciConfig->DeviceID;
178     PciDevice->BaseClass = PciConfig->BaseClass;
179     PciDevice->SubClass = PciConfig->SubClass;
180     PciDevice->ProgIf = PciConfig->ProgIf;
181 
182     /* Enable decodes */
183     PciConfig->Command |= (PCI_ENABLE_MEMORY_SPACE |
184                            PCI_ENABLE_IO_SPACE |
185                            PCI_ENABLE_BUS_MASTER);
186     HalpPhase0SetPciDataByOffset(PciBus,
187                                  PciSlot,
188                                  &PciConfig->Command,
189                                  FIELD_OFFSET(PCI_COMMON_HEADER, Command),
190                                  sizeof(USHORT));
191 
192     return TRUE;
193 }
194 
195 static
196 CODE_SEG("INIT")
197 BOOLEAN
198 HalpMatchDebuggingDevice(
199     _In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice,
200     _In_ ULONG PciBus,
201     _In_ PCI_SLOT_NUMBER PciSlot,
202     _In_ PPCI_COMMON_HEADER PciConfig)
203 {
204     /* Check if we weren't given a specific device location */
205     if (PciDevice->Bus == 0xFFFFFFFF && PciDevice->Slot == 0xFFFFFFFF)
206     {
207         if (PciDevice->DeviceID == 0xFFFF && PciDevice->VendorID == 0xFFFF)
208         {
209             if (PciDevice->BaseClass == PciConfig->BaseClass &&
210                 PciDevice->SubClass == PciConfig->SubClass)
211             {
212                 if (PciDevice->ProgIf == 0xFF ||
213                     PciDevice->ProgIf == PciConfig->ProgIf)
214                 {
215                     return TRUE;
216                 }
217             }
218         }
219         else if (PciDevice->DeviceID == PciConfig->DeviceID &&
220                  PciDevice->VendorID == PciConfig->VendorID)
221         {
222             return TRUE;
223         }
224     }
225     else if (PciDevice->Bus == PciBus &&
226              PciDevice->Slot == PciSlot.u.AsULONG)
227     {
228          return TRUE;
229     }
230 
231     return FALSE;
232 }
233 
234 static
235 CODE_SEG("INIT")
236 BOOLEAN
237 HalpFindMatchingDebuggingDevice(
238     _In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)
239 {
240     ULONG BusNumber, DeviceNumber, FunctionNumber;
241 
242     for (BusNumber = 0; BusNumber < 0xFF; ++BusNumber)
243     {
244         for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; ++DeviceNumber)
245         {
246             for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; ++FunctionNumber)
247             {
248                 ULONG Bytes;
249                 PCI_SLOT_NUMBER PciSlot;
250                 PCI_COMMON_HEADER PciConfig;
251 
252                 PciSlot.u.bits.DeviceNumber = DeviceNumber;
253                 PciSlot.u.bits.FunctionNumber = FunctionNumber;
254                 PciSlot.u.bits.Reserved = 0;
255                 Bytes = HalpPhase0GetPciDataByOffset(BusNumber,
256                                                      PciSlot,
257                                                      &PciConfig,
258                                                      0,
259                                                      PCI_COMMON_HDR_LENGTH);
260                 if (Bytes != PCI_COMMON_HDR_LENGTH ||
261                     PciConfig.VendorID == PCI_INVALID_VENDORID ||
262                     PciConfig.VendorID == 0)
263                 {
264                     if (FunctionNumber == 0)
265                     {
266                         /* This slot has no single- or a multi-function device */
267                         break;
268                     }
269                     else
270                     {
271                         /* Continue scanning the functions */
272                         continue;
273                     }
274                 }
275 
276                 DPRINT0("Check %02x:%02x.%x [%04x:%04x]\n",
277                         BusNumber, DeviceNumber, FunctionNumber,
278                         PciConfig.VendorID, PciConfig.DeviceID);
279 
280                 switch (PCI_CONFIGURATION_TYPE(&PciConfig))
281                 {
282                     case PCI_DEVICE_TYPE:
283                     {
284                         if (HalpMatchDebuggingDevice(PciDevice, BusNumber, PciSlot, &PciConfig))
285                         {
286                             DPRINT0("Found device\n");
287 
288                             if (HalpConfigureDebuggingDevice(PciDevice,
289                                                              BusNumber,
290                                                              PciSlot,
291                                                              &PciConfig))
292                             {
293                                 DPRINT0("Device is ready\n");
294                                 return TRUE;
295                             }
296                         }
297                         break;
298                     }
299 
300                     case PCI_BRIDGE_TYPE:
301                     {
302                         /* FIXME: Implement PCI resource allocator */
303                         break;
304                     }
305 
306                     case PCI_CARDBUS_BRIDGE_TYPE:
307                     {
308                         /* FIXME: Implement PCI resource allocator */
309                         break;
310                     }
311 
312                     default:
313                         break;
314                 }
315 
316                 if (!PCI_MULTIFUNCTION_DEVICE(&PciConfig))
317                 {
318                     /* The device is a single function device */
319                     break;
320                 }
321             }
322         }
323     }
324 
325     return FALSE;
326 }
327 
328 CODE_SEG("INIT")
329 VOID
330 NTAPI
331 HalpRegisterPciDebuggingDeviceInfo(VOID)
332 {
333     ULONG i;
334     NTSTATUS Status;
335     WCHAR StringBuffer[16];
336     BOOLEAN HasDebuggingDevice = FALSE;
337     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\"
338                                                  L"CurrentControlSet\\Services\\PCI\\Debug");
339     HANDLE Handle, KeyHandle;
340 
341     PAGED_CODE();
342 
343     for (i = 0; i < RTL_NUMBER_OF(HalpPciDebuggingDevice); ++i)
344     {
345         if (HalpPciDebuggingDevice[i].InUse)
346         {
347             HasDebuggingDevice = TRUE;
348             break;
349         }
350     }
351     if (!HasDebuggingDevice)
352     {
353         /* Nothing to register */
354         return;
355     }
356 
357     Status = HalpOpenRegistryKey(&Handle, 0, &KeyName, KEY_ALL_ACCESS, TRUE);
358     if (!NT_SUCCESS(Status))
359         return;
360 
361     for (i = 0; i < RTL_NUMBER_OF(HalpPciDebuggingDevice); ++i)
362     {
363         ULONG Value;
364         PCI_SLOT_NUMBER PciSlot;
365 
366         if (!HalpPciDebuggingDevice[i].InUse)
367             continue;
368 
369         RtlInitEmptyUnicodeString(&KeyName, StringBuffer, sizeof(StringBuffer));
370         RtlIntegerToUnicodeString(i, 10, &KeyName);
371         Status = HalpOpenRegistryKey(&KeyHandle,
372                                      Handle,
373                                      &KeyName,
374                                      KEY_ALL_ACCESS,
375                                      TRUE);
376         if (!NT_SUCCESS(Status))
377             continue;
378 
379         Value = HalpPciDebuggingDevice[i].BusNumber;
380         RtlInitUnicodeString(&KeyName, L"Bus");
381         ZwSetValueKey(KeyHandle,
382                       &KeyName,
383                       0,
384                       REG_DWORD,
385                       &Value,
386                       sizeof(Value));
387 
388         PciSlot.u.AsULONG = 0;
389         PciSlot.u.bits.DeviceNumber = HalpPciDebuggingDevice[i].DeviceNumber;
390         PciSlot.u.bits.FunctionNumber = HalpPciDebuggingDevice[i].FunctionNumber;
391         Value = PciSlot.u.AsULONG;
392         RtlInitUnicodeString(&KeyName, L"Slot");
393         ZwSetValueKey(KeyHandle,
394                       &KeyName,
395                       0,
396                       REG_DWORD,
397                       &Value,
398                       sizeof(Value));
399 
400         ZwClose(KeyHandle);
401     }
402 
403     ZwClose(Handle);
404 }
405 
406 /**
407  * @brief
408  * Releases the PCI device MMIO mappings
409  * previously allocated with HalpSetupPciDeviceForDebugging().
410  *
411  * This is used to release resources when a device specific initialization fails.
412  *
413  * @param[in,out]   PciDevice
414  * Pointer to the debug device descriptor, whose mappings are to be released.
415  *
416  * @return STATUS_SUCCESS.
417  */
418 CODE_SEG("INIT")
419 NTSTATUS
420 NTAPI
421 HalpReleasePciDeviceForDebugging(
422     _Inout_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)
423 {
424     ULONG i;
425 
426     DPRINT0("%s(%p) called\n", __FUNCTION__, PciDevice);
427 
428     for (i = 0; i < MAXIMUM_DEBUG_BARS; ++i)
429     {
430         PDEBUG_DEVICE_ADDRESS DeviceAddress = &PciDevice->BaseAddress[i];
431 
432         if (DeviceAddress->Type == CmResourceTypeMemory && DeviceAddress->Valid)
433         {
434             HalpUnmapVirtualAddress(DeviceAddress->TranslatedAddress,
435                                     BYTES_TO_PAGES(DeviceAddress->Length));
436 
437             DeviceAddress->Valid = FALSE;
438         }
439     }
440 
441     return STATUS_SUCCESS;
442 }
443 
444 /**
445  * @brief
446  * Finds and fully initializes the PCI device
447  * associated with the supplied debug device descriptor.
448  *
449  * @param[in]       LoaderBlock
450  * Pointer to the Loader parameter block. Can be NULL.
451  *
452  * @param[in,out]   PciDevice
453  * Pointer to the debug device descriptor.
454  *
455  * @return Status.
456  *
457  * This routine is used to match devices to debug device descriptors during
458  * boot phase of the system. This function will search the first device that
459  * matches the criteria given by the fields of the debug device descriptor.
460  * A value of all 1's for the field will indicate that the function
461  * should ignore that field in the search criteria.
462  * The @c Length field of the debug memory requirements optionally specifies
463  * library-determined number of bytes to be allocated for the device context.
464  *
465  * Example:
466  * @code
467  * RtlZeroMemory(&PciDevice, sizeof(DEBUG_DEVICE_DESCRIPTOR));
468  * PciDevice.VendorID = 0xFFFF;
469  * PciDevice.DeviceID = 0xFFFF;
470  * PciDevice.Bus = 0xFFFFFFFF;
471  * PciDevice.Slot = 0xFFFFFFFF;
472  * PciDevice.BaseClass = PCI_CLASS_SERIAL_BUS_CTLR;
473  * PciDevice.SubClass = PCI_SUBCLASS_SB_USB;
474  * PciDevice.ProgIf = 0x30;
475  * PciDevice.Memory.Length = sizeof(HW_EXTENSION);
476  * @endcode
477  *
478  * @sa HalpReleasePciDeviceForDebugging
479  */
480 CODE_SEG("INIT")
481 NTSTATUS
482 NTAPI
483 HalpSetupPciDeviceForDebugging(
484     _In_opt_ PVOID LoaderBlock,
485     _Inout_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)
486 {
487     ULONG i;
488     ULONG64 MaxAddress;
489     PFN_NUMBER PageCount;
490     PCI_SLOT_NUMBER PciSlot;
491     PHYSICAL_ADDRESS PhysicalAddress;
492     PPCI_TYPE1_CFG_CYCLE_BITS DebuggingDevice;
493 
494 #if defined(EARLY_DEBUG)
495     if (LoaderBlock)
496     {
497         /* Define your own function or use the trick with FreeLoader */
498         DPRINT0 = ((PLOADER_PARAMETER_BLOCK)LoaderBlock)->u.I386.CommonDataArea;
499     }
500 #endif
501 
502     DPRINT0("%s(%p, %p) called\n", __FUNCTION__, LoaderBlock, PciDevice);
503 
504     if (!HalpFindMatchingDebuggingDevice(PciDevice))
505     {
506         DPRINT0("No device found matching given device descriptor!\n");
507         return STATUS_DEVICE_DOES_NOT_EXIST;
508     }
509 
510     if (PciDevice->Initialized)
511         return STATUS_SUCCESS;
512 
513     PciSlot.u.AsULONG = PciDevice->Slot;
514 
515     /* Check if the device is already present */
516     for (i = 0; i < RTL_NUMBER_OF(HalpPciDebuggingDevice); ++i)
517     {
518         DebuggingDevice = &HalpPciDebuggingDevice[i];
519 
520         if (DebuggingDevice->InUse &&
521             DebuggingDevice->DeviceNumber == PciSlot.u.bits.DeviceNumber &&
522             DebuggingDevice->FunctionNumber == PciSlot.u.bits.FunctionNumber &&
523             DebuggingDevice->BusNumber == PciDevice->Bus)
524         {
525             DPRINT0("Device %p(0x%lx) is already in use!\n", PciDevice, PciDevice->Slot);
526             return STATUS_UNSUCCESSFUL;
527         }
528     }
529 
530     /* Save the device location */
531     for (i = 0; i < RTL_NUMBER_OF(HalpPciDebuggingDevice); ++i)
532     {
533         DebuggingDevice = &HalpPciDebuggingDevice[i];
534 
535         if (!DebuggingDevice->InUse)
536         {
537             DebuggingDevice->DeviceNumber = PciSlot.u.bits.DeviceNumber;
538             DebuggingDevice->FunctionNumber = PciSlot.u.bits.FunctionNumber;
539             DebuggingDevice->BusNumber = PciDevice->Bus;
540             DebuggingDevice->InUse = TRUE;
541 
542             PciDevice->Initialized = TRUE;
543             break;
544         }
545     }
546     if (i == RTL_NUMBER_OF(HalpPciDebuggingDevice))
547     {
548         DPRINT0("Maximum device count reached!\n");
549         return STATUS_UNSUCCESSFUL;
550     }
551 
552     if (!PciDevice->Memory.Length)
553         return STATUS_SUCCESS;
554 
555     if (!LoaderBlock)
556         return STATUS_INVALID_PARAMETER_1;
557 
558     if (!PciDevice->Memory.MaxEnd.QuadPart)
559     {
560         PciDevice->Memory.MaxEnd.QuadPart = (ULONG64)-1;
561     }
562     MaxAddress = min(PciDevice->Memory.MaxEnd.QuadPart, 0xFFFFFFFF);
563     PageCount = BYTES_TO_PAGES(PciDevice->Memory.Length);
564 
565     /* Allocate the device context */
566     PhysicalAddress.QuadPart = HalpAllocPhysicalMemory(LoaderBlock,
567                                                        MaxAddress,
568                                                        PageCount,
569                                                        FALSE);
570     PciDevice->Memory.Start = PhysicalAddress;
571     if (!PhysicalAddress.QuadPart)
572     {
573         return STATUS_INSUFFICIENT_RESOURCES;
574     }
575     PciDevice->Memory.VirtualAddress = HalpMapPhysicalMemory64(PhysicalAddress, PageCount);
576 
577     return STATUS_SUCCESS;
578 }
579