Commit 01e9a18730 for qemu.org

commit 01e9a18730e6f56f713ed074603a8b0f2982ed26
Author: Shameer Kolothum <skolothumtho@nvidia.com>
Date:   Fri Aug 29 09:25:25 2025 +0100

    hw/arm/virt-acpi-build: Update IORT for multiple smmuv3 devices

    With the soon to be introduced user-creatable SMMUv3 devices for
    virt, it is possible to have multiple SMMUv3 devices associated
    with different PCIe root complexes.

    Update IORT nodes accordingly.

    An example IORT Id mappings for a Qemu virt machine with two
    PCIe Root Complexes each assocaited with a SMMUv3 will
    be something like below,

      -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0
      -device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1
      ...

      +--------------------+           +--------------------+
      |   Root Complex 0   |           |   Root Complex 1   |
      |                    |           |                    |
      |  Requestor IDs     |           |  Requestor IDs     |
      |  0x0000 - 0x00FF   |           |  0x0100 - 0x01FF   |
      +---------+----------+           +---------+----------+
                |                               |
                |                               |
                |       Stream ID Mapping       |
                v                               v
      +--------------------+          +--------------------+
      |    SMMUv3 Node 0   |          |    SMMUv3 Node 1   |
      |                    |          |                    |
      | Stream IDs 0x0000- |          | Stream IDs 0x0100- |
      | 0x00FF mapped from |          | 0x01FF mapped from |
      | RC0 Requestor IDs  |          | RC1 Requestor IDs  |
      +--------------------+          +--------------------+
                |                                |
                |                                |
                +----------------+---------------+
                                 |
                                 |Device ID Mapping
                                 v
                  +----------------------------+
                  |       ITS Node 0           |
                  |                            |
                  | Device IDs:                |
                  | 0x0000 - 0x00FF (from RC0) |
                  | 0x0100 - 0x01FF (from RC1) |
                  | 0x0200 - 0xFFFF (No SMMU)  |
                  +----------------------------+

    Tested-by: Nathan Chen <nathanc@nvidia.com>
    Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
    Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
    Reviewed-by: Eric Auger <eric.auger@redhat.com>
    Tested-by: Eric Auger <eric.auger@redhat.com>
    Tested-by: Nicolin Chen <nicolinc@nvidia.com>
    Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
    Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
    Reviewed-by: Donald Dutile <ddutile@redhat.com>
    Message-id: 20250829082543.7680-4-skolothumtho@nvidia.com
    Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index bef4fabe56..96830f7c4e 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -45,6 +45,7 @@
 #include "hw/acpi/generic_event_device.h"
 #include "hw/acpi/tpm.h"
 #include "hw/acpi/hmat.h"
+#include "hw/arm/smmuv3.h"
 #include "hw/cxl/cxl.h"
 #include "hw/pci/pcie_host.h"
 #include "hw/pci/pci.h"
@@ -338,6 +339,67 @@ static int populate_smmuv3_legacy_dev(GArray *sdev_blob)
     return sdev.rc_smmu_idmaps->len;
 }

+static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b)
+{
+    AcpiIortSMMUv3Dev *sdev_a = (AcpiIortSMMUv3Dev *)a;
+    AcpiIortSMMUv3Dev *sdev_b = (AcpiIortSMMUv3Dev *)b;
+    AcpiIortIdMapping *map_a = &g_array_index(sdev_a->rc_smmu_idmaps,
+                                              AcpiIortIdMapping, 0);
+    AcpiIortIdMapping *map_b = &g_array_index(sdev_b->rc_smmu_idmaps,
+                                              AcpiIortIdMapping, 0);
+    return map_a->input_base - map_b->input_base;
+}
+
+static int iort_smmuv3_devices(Object *obj, void *opaque)
+{
+    VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
+    GArray *sdev_blob = opaque;
+    AcpiIortIdMapping idmap;
+    PlatformBusDevice *pbus;
+    AcpiIortSMMUv3Dev sdev;
+    int min_bus, max_bus;
+    SysBusDevice *sbdev;
+    PCIBus *bus;
+
+    if (!object_dynamic_cast(obj, TYPE_ARM_SMMUV3)) {
+        return 0;
+    }
+
+    bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort));
+    pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
+    sbdev = SYS_BUS_DEVICE(obj);
+    sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+    sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base;
+    sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0);
+    sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS];
+    sdev.irq += ARM_SPI_BASE;
+
+    pci_bus_range(bus, &min_bus, &max_bus);
+    sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
+    idmap.input_base = min_bus << 8,
+    idmap.id_count = (max_bus - min_bus + 1) << 8,
+    g_array_append_val(sdev.rc_smmu_idmaps, idmap);
+    g_array_append_val(sdev_blob, sdev);
+    return 0;
+}
+
+/*
+ * Populate the struct AcpiIortSMMUv3Dev for all SMMUv3 devices and
+ * return the total number of idmaps.
+ */
+static int populate_smmuv3_dev(GArray *sdev_blob)
+{
+    object_child_foreach_recursive(object_get_root(),
+                                   iort_smmuv3_devices, sdev_blob);
+    /* Sort the smmuv3 devices(if any) by smmu idmap input_base */
+    g_array_sort(sdev_blob, smmuv3_dev_idmap_compare);
+    /*
+     * Since each SMMUv3 dev is assocaited with specific host bridge,
+     * total number of idmaps equals to total number of smmuv3 devices.
+     */
+    return sdev_blob->len;
+}
+
 /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */
 static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs)
 {
@@ -401,6 +463,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)

     if (vms->legacy_smmuv3_present) {
         rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs);
+    } else {
+        rc_smmu_idmaps_len = populate_smmuv3_dev(smmuv3_devs);
     }

     num_smmus = smmuv3_devs->len;