The virt machine creates its VirtIO MMIO transports one at a time.
Each transport maps its register region while the board is still
being initialized, publishing intermediate memory topology updates.

Enclose the transport creation loop in a memory region transaction
so that its mappings are published together at commit. Keep each
transport's creation, address and interrupt connection unchanged.
The transaction is committed before initializing the following board
devices and before any guest execution.

Add a QTest case that checks all eight transport register windows,
the attached RNG device and guest RAM access. Verify the transport
registers again after system reset. Exercise both legacy and modern
transports with one, two and four vCPUs.
---
 MAINTAINERS                        |  2 +
 hw/riscv/virt.c                    |  3 ++
 tests/qtest/meson.build            |  3 ++
 tests/qtest/riscv-virt-mmio-test.c | 65 ++++++++++++++++++++++++++++++
 4 files changed, 73 insertions(+)
 create mode 100644 tests/qtest/riscv-virt-mmio-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 70e9fed776..a6a2c027eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -387,6 +387,7 @@ F: tests/functional/riscv64
 F: tests/tcg/riscv32/
 F: tests/tcg/riscv64/
 F: tests/qtest/iommu-riscv-test.c
+F: tests/qtest/riscv-virt-mmio-test.c
 
 RISC-V XThead* extensions
 M: Christoph Muellner <[email protected]>
@@ -3755,6 +3756,7 @@ F: scripts/qom-cast-macro-clean-cocci-gen.py
 F: qom/
 F: stubs/qom-compat-properties.c
 F: tests/unit/check-qom-interface.c
+F: tests/unit/check-qom-enumeration.c
 F: tests/unit/check-qom-proplist.c
 F: tests/qtest/qom-test.c
 
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index de1c92463a..c4f06bcdcf 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -898,11 +898,14 @@ static void virt_machine_init(MachineState *machine)
     sifive_test_create(s->memmap[VIRT_TEST].base);
 
     /* VirtIO MMIO devices */
+    /* Publish the transport mappings together before any guest execution. */
+    memory_region_transaction_begin();
     for (i = 0; i < VIRTIO_COUNT; i++) {
         sysbus_create_simple("virtio-mmio",
             s->memmap[VIRT_VIRTIO].base + i * s->memmap[VIRT_VIRTIO].size,
             qdev_get_gpio_in(virtio_irqchip, VIRTIO_IRQ + i));
     }
+    memory_region_transaction_commit();
 
     riscv_gpex_pcie_init(system_memory, pcie_irqchip,
                    &s->memmap[VIRT_PCIE_ECAM], &s->memmap[VIRT_PCIE_MMIO],
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 46d4fc1d2b..2cc4fe71cd 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -313,6 +313,9 @@ qtests_riscv32 = \
   (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? 
['sifive-e-aon-watchdog-test'] : [])
 
 qtests_riscv64 = ['riscv-csr-test'] + \
+  (config_all_devices.has_key('CONFIG_RISCV_VIRT') and
+   config_all_devices.has_key('CONFIG_VIRTIO_RNG') ?
+   ['riscv-virt-mmio-test'] : []) + \
   (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
   (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
    config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
diff --git a/tests/qtest/riscv-virt-mmio-test.c 
b/tests/qtest/riscv-virt-mmio-test.c
new file mode 100644
index 0000000000..3c55b37f51
--- /dev/null
+++ b/tests/qtest/riscv-virt-mmio-test.c
@@ -0,0 +1,65 @@
+/*
+ * Check RISC-V virt MMIO transports before and after reset.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#define VIRTIO_BASE 0x10001000
+#define VIRTIO_STRIDE 0x1000
+#define VIRTIO_TRANSPORTS 8
+
+static void check_transports(QTestState *qts, bool modern)
+{
+    unsigned attached = 0;
+
+    for (unsigned i = 0; i < VIRTIO_TRANSPORTS; i++) {
+        uint64_t base = VIRTIO_BASE + i * VIRTIO_STRIDE;
+        uint32_t device = qtest_readl(qts, base + 8);
+
+        g_assert_cmphex(qtest_readl(qts, base), ==, 0x74726976);
+        g_assert_cmpuint(qtest_readl(qts, base + 4), ==, modern ? 2 : 1);
+        g_assert_cmpuint(qtest_readl(qts, base + 0x70), ==, 0);
+        if (device) {
+            g_assert_cmpuint(device, ==, 4);
+            attached++;
+        }
+    }
+    g_assert_cmpuint(attached, ==, 1);
+}
+
+static void test_transports(gconstpointer opaque)
+{
+    unsigned setting = GPOINTER_TO_UINT(opaque);
+    unsigned cpus = 1U << (setting / 2);
+    bool modern = setting % 2;
+    QTestState *qts = qtest_initf(
+        "-machine virt -bios none -m 64M -smp %u -nodefaults "
+        "-global virtio-mmio.force-legacy=%s -device virtio-rng-device",
+        cpus, modern ? "false" : "true");
+
+    check_transports(qts, modern);
+    qtest_writel(qts, 0x80001000, 0x12345678);
+    g_assert_cmphex(qtest_readl(qts, 0x80001000), ==, 0x12345678);
+    qtest_system_reset(qts);
+    check_transports(qts, modern);
+    qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    if (qtest_has_machine("virt")) {
+        for (unsigned setting = 0; setting < 6; setting++) {
+            g_autofree char *name = g_strdup_printf(
+                "/riscv/virt/mmio/%u-cpus/%s", 1U << (setting / 2),
+                setting % 2 ? "modern" : "legacy");
+
+            qtest_add_data_func(name, GUINT_TO_POINTER(setting),
+                                test_transports);
+        }
+    }
+    return g_test_run();
+}
-- 
2.47.0.windows.1


Reply via email to