On 5/6/25 7:47 AM, Markus Armbruster wrote:
Shameer Kolothum via <qemu-devel@nongnu.org> writes:

Although this change does not affect functionality at present, it lays
the groundwork for enabling user-created SMMUv3 devices in
future patches

Signed-off-by: Shameer Kolothum <shameerali.kolothum.th...@huawei.com>
---
  hw/arm/smmuv3.c | 26 ++++++++++++++++++++++++++
  hw/arm/virt.c   |  3 ++-
  2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index ab67972353..605de9b721 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -24,6 +24,7 @@
  #include "hw/qdev-properties.h"
  #include "hw/qdev-core.h"
  #include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
  #include "cpu.h"
  #include "exec/target_page.h"
  #include "trace.h"
@@ -1874,6 +1875,25 @@ static void smmu_reset_exit(Object *obj, ResetType type)
      smmuv3_init_regs(s);
  }
+static int smmuv3_pcie_bus(Object *obj, void *opaque)
+{
+    DeviceState *d = opaque;
+    PCIBus *bus;
+
+    if (!object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
+        return 0;
+    }
+
+    bus = PCI_HOST_BRIDGE(obj)->bus;
+    if (d->parent_bus && !strcmp(bus->qbus.name, d->parent_bus->name)) {
+        object_property_set_link(OBJECT(d), "primary-bus", OBJECT(bus),
+                                 &error_abort);
+        /* Return non-zero as we got the bus and don't need further 
iteration.*/
+        return 1;
+    }
+    return 0;
+}
+
  static void smmu_realize(DeviceState *d, Error **errp)
  {
      SMMUState *sys = ARM_SMMU(d);
@@ -1882,6 +1902,10 @@ static void smmu_realize(DeviceState *d, Error **errp)
      SysBusDevice *dev = SYS_BUS_DEVICE(d);
      Error *local_err = NULL;
+ if (!object_property_get_link(OBJECT(d), "primary-bus", &error_abort)) {
+        object_child_foreach_recursive(object_get_root(), smmuv3_pcie_bus, d);
+    }
+
      c->parent_realize(d, &local_err);
      if (local_err) {
          error_propagate(errp, local_err);
@@ -1996,6 +2020,8 @@ static void smmuv3_class_init(ObjectClass *klass, const 
void *data)
      device_class_set_parent_realize(dc, smmu_realize,
                                      &c->parent_realize);
      device_class_set_props(dc, smmuv3_properties);
+    dc->hotpluggable = false;
+    dc->bus_type = TYPE_PCIE_BUS;

This is very, very wrong.

The function serves as .class_init() for QOM type "arm-smmuv3", defined
as:

    static const TypeInfo smmuv3_type_info = {
        .name          = TYPE_ARM_SMMUV3,
        .parent        = TYPE_ARM_SMMU,

Subtype of "arm-smmuv3".

        .instance_size = sizeof(SMMUv3State),
        .instance_init = smmuv3_instance_init,
        .class_size    = sizeof(SMMUv3Class),
        .class_init    = smmuv3_class_init,
    };


     static const TypeInfo smmu_base_info = {
         .name          = TYPE_ARM_SMMU,
         .parent        = TYPE_SYS_BUS_DEVICE,

Subtype of "sys-bus-device".

         .instance_size = sizeof(SMMUState),
         .class_data    = NULL,
         .class_size    = sizeof(SMMUBaseClass),
         .class_init    = smmu_base_class_init,
         .abstract      = true,
     };

Have a look at the instance struct:

    struct SMMUv3State {
        SMMUState     smmu_state;

Starts with the supertype's instance struct, as is proper.

        uint32_t features;
        [more ...]
    };

Here's the supertype's instance struct:

    struct SMMUState {
        /* <private> */
        SysBusDevice  dev;

Again, starts with the supertype's instance struct.

        const char *mrtypename;
        [more...]
    };

This is a sysbus device, not a PCI device.  Monkey-patching dc->bus_type
from TYPE_SYSTEM_BUS to TYPE_PCIE_BUS won't change that.  All it
accomplishes is making the qdev core believe it plugs into a PCIE bus.
This can only end in tears.

In fact, when I build with the entire series applied (so the device can
actually be used with -device), the result dies within seconds of my ad
hoc testing:

     $ qemu-system-aarch64 -nodefaults -S -display none -monitor stdio -M virt 
-device pxb-pcie,id=pcie.1,bus_nr=2 -device arm-smmuv3,bus=pcie.1
     QEMU 10.0.50 monitor - type 'help' for more information
     qemu-system-aarch64: -device arm-smmuv3,bus=pcie.1: warning: SMMUv3 device 
only supported with pcie.0 for DT
     (qemu) info qtree
     bus: main-system-bus
       type System
       dev: pxb-host, id ""
         x-config-reg-migration-enabled = true
         bypass-iommu = false
         bus: pcie.1
           type pxb-pcie-bus
           dev: arm-smmuv3, id ""
             gpio-out "sysbus-irq" 4
             stage = "nested"
             bus_num = 0 (0x0)
     Segmentation fault (core dumped)

Backtrace:

     #0  0x00005555557d8521 in pcibus_dev_print
         (mon=0x55555826d0e0, dev=0x5555590ad360, indent=8)
         at ../hw/pci/pci-hmp-cmds.c:140
     #1  0x0000555555eac0a0 in bus_print_dev
         (bus=<optimized out>, mon=<optimized out>, dev=0x5555590ad360, 
indent=8)
         at ../system/qdev-monitor.c:773
     #2  qdev_print (mon=<optimized out>, dev=<optimized out>, indent=8)
         at ../system/qdev-monitor.c:805
     #3  qbus_print
         (mon=mon@entry=0x55555826d0e0, bus=bus@entry=0x5555590ac4a0, indent=6,
         indent@entry=4, details=details@entry=true) at 
../system/qdev-monitor.c:821
     #4  0x0000555555eabd92 in qbus_print
         (mon=0x55555826d0e0, bus=<optimized out>, indent=2, details=true)
         at ../system/qdev-monitor.c:824
     #5  0x0000555555979789 in handle_hmp_command_exec
         (cmd=<optimized out>, mon=0x55555826d0e0, qdict=0x55555907d8e0)
         at ../monitor/hmp.c:1106
     #6  handle_hmp_command_exec
         (mon=0x55555826d0e0, cmd=0x55555769d220 <hmp_info_cmds+2560>, 
qdict=0x55555907d8e0) at ../monitor/hmp.c:1098
     #7  handle_hmp_command (mon=mon@entry=0x55555826d0e0, cmdline=<optimized 
out>,
         cmdline@entry=0x555558657490 "info qtree") at ../monitor/hmp.c:1158
     #8  0x000055555597983c in monitor_command_cb

Crash line is

     int class = pci_get_word(d->config + PCI_CLASS_DEVICE);

Debugger shows that d->config is invalid.  This is hardly surprising!

The qdev core is trying to print information on the "arm_smmuv3" device
here.  It's working with a DeviceState.  Your monkey-patching convinced
it it's a PCIDevice, so it duly calls pcibus_dev_print() to print PCI
device information.  pcibus_dev_print() casts the DeviceState * to
PCIDevice *.  This assumes the device's instance actually starts with
PCIDevice.  It actually starts with SysBusDevice.

Unsurprisingly, and "make check" also fails:

     >>> MESON_TEST_ITERATION=1 
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 
RUST_BACKTRACE=1 QTEST_QEMU_BINARY=./qemu-system-aarch64 QTEST_QEMU_IMG=./qemu-img 
G_TEST_DBUS_DAEMON=/work/armbru/qemu/tests/dbus-vmstate-daemon.sh 
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 
PYTHON=/work/armbru/qemu/bld-arm/pyvenv/bin/python3 
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 
MALLOC_PERTURB_=22 /work/armbru/qemu/bld-arm/tests/qtest/test-hmp --tap -k
     ――――――――――――――――――――――――――――――――――――― ✀  
―――――――――――――――――――――――――――――――――――――
     stderr:
     qemu-system-aarch64: ../hw/core/qdev.c:113: qdev_set_parent_bus: Assertion 
`dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type)' failed.
     Broken pipe
     ../tests/qtest/libqtest.c:208: kill_qemu() detected QEMU death from signal 
6 (Aborted) (core dumped)

and

     >>> MESON_TEST_ITERATION=1 
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 
RUST_BACKTRACE=1 QTEST_QEMU_BINARY=./qemu-system-aarch64 MALLOC_PERTURB_=86 
QTEST_QEMU_IMG=./qemu-img 
G_TEST_DBUS_DAEMON=/work/armbru/qemu/tests/dbus-vmstate-daemon.sh 
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 
PYTHON=/work/armbru/qemu/bld-arm/pyvenv/bin/python3 
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 
/work/armbru/qemu/bld-arm/tests/qtest/qom-test --tap -k
     ――――――――――――――――――――――――――――――――――――― ✀  
―――――――――――――――――――――――――――――――――――――
     stderr:
     qemu-system-aarch64: ../hw/core/qdev.c:113: qdev_set_parent_bus: Assertion 
`dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type)' failed.
     Broken pipe
     ../tests/qtest/libqtest.c:208: kill_qemu() detected QEMU death from signal 
