diff mbox series

[RFC,5/9] hw/misc: Add MMIO test device

Message ID 20200817161853.593247-6-f4bug@amsat.org
State New
Headers show
Series [RFC,1/9] memory: Initialize MemoryRegionOps for RAM memory regions | expand

Commit Message

Philippe Mathieu-Daudé Aug. 17, 2020, 4:18 p.m. UTC
Add a MMIO test device handy to test QEMU internal devices via MMIO
accesses.

This device is meant to be run by the 'none' machine, thus no CPU
is required.

So far it is only useful to test the interleaver device.
A SRAM region is split into 256B subregions, and these subregions
are mapped at different addresses in an interleaved setup.
All the following (INPUT x OUTPUT) configurations can be tested:
16x8, 32x8, 32x16, 64x8, 64x16 and 64x32.

Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 include/hw/misc/testdev.h |  15 ++++
 hw/misc/mmio-testdev.c    | 146 ++++++++++++++++++++++++++++++++++++++
 MAINTAINERS               |   2 +
 hw/misc/Kconfig           |   5 ++
 hw/misc/Makefile.objs     |   1 +
 5 files changed, 169 insertions(+)
 create mode 100644 include/hw/misc/testdev.h
 create mode 100644 hw/misc/mmio-testdev.c
diff mbox series

Patch

