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


Reply via email to