- SW 透過 host controller 設定 BAR 的位址,實際上在 QEMU 中就是新增/改變對應到該 BAR memory region 的 memory subregion base address
- BAR 空間記錄的是 PCI/PCIe 自己的 address space,而不是 memory address space,兩者實際上是不相通的。但由於大部分系統都會將 PCI/PCIe address space 映射到相同位址的 memory address space,因此”看起來”就像是 CPU 直接對 memory address space 做存取,可其實兩個 address spaces 是需要透過 host controller 做轉換的
- CPU 存取 PCI/PCIe device 的時候,實際硬體是必須透過 host controller 來將 MMIO transaction (e.g., AXI transaction) / address 轉換成 PCI/PCIe transaction / address 才能存取到該 PCI/PCIe device 的。但 QEMU 的模擬並不會真的透過 host controller,而是直接存取前述之 BAR memory subregion
- 反之,當 PCI/PCIe master device 做 DMA 的時候,也是需要透過 host controller 才能將其所發出的 PCI/PCIe transaction / address 轉換成 MMIO transaction (e.g., AXI transaction) / address
- BAR 空間記錄的是 PCI/PCIe 自己的 address space,而不是 memory address space,兩者實際上是不相通的。但由於大部分系統都會將 PCI/PCIe address space 映射到相同位址的 memory address space,因此”看起來”就像是 CPU 直接對 memory address space 做存取,可其實兩個 address spaces 是需要透過 host controller 做轉換的
- 在配置 BAR 位址後,CPU 就可以透過存取 BAR 空間 (通常會被映射到 PCI/PCIe device 的 controller registers) 來控制該 PCI/PCIe device
- e.g., PCIe BAR0 所記錄之 base address =
0x40000000, size =0x1000000(16 MB),CPU 就可以透過存取0x40000000 ~ 0x40FFFFFF來控制該 PCI/PCIe device
- e.g., PCIe BAR0 所記錄之 base address =
PCI/PCIe Device:
- TYPE_PCI_DEVICE (
struct PCIDevice,hw/pci/pci.c)TYPE_DEVICE (
struct DeviceState,hw/core/qdev.c)There no such TYPE_PCIE_DEVICE. PCIe related structure is embedded in
struct PCIDevice:
- TYPE_PCI_DEVICE (
PCI Bus:
- TYPE_PCI_BUS (
struct PCIBus,hw/pci/pci.c)- TYPE_BUS (
struct BusState,hw/core/bus.c)
- TYPE_BUS (
- TYPE_PCI_BUS (
PCIe Bus:
- TYPE_PCIE_BUS (
hw/pci/pci.c)- TYPE_PCI_BUS (
struct PCIBus,hw/pci/pci.c)- TYPE_BUS (
struct BusState,hw/core/bus.c)
- TYPE_BUS (
- TYPE_PCI_BUS (
- TYPE_PCIE_BUS (
CXL Bus:
- TYPE_CXL_BUS (
hw/pci/pci.c)- TYPE_PCIE_BUS (
hw/pci/pci.c)- TYPE_PCI_BUS (
struct PCIBus,hw/pci/pci.c)- TYPE_BUS (
struct BusState,hw/core/bus.c)
- TYPE_BUS (
- TYPE_PCI_BUS (
- TYPE_PCIE_BUS (
- TYPE_CXL_BUS (
PCI-PCI Bridge - PCI-PCI bridge itself is a
PCIDevice, but also includes aPCIBus:- TYPE_PCI_BRIDGE (
struct PCIBridge,hw/pci/pci_bridge.c)- TYPE_PCI_DEVICE (
struct PCIDevice,hw/pci/pci.c)- TYPE_DEVICE (
struct DeviceState,hw/core/qdev.c)
- TYPE_DEVICE (
- TYPE_PCI_DEVICE (
- TYPE_PCI_BRIDGE (
PCIe-PCI Bridge:
- TYPE_PCIE_PCI_BRIDGE_DEV (
struct PCIBridgeDev,hw/pci-bridge/pcie_pci_bridge.c)- TYPE_PCI_BRIDGE (
struct PCIBridge,hw/pci/pci_bridge.c)- TYPE_PCI_DEVICE (
struct PCIDevice,hw/pci/pci.c)- TYPE_DEVICE (
struct DeviceState,hw/core/qdev.c)
- TYPE_DEVICE (
- TYPE_PCI_DEVICE (
- TYPE_PCI_BRIDGE (
- TYPE_PCIE_PCI_BRIDGE_DEV (
PCIe Root Port:
- TYPE_PCIE_ROOT_PORT (
hw/pci-bridge/pcie_root_port.c)- TYPE_PCIE_SLOT (
struct PCIESlot,hw/pci/pcie_port.c)- TYPE_PCIE_PORT (
struct PCIEPort,hw/pci/pcie_port.c)- TYPE_PCI_BRIDGE (
struct PCIBridge,hw/pci/pci_bridge.c)- TYPE_PCI_DEVICE (
struct PCIDevice,hw/pci/pci.c)- TYPE_DEVICE (
struct DeviceState,hw/core/qdev.c)
- TYPE_DEVICE (
- TYPE_PCI_DEVICE (
- TYPE_PCI_BRIDGE (
- TYPE_PCIE_PORT (
- TYPE_PCIE_SLOT (
- TYPE_PCIE_ROOT_PORT (
PCI Host Bridge - For CPU to access the PCI bus, converts the memory loads/stores to PCI loads/stores. It also includes a
PCIBus, which is created withpci_register_root_bus().- TYPE_PCI_HOST_BRIDGE (
struct PCIHostState,hw/pci/pci_host.c)- TYPE_SYS_BUS_DEVICE (
struct SysBusDevice,hw/core/sysbus.c)
- TYPE_SYS_BUS_DEVICE (
- TYPE_PCI_HOST_BRIDGE (
PCIe Host Bridge - For CPU to access the PCIe bus, converts the memory loads/stores to PCI loads/stores.
- TYPE_PCIE_HOST_BRIDGE (
struct PCIExpressHost,hw/pci/pcie_host.c)- TYPE_PCI_HOST_BRIDGE (
struct PCIHostState,hw/pci/pci_host.c)- TYPE_SYS_BUS_DEVICE (
struct SysBusDevice,hw/core/sysbus.c)
- TYPE_SYS_BUS_DEVICE (
- TYPE_PCI_HOST_BRIDGE (
- TYPE_PCIE_HOST_BRIDGE (
- DesignWare PCIe host controller (
hw/pci-host/designware.c):- TYPE_DESIGNWARE_PCIE_HOST (
struct DesignwarePCIEHost) - Host-facing part- TYPE_PCI_HOST_BRIDGE (
struct PCIHostState,hw/pci/pci_host.c)
- TYPE_PCI_HOST_BRIDGE (
- TYPE_DESIGNWARE_PCIE_ROOT (
struct DesignwarePCIERoot) - PCI-facing part- TYPE_PCI_BRIDGE (
struct PCIBridge,hw/pci/pci_bridge.c)
- TYPE_PCI_BRIDGE (
- TYPE_DESIGNWARE_PCIE_HOST (
- QEMU Generic PCIe host controller (
hw/pci-host/gpex.c)- TYPE_GPEX_HOST (
struct GPEXHost) - Host-facing part- TYPE_PCIE_HOST_BRIDGE (
struct PCIExpressHost,hw/pci/pcie_host.c)
- TYPE_PCIE_HOST_BRIDGE (
- TYPE_GPEX_ROOT_DEVICE (
struct GPEXRootState) - PCI-facing part- TYPE_PCI_DEVICE (
struct PCIDevice,hw/pci/pci.c)
- TYPE_PCI_DEVICE (
- TYPE_GPEX_HOST (
- Xilinx PCIe host controller (
hw/pci-host/xilinx-pcie.c)- TYPE_XILINX_PCIE_HOST (
struct XilinxPCIEHost) - Host-facing part- TYPE_PCIE_HOST_BRIDGE (
struct PCIExpressHost,hw/pci/pcie_host.c)
- TYPE_PCIE_HOST_BRIDGE (
- TYPE_XILINX_PCIE_ROOT (
struct XilinxPCIERoot) - PCI-facing part- TYPE_PCI_BRIDGE (
struct PCIBridge,hw/pci/pci_bridge.c)
- TYPE_PCI_BRIDGE (
- TYPE_XILINX_PCIE_HOST (
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
PCIe Host Read/Write
| |
| |
| |
| |
ViewPort MEM/CFG Read/Write
| |
| |
| |
regs:config: Read/write the PCIe devices’ configuration space registers (e.g., Vendor ID, Device ID, BAR 0, BAR 1… etc) under DW PCIe host controller- base:
0x12_00000000 - size:
0x10000000hw/pci-host/designware.c:viewport->cfgdesignware_pci_host_conf_ops:- .read =
designware_pcie_root_data_read() - .write =
designware_pcie_root_data_write()designware_pcie_root_data_access(), get PCIe device’s BDF fromviewport->targetpci_host_config_write_common()pci_host_config_read_common()
- .read =
- base:
dbi: Data Bus Interface, read/write DW PCIe host controller itself’s configuration space registers (e.g., Vendor ID, Device ID, BAR 0, BAR 1… etc)- base:
0x11_00000000 - size:
0x1_00000000hw/pci-host/designware.c:designware_pci_mmio_ops:- .read =
designware_pcie_host_mmio_read(), Device/Function ID are hard-coded to0/0(i.e. DWPCIe host controller itself)pci_host_config_read_common()
- .write:
designware_pcie_host_mmio_write()pci_host_config_write_common()designware_pcie_root_class_init()- k->config_read =
designware_pcie_root_config_read() - k->config_write =
designware_pcie_root_config_write()
- k->config_read =
- .read =
- base:
mgmt: PHY management- base:
0x20004000 - size:
0x1000
- base:
ranges:Outbound viewports (CPU address space → PCIe address space):
- e.g., Configure BAR to store the base address (in PCI address space) of PCIe device’s MMIO registers…etc. CPU can then access PCIe device’s MMIO register by the CPU address. The value written to BAR is allocated by the Linux from the given memory regions specified in
ranges.
- e.g., Configure BAR to store the base address (in PCI address space) of PCIe device’s MMIO registers…etc. CPU can then access PCIe device’s MMIO register by the CPU address. The value written to BAR is allocated by the Linux from the given memory regions specified in
dma-ranges:Inbound viewports (PCIe address space → CPU address space):
- e.g., PCIe device DMA.
DW PCIe host controller registers root PCI bus’ MemoryRegion:
host->pci.address_space(AddressSpace)- Container:
host->pci.address_space_root(MemoryRegion; size:UINT64_MAX)- Aliases:
root->viewports[inbound][i]->mem(MemoryRegion; base and size are run-time configurable) - Container:
host->pci.memory(MemoryRegion; base:0x0, size:UINT64_MAX)- Aliases:
root->viewports[outbound][i]->mem(MemoryRegion; base and size are run-time configurable) root->msi.iomem(MemoryRegion; base is run-time configurable, size:0x4)
- Aliases:
- Aliases:
- Container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28// hw/pci-host/designware.c static void designware_pcie_host_realize(DeviceState *dev, Error **errp) { ... pci->bus = pci_register_root_bus(dev, "pcie", designware_pcie_set_irq, pci_swizzle_map_irq_fn, s, &s->pci.memory, &s->pci.io, 0, 4, TYPE_PCIE_BUS); memory_region_init(&s->pci.address_space_root, OBJECT(s), "pcie-bus-address-space-root", UINT64_MAX); memory_region_add_subregion(&s->pci.address_space_root, 0x0, &s->pci.memory); address_space_init(&s->pci.address_space, &s->pci.address_space_root, "pcie-bus-address-space"); pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal); }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17// hw/pci/pci.c PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, int nirq, const char *typename) { PCIBus *bus; bus = pci_root_bus_new(parent, name, address_space_mem, address_space_io, devfn_min, typename); pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); return bus; }1 2 3 4 5 6 7 8 9 10 11 12 13 14// hw/pci/pci.c PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, const char *typename) { PCIBus *bus; bus = PCI_BUS(qbus_new(typename, parent, name)); pci_root_bus_internal_init(bus, parent, address_space_mem, address_space_io, devfn_min); return bus; }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19// hw/pci/pci.c static void pci_root_bus_internal_init(PCIBus *bus, DeviceState *parent, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min) { assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; bus->slot_reserved_mask = 0x0; bus->address_space_mem = address_space_mem; bus->address_space_io = address_space_io; bus->flags |= PCI_BUS_IS_ROOT; /* host bridge */ QLIST_INIT(&bus->child); pci_host_bus_register(parent); }PCIe device’s
bus_master_as:- PCI device
bus_master_as, e.g., DW PCIe host controller + e1000e:pci-dev->bus_master_as(AddressSpace, e1000e):- Container:
pci_dev->bus_master_container_region(MemoryRegion, e1000e)- Alias:
pci_dev->bus_master_enable_region(MemoryRegion, e1000e), alias ofhost->pci.address_space’s (AddressSpace, DW PCIe) root MemoryRegion ⇒ Container:host->pci.address_space_root(MemoryRegion, DW PCIe, size:UINT64_MAX); base:0x0, size:host->pci.address_space_root’s size ⇒UINT64_MAX.- Aliases:
root->viewports[inbound][i]->mem(MemoryRegion; base and size are run-time configurable) - Container:
host->pci.memory(MemoryRegion; base:0x0, size:UINT64_MAX)- Aliases:
root->viewports[outbound][i]->mem(MemoryRegion; base and size are run-time configurable) root->msi.iomem(MemoryRegion; base is run-time configurable, size:0x4)
- Aliases:
- Aliases:
- Alias:
- Container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28// hw/pci/pci.c /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, const char *name, int devfn, Error **errp) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); PCIConfigReadFunc *config_read = pc->config_read; PCIConfigWriteFunc *config_write = pc->config_write; Error *local_err = NULL; DeviceState *dev = DEVICE(pci_dev); PCIBus *bus = pci_get_bus(pci_dev); ... memory_region_init(&pci_dev->bus_master_container_region, OBJECT(pci_dev), "bus master container", UINT64_MAX); address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_container_region, pci_dev->name); if (phase_check(MACHINE_INIT_PHASE_READY)) { pci_init_bus_master(pci_dev); } ... return pci_dev; }1 2 3 4 5 6 7 8 9 10 11// hw/pci/pci.c static void pci_bus_realize(BusState *qbus, Error **errp) { PCIBus *bus = PCI_BUS(qbus); bus->machine_done.notify = pcibus_machine_done; qemu_add_machine_init_done_notifier(&bus->machine_done); vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus); }1 2 3 4 5 6 7 8 9 10 11 12 13 14// hw/pci/pci.c static void pcibus_machine_done(Notifier *notifier, void *data) { PCIBus *bus = container_of(notifier, PCIBus, machine_done); int i; for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { if (bus->devices[i]) { pci_init_bus_master(bus->devices[i]); pci_init_mem_attrs(bus->devices[i]); } } }1 2 3 4 5 6 7 8 9 10 11 12 13// hw/pci/pci.c static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); memory_region_init_alias(&pci_dev->bus_master_enable_region, OBJECT(pci_dev), "bus master", dma_as->root, 0, memory_region_size(dma_as->root)); memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); memory_region_add_subregion(&pci_dev->bus_master_container_region, 0, &pci_dev->bus_master_enable_region); }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33// hw/pci/pci.c AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) { PCIBus *bus = pci_get_bus(dev); PCIBus *iommu_bus = bus; uint8_t devfn = dev->devfn; while (iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { PCIBus *parent_bus = pci_get_bus(iommu_bus->parent_dev); ... if (!pci_bus_is_express(iommu_bus)) { PCIDevice *parent = iommu_bus->parent_dev; if (pci_is_express(parent) && pcie_cap_get_type(parent) == PCI_EXP_TYPE_PCI_BRIDGE) { devfn = PCI_DEVFN(0, 0); bus = iommu_bus; } else { devfn = parent->devfn; bus = parent_bus; } } iommu_bus = parent_bus; } if (!pci_bus_bypass_iommu(bus) && iommu_bus && iommu_bus->iommu_fn) { return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, devfn); } return &address_space_memory; }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19// hw/pci-host/designware.c static void designware_pcie_host_realize(DeviceState *dev, Error **errp) { ... memory_region_init(&s->pci.address_space_root, OBJECT(s), "pcie-bus-address-space-root", UINT64_MAX); memory_region_add_subregion(&s->pci.address_space_root, 0x0, &s->pci.memory); address_space_init(&s->pci.address_space, &s->pci.address_space_root, "pcie-bus-address-space"); pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal); }- PCI device
PCI device’s DMA:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20// hw/pci/pci.h /** * pci_dma_read: Read from an address space from PCI device. * * Return a MemTxResult indicating whether the operation succeeded * or failed (eg unassigned memory, device rejected the transaction, * IOMMU fault). Called within RCU critical section. * * @dev: #PCIDevice doing the memory access * @addr: address within the #PCIDevice address space * @buf: buffer with the data transferred * @len: length of the data transferred */ static inline MemTxResult pci_dma_read(PCIDevice *dev, dma_addr_t addr, void *buf, dma_addr_t len) { return pci_dma_rw(dev, addr, buf, len, DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22// hw/pci/pci.h /** * pci_dma_rw: Read from or write to an address space from PCI device. * * Return a MemTxResult indicating whether the operation succeeded * or failed (eg unassigned memory, device rejected the transaction, * IOMMU fault). * * @dev: #PCIDevice doing the memory access * @addr: address within the #PCIDevice address space * @buf: buffer with the data transferred * @len: the number of bytes to read or write * @dir: indicates the transfer direction */ static inline MemTxResult pci_dma_rw(PCIDevice *dev, dma_addr_t addr, void *buf, dma_addr_t len, DMADirection dir, MemTxAttrs attrs) { return dma_memory_rw(pci_get_address_space(dev), addr, buf, len, dir, attrs); }