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