6 (Aborted) (core dumped)

Please make sure "make check" passes before posting patches for review.
If you need help getting there, post them marked RFC and with the bad
test results right in the cover letter.

  }
static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 177f3dd22c..3bae4e374f 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -56,6 +56,7 @@
  #include "qemu/cutils.h"
  #include "qemu/error-report.h"
  #include "qemu/module.h"
+#include "hw/pci/pci_bus.h"
  #include "hw/pci-host/gpex.h"
  #include "hw/virtio/virtio-pci.h"
  #include "hw/core/sysbus-fdt.h"
@@ -1442,7 +1443,7 @@ static void create_smmu(const VirtMachineState *vms,
      }
      object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus),
                               &error_abort);
-    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+    qdev_realize_and_unref(dev, &bus->qbus, &error_fatal);
      sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
      for (i = 0; i < NUM_SMMU_IRQS; i++) {
          sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,

What are you trying to accomplish?

I *guess* you're trying to change the "arm-smmuv3" device to be a PCI
device.  Correct?

The only way to do that is making it a subtype of PCIDevice, i.e. change
the parent chain from

    arm-smmuv3 -> arm-smmu -> sys-bus-device -> device -> object

to something like

    arm-smmuv3 ->    ...   -> pci-device -> device -> object

Note you cannot have different subtypes of the same supertype (say
"arm-smmu") plug into different buses.  If you need a common device core
to plug into different buses, things get more complicated.  Here's how
the "serial-FOO" devices do it:

* "serial-mm", a subtype of "sys-bus-device", thus plugs into system bus
* "serial-isa", a subtype of "isa-device", thus plugs into ISA bus
* "serial-pci", a subtype of "pci-device", thus plugs into PCI bus

They all *contain* the core "serial" device, which is a subtype of
"device", and thus does not plug into any bus.  "Contain" is "has a",
not "subtype of".

[...]

The serial-FOO analogy doesn't quite work for iommu/smmu's, because one is 
plugging a 'serial device' INTO a FOO-bus.

In this series, an iommu/smmu needs to be placed -BETWEEN- a sysbus and a 
PCIe-tree,
or step-wise, plug an smmuv3 into a sysbus, and a pcie tree/domain/RC into an 
SMMUv3.

So, an smmu needs to be associated with a bus (tree), i.e., pcie.0, pcie.1...
One could model it as a PCIe device, attached at the pcie-RC ... but that's not 
how it's modelled in ARM hw.
SMMU's are discovered via ACPI tables.

That leaves us back to the 'how to associate an SMMUv3 to a PCIe tree(RC)',
and that leads me to the other discussion & format I saw btwn Eric & Shameer:
 -device arm-smmv3,id=smmuv3.3
 -device xxxx,smmuv3= smmuv3.3
where one tags a (PCIe) device to an smmuv3(id), which is needed to build the 
(proper) IORT for (pcie-)device<->SMMUv3 associativity in a multi-SMMUv3 
configuration.

We could keep the bus=pcie.X option for the -device arm-smmuv3 to indicate that 
all PCIe devices connected to the pcie.0 tree go through that smmuv3;
qdev would model/config as the smmuv3 is 'attached to pcie.0'... which it sorta 
is...  and I think the IORT build could associate all devices on pcie.0 to be 
associated
with the proper smmuv3.




Reply via email to