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
HalpPciBarLength(_In_ ULONG CurrentBar,_In_ ULONG NextBar)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
HalpConfigureDebuggingDevice(_In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice,_In_ ULONG PciBus,_In_ PCI_SLOT_NUMBER PciSlot,_Inout_ PPCI_COMMON_HEADER PciConfig)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
HalpMatchDebuggingDevice(_In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice,_In_ ULONG PciBus,_In_ PCI_SLOT_NUMBER PciSlot,_In_ PPCI_COMMON_HEADER PciConfig)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
HalpFindMatchingDebuggingDevice(_In_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)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
HalpRegisterPciDebuggingDeviceInfo(VOID)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
HalpReleasePciDeviceForDebugging(_Inout_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)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
HalpSetupPciDeviceForDebugging(_In_opt_ PVOID LoaderBlock,_Inout_ PDEBUG_DEVICE_DESCRIPTOR PciDevice)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