Understanding how UEFI EDK2 discovers, enumerates, and manages PCI/PCIe devices
Full 256-byte PCI header and 4K PCIe Extended Configuration Space
How x86 and ARM platforms read/write PCI configuration space
ECAM_BASE + (Bus << 20) + (Dev << 15) + (Fun << 12) + Offset
Complete device discovery and resource allocation flow in PciBusDxe
Platform-level PCI access abstraction
Device-level PCI access interface
Key differences in the PCI subsystem across architectures
Key EDK2 PCI subsystem source code snippets
// MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c (simplified)
for (Bus = StartBus; Bus <= EndBus; Bus++) {
for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) {
for (Func = 0; Func <= PCI_MAX_FUNC; Func++) {
Status = PciRootBridgeIo->Pci.Read (
PciRootBridgeIo,
EfiPciWidthUint32,
EFI_PCI_ADDRESS (Bus, Device, Func, 0),
1,
&PciData
);
if (PciData.Hdr.VendorId == 0xFFFF) {
// No device present
if (Func == 0) break; // Skip remaining functions
continue;
}
// Device found — create PCI_IO_DEVICE
PciDevice = CreatePciIoDevice (Bridge, Bus, Device, Func);
// Check multi-function bit
if (Func == 0 &&
!IS_PCI_MULTI_FUNC (&PciData)) {
break; // Not multi-function, skip Func 1-7
}
}
}
}
// MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c (simplified)
for (Offset = 0x10; Offset <= 0x24; Offset += 4) {
// 1. Save original BAR value
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, Offset, 1, &OriginalValue);
// 2. Write all 1s
AllOnes = 0xFFFFFFFF;
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &AllOnes);
// 3. Read back to get size mask
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, Offset, 1, &ReadBack);
// 4. Restore original value
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &OriginalValue);
if (ReadBack == 0) continue; // BAR not implemented
if (ReadBack & 0x01) {
// I/O BAR: mask low 2 bits
Size = ~(ReadBack & 0xFFFFFFFC) + 1;
} else {
// Memory BAR: mask low 4 bits
Size = ~(ReadBack & 0xFFFFFFF0) + 1;
Is64Bit = ((ReadBack & 0x06) == 0x04);
}
}
// Example: Device driver reads Vendor/Device ID
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT16 VendorId;
UINT16 DeviceId;
// Open PCI IO Protocol on the device handle
Status = gBS->OpenProtocol (
DeviceHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
DriverBinding->DriverBindingHandle,
DeviceHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
// Read Vendor ID (offset 0x00)
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint16,
PCI_VENDOR_ID_OFFSET, // 0x00
1,
&VendorId
);
// Read Device ID (offset 0x02)
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint16,
PCI_DEVICE_ID_OFFSET, // 0x02
1,
&DeviceId
);
DEBUG ((DEBUG_INFO, "Found device %04X:%04X\n", VendorId, DeviceId));
// MdeModulePkg/Bus/Pci/PciHostBridgeDxe (ECAM path, simplified)
EFI_STATUS
RootBridgeIoPciRead (
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This,
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width,
IN UINT64 Address,
IN UINTN Count,
OUT VOID *Buffer
)
{
PCI_ROOT_BRIDGE_INSTANCE *RootBridge;
UINT64 EcamBase;
UINT64 PciAddress;
RootBridge = ROOT_BRIDGE_FROM_THIS (This);
EcamBase = RootBridge->EcamBase;
// Calculate ECAM address:
// Address encodes Bus[23:16] Dev[15:11] Fun[10:8] Reg[7:0]
PciAddress = EcamBase
+ ((RB_PCI_BUS(Address)) << 20)
+ ((RB_PCI_DEV(Address)) << 15)
+ ((RB_PCI_FUNC(Address)) << 12)
+ RB_PCI_REG(Address);
// Perform memory-mapped read
return MmioReadBuffer (Width, PciAddress, Count, Buffer);
}
Further reading and official specifications
PCI-SIG official specifications — PCI Local Bus, PCIe Base Spec, ECN
MdeModulePkg/Bus/Pci — PciBusDxe, PciHostBridgeDxe, PciSioSerialDxe
UEFI Spec Chapter 14 (PCI Bus Support), PI Spec Vol 5 (PCI Host Bridge)
OSDev Wiki PCI page, ECAM specification, MCFG ACPI table documentation