diff --git a/include/hw/misc/testdev.h b/include/hw/misc/testdev.h
new file mode 100644
index 0000000000..2ff47d2766
--- /dev/null
+++ b/include/hw/misc/testdev.h
@@ -0,0 +1,15 @@ 
+/*
+ * QEMU MMIO test device
+ *
+ * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_TESTDEV_H
+#define HW_MISC_TESTDEV_H
+
+#define TYPE_MMIO_TESTDEV "mmio-testdev"
+
+#endif
+
diff --git a/hw/misc/mmio-testdev.c b/hw/misc/mmio-testdev.c
new file mode 100644
index 0000000000..3b7a8057b2
--- /dev/null
+++ b/hw/misc/mmio-testdev.c
@@ -0,0 +1,146 @@ 
+/*
+ * QEMU MMIO test device
+ *
+ * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This device is mostly used to test QEMU internal MMIO devices.
+ * Accesses using CPU core are not allowed.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+#include "hw/misc/testdev.h"
+#include "hw/misc/interleaver.h"
+
+/*
+ * Device Memory Map:
+ *
+ *   offset       size         description
+ * ----------  ----------  --------------------
+ * 0x00000000  [   2 KiB]  SRAM (8 banks of 256B)
+ * 0x10000000  [ 128 MiB]  interleaved-container
+ * 0x11608000  [   4 KiB]  interleaved-16x8  (each device interleaves the sram)
+ * 0x13208000  [   8 KiB]  interleaved-32x8   "
+ * 0x13216000  [   4 KiB]  interleaved-32x16  "
+ * 0x16408000  [  16 KiB]  interleaved-64x8   "
+ * 0x16416000  [   8 KiB]  interleaved-64x16  "
+ * 0x16432000  [   4 KiB]  interleaved-64x32  "
+ * 0x20000000  [ 256 MiB]  container
+ *
+ * All gap regions are reserved.
+ */
+
+typedef struct MmioTestDevice {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion container;
+    MemoryRegion sram;
+    MemoryRegion sram_alias[8];
+    MemoryRegion interleaver_container;
+    MemoryRegion iomem;
+
+    uint64_t base;
+} MmioTestDevice;
+
+#define TESTDEV(obj) \
+     OBJECT_CHECK(MmioTestDevice, (obj), TYPE_MMIO_TESTDEV)
+
+static void mmio_testdev_realize(DeviceState *dev, Error **errp)
+{
+    static const unsigned bhexs[] = {
+        [8] = 0x8, [16] = 0x16, [32] = 0x32, [64] = 0x64,
+    };
+    static const struct {
+        unsigned in, out;
+        const char *typename;
+    } cfg[] = {
+        {16, 8,  TYPE_INTERLEAVER_16X8_DEVICE},
+        {32, 8,  TYPE_INTERLEAVER_32X8_DEVICE},
+        {32, 16, TYPE_INTERLEAVER_32X16_DEVICE},
+        {64, 8,  TYPE_INTERLEAVER_64X8_DEVICE},
+        {64, 16, TYPE_INTERLEAVER_64X16_DEVICE},
+        {64, 32, TYPE_INTERLEAVER_64X32_DEVICE},
+    };
+    MmioTestDevice *s = TESTDEV(dev);
+    DeviceState *interleaver;
+
+    if (s->base == UINT64_MAX) {
+        error_setg(errp, "property 'address' not specified or zero");
+        return;
+    }
+
+    memory_region_init(&s->container, OBJECT(s), "testdev", 0x20000000);
+
+    memory_region_init_ram(&s->sram, OBJECT(s), "testdev-sram",
+                           0x800, &error_fatal);
+    memory_region_add_subregion(&s->container, 0x000000, &s->sram);
+
+    /* interleaved memory */
+    memory_region_init(&s->interleaver_container, OBJECT(s),
+                       "interleaver-container", 0x8000000);
+    memory_region_add_subregion(&s->container, 0x10000000,
+                                &s->interleaver_container);
+    for (unsigned i = 0; i < 8; i++) {
+        g_autofree char *name = g_strdup_printf("sram-p%u", i);
+        /* Each alias access a 256B region of the SRAM */
+        memory_region_init_alias(&s->sram_alias[i], OBJECT(s), name,
+                                 &s->sram, i * 0x100, 0x100);
+    }
+    for (size_t i = 0; i < ARRAY_SIZE(cfg); i++) {
+        unsigned count = cfg[i].in / cfg[i].out;
+
+        interleaver = qdev_new(cfg[i].typename);
+        qdev_prop_set_uint64(interleaver, "size", count * 0x100);
+        /* Map 256B SRAM regions on interleaver banks */
+        for (unsigned c = 0; c < count; c++) {
+            g_autofree char *prop_name = g_strdup_printf("mr%u", c);
+            object_property_set_link(OBJECT(interleaver), prop_name,
+                                     OBJECT(&s->sram_alias[c]), &error_abort);
+        }
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(interleaver), &error_fatal);
+        memory_region_add_subregion(&s->interleaver_container,
+                (bhexs[cfg[i].in] << 20) | (bhexs[cfg[i].out] << 12),
+                sysbus_mmio_get_region(SYS_BUS_DEVICE(interleaver), 0));
+    }
+
+    memory_region_add_subregion(get_system_memory(), s->base, &s->container);
+}
+
+static Property mmio_testdev_properties[] = {
+    DEFINE_PROP_UINT64("address", MmioTestDevice, base, UINT64_MAX),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mmio_testdev_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = mmio_testdev_realize;
+    dc->user_creatable = true;
+    device_class_set_props(dc, mmio_testdev_properties);
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo mmio_testdev_info = {
+    .name           = TYPE_MMIO_TESTDEV,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(MmioTestDevice),
+    .class_init     = mmio_testdev_class_init,
+};
+
+static void mmio_testdev_register_types(void)
+{
+    type_register_static(&mmio_testdev_info);
+}
+
+type_init(mmio_testdev_register_types)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1efce3dd27..f75b8c984a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1966,6 +1966,8 @@  M: Philippe Mathieu-Daudé <f4bug@amsat.org>
 S: Maintained
 F: include/hw/misc/interleaver.h
 F: hw/misc/interleaver.c
+F: hw/misc/mmio-testdev.c
+F: include/hw/misc/testdev.h
 
 Standard VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 7ed0f4ccc7..5b101abeea 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -30,6 +30,11 @@  config ISA_TESTDEV
     default y if TEST_DEVICES
     depends on ISA_BUS
 
+config MMIO_TESTDEV
+    bool
+    default y if TEST_DEVICES
+    depends on INTERLEAVER
+
 config PCI_TESTDEV
     bool
     default y if TEST_DEVICES
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index aa753a847f..b3e7da7177 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -5,6 +5,7 @@  common-obj-$(CONFIG_TMP421) += tmp421.o
 common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o
 common-obj-$(CONFIG_SGA) += sga.o
 common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
+common-obj-$(CONFIG_MMIO_TESTDEV) += mmio-testdev.o
 common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
 common-obj-$(CONFIG_EDU) += edu.o
 common-obj-$(CONFIG_PCA9552) += pca9552.o