From: Manivannan Sadhasivam <[email protected]> Wire the pcie-ep-ctrl controller into the ARM virt machine as a dynamic sysbus device on the platform bus.
-device pcie-ep-ctrl,target-bus=ID The platform bus assigns the MMIO windows, so the machine emits the device tree node (compatible "pci-ep-generic") with the resolved addresses. The node is also marked dma-coherent. This allows the Endpoint Controller driver to allocate coherent DMA memory from system RAM for the Endpoint Function BARs. Allow the device on the platform bus with machine_class_allow_dynamic_sysbus_dev(). Also register a no_fdt_node binding in hw/core/sysbus-fdt.c so the generic platform bus code leaves the node to the machine. The target-bus property names the PCIe Root Port the Endpoint Function is hotplugged onto when the Link comes up. A single guest kernel then drives the controller through the pci-ep-generic EPC driver. In the same kernel, it enumerates the resulting Function as a PCI host. This allows running Linux EPF drivers such as pci-epf-test against the matching host driver without a second QEMU instance. Signed-off-by: Manivannan Sadhasivam <[email protected]> --- hw/arm/Kconfig | 1 + hw/arm/virt.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/core/sysbus-fdt.c | 3 +++ 3 files changed, 61 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index fb798ccbee..0c8266d403 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -36,6 +36,7 @@ config ARM_VIRT select VIRTIO_MEM_SUPPORTED select ACPI_CXL select ACPI_HMAT + select PCIE_EP_CTRL config CUBIEBOARD bool diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b090233893..25091d2639 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -94,6 +94,7 @@ #include "hw/core/cpu.h" #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_host.h" +#include "hw/misc/pcie-ep-ctrl.h" #include "qemu/guest-random.h" static GlobalProperty arm_virt_compat_defaults[] = { @@ -1569,6 +1570,57 @@ static void create_rtc(const VirtMachineState *vms) g_free(nodename); } +/* + * Emit the device tree node for a pcie-ep-ctrl instantiated on the platform + * bus and hand the device the absolute addresses the bus assigned to its + * regions. Called from the plug handler once platform_bus_link_device() has + * placed the MMIO regions, mirroring how the SMMUv3 device builds its own + * node in create_smmuv3_dev_dtb(). + */ +static void create_pcie_ep_ctrl_dtb(VirtMachineState *vms, DeviceState *dev) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); + PCIeEPCtrlState *ec = PCIE_EP_CTRL(dev); + MachineState *ms = MACHINE(vms); + hwaddr pbus_base = vms->memmap[VIRT_PLATFORM_BUS].base; + hwaddr ctrl_base, ob_base; + hwaddr ctrl_size, ob_size; + char *nodename; + static const char compat[] = "pci-ep-generic"; + static const char reg_names[] = "ctrl\0outbound"; + + /* Absolute base of each region as the platform bus mapped it. */ + ctrl_base = pbus_base + platform_bus_get_mmio_addr(pbus, sbdev, 0); + ob_base = pbus_base + platform_bus_get_mmio_addr(pbus, sbdev, 1); + + ctrl_size = memory_region_size(sysbus_mmio_get_region(sbdev, 0)); + ob_size = memory_region_size(sysbus_mmio_get_region(sbdev, 1)); + + /* + * The EP guest programs outbound window addresses using these absolute CPU + * addresses, so the device must know where the platform bus placed the + * region to translate them back into window offsets. + */ + ec->ob_base = ob_base; + + nodename = g_strdup_printf("/pcie-ep@%" PRIx64, ctrl_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop(ms->fdt, nodename, "compatible", + compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, ctrl_base, 2, ctrl_size, + 2, ob_base, 2, ob_size); + qemu_fdt_setprop(ms->fdt, nodename, "reg-names", + reg_names, sizeof(reg_names)); + /* + * Mark the controller DMA-coherent so the EPC driver's dma_alloc_coherent + * returns cacheable coherent RAM for the Endpoint BAR backing buffers. + */ + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + g_free(nodename); +} + static DeviceState *gpio_key_dev; static void virt_powerdown_req(Notifier *n, void *opaque) { @@ -3843,6 +3895,10 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, if (device_is_dynamic_sysbus(mc, dev)) { platform_bus_link_device(PLATFORM_BUS_DEVICE(vms->platform_bus_dev), SYS_BUS_DEVICE(dev)); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCIE_EP_CTRL)) { + create_pcie_ep_ctrl_dtb(vms, dev); + } } } @@ -4091,6 +4147,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PCIE_EP_CTRL); mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index 89d0c46445..60586cc9b2 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -36,6 +36,7 @@ #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" +#include "hw/misc/pcie-ep-ctrl.h" /* * internal struct that contains the information to create dynamic @@ -140,6 +141,8 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node), TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), + /* Node emitted by the arm virt machine (create_pcie_ep_ctrl_dtb) */ + TYPE_BINDING(TYPE_PCIE_EP_CTRL, no_fdt_node), TYPE_BINDING("", NULL), /* last element */ }; -- 2.43.0
