[RFC PATCH 07/23] tests/unit: add system bus mock

Octavian Purdila posted 23 patches 3 months, 2 weeks ago
There is a newer version of this series
[RFC PATCH 07/23] tests/unit: add system bus mock
Posted by Octavian Purdila 3 months, 2 weeks ago
From: Valentin Ghita <valentinghita@google.com>

From: Valentin Ghita <valentinghita@google.com>

Add a system bus mock and the necessary memory access functions to be
able to create unit tests for device models.

Signed-off-by: Valentin Ghita <valentinghita@google.com>
Signed-off-by: Octavian Purdila <tavip@google.com>
---
 tests/unit/sysbus-mock.c | 314 +++++++++++++++++++++++++++++++++++++++
 tests/unit/sysbus-mock.h |  82 ++++++++++
 2 files changed, 396 insertions(+)
 create mode 100644 tests/unit/sysbus-mock.c
 create mode 100644 tests/unit/sysbus-mock.h

diff --git a/tests/unit/sysbus-mock.c b/tests/unit/sysbus-mock.c
new file mode 100644
index 0000000000..c6c654eabc
--- /dev/null
+++ b/tests/unit/sysbus-mock.c
@@ -0,0 +1,314 @@
+/*
+ * System Bus Mock
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-core.h"
+
+#include "sysbus-mock.h"
+
+AddressSpace address_space_memory;
+
+/* Simulates guest memory space. */
+static uint8_t *guest_mem;
+static size_t guest_mem_size;
+
+static uint64_t memory_region_ram_device_read(void *opaque,
+                                              hwaddr addr, unsigned size)
+{
+    uint64_t data = (uint64_t)~0;
+    uint8_t *buf = opaque;
+
+    switch (size) {
+    case 1:
+        data = *(uint8_t *)(buf + addr);
+        break;
+    case 2:
+        data = *(uint16_t *)(buf + addr);
+        break;
+    case 4:
+        data = *(uint32_t *)(buf + addr);
+        break;
+    case 8:
+        data = *(uint64_t *)(buf + addr);
+        break;
+    }
+
+    return data;
+}
+
+static void memory_region_ram_device_write(void *opaque, hwaddr addr,
+                                           uint64_t data, unsigned size)
+{
+    uint8_t *buf = opaque;
+
+    switch (size) {
+    case 1:
+        *(uint8_t *)(buf + addr) = (uint8_t)data;
+        break;
+    case 2:
+        *(uint16_t *)(buf + addr) = (uint16_t)data;
+        break;
+    case 4:
+        *(uint32_t *)(buf + addr) = (uint32_t)data;
+        break;
+    case 8:
+        *(uint64_t *)(buf + addr) = data;
+        break;
+    }
+}
+
+static const MemoryRegionOps ram_device_mem_ops = {
+    .read = memory_region_ram_device_read,
+    .write = memory_region_ram_device_write,
+    .endianness = DEVICE_HOST_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+};
+
+void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, bool is_write)
+{
+    /* Mock implementation. Return a pointer inside the guest_mem buffer. */
+    g_assert(guest_mem != NULL);
+    g_assert(guest_mem_size <= addr + (size_t)plen);
+
+    return guest_mem + addr;
+}
+
+void cpu_physical_memory_unmap(void *buffer, hwaddr len,
+                               bool is_write, hwaddr access_len)
+{
+    /* Mock implementation. */
+}
+
+MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
+                                    MemTxAttrs attrs, void *buf, hwaddr len)
+{
+    /* Mock implementation */
+    g_assert(guest_mem != NULL);
+
+    if (guest_mem_size < addr + (size_t)len) {
+        return MEMTX_ERROR;
+    }
+
+    memcpy(buf, guest_mem + addr, len);
+
+    return MEMTX_OK;
+}
+
+MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
+                                MemTxAttrs attrs,
+                                const void *buf, hwaddr len)
+{
+    /* Mock implementation */
+    g_assert(guest_mem != NULL);
+
+    if (guest_mem_size < addr + (size_t)len) {
+        return MEMTX_ERROR;
+    }
+
+    memcpy(guest_mem + addr, buf, len);
+
+    return MEMTX_OK;
+}
+
+MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
+                             void *buf, hwaddr len, bool is_write)
+{
+    if (is_write) {
+        return address_space_write(as, addr, attrs, buf, len);
+    } else {
+        return address_space_read_full(as, addr, attrs, buf, len);
+    }
+}
+
+void cpu_physical_memory_rw(hwaddr addr, void *buf,
+                            hwaddr len, bool is_write)
+{
+    address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED,
+                     buf, len, is_write);
+}
+
+void memory_region_init_io(MemoryRegion *mr, Object *owner,
+                           const MemoryRegionOps *ops, void *opaque,
+                           const char *name, uint64_t size)
+{
+    /* Mock implementation. */
+    mr->size = size;
+    mr->ops = ops;
+    mr->opaque = opaque;
+}
+
+void memory_region_init_ram_device_ptr(MemoryRegion *mr, Object *owner,
+                                       const char *name, uint64_t size,
+                                       void *ptr)
+{
+    mr->size = size;
+    mr->ops = &ram_device_mem_ops;
+    mr->opaque = ptr;
+}
+
+void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
+{
+    if (mr->readonly != readonly) {
+        mr->readonly = readonly;
+    }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+{
+    assert(n >= 0 && n < dev->num_mmio);
+    dev->mmio[n].addr = addr;
+    dev->mmio[n].memory->addr = addr;
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory)
+{
+    /* Mock implementation. */
+    assert(dev->num_mmio < QDEV_MAX_MMIO);
+    int n = dev->num_mmio++;
+    dev->mmio[n].addr = -1;
+    dev->mmio[n].memory = memory;
+}
+
+static void sysbus_device_class_init(ObjectClass *klass, void *data)
+{
+    /* Mock implementation. */
+}
+
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+    qdev_init_gpio_out_named(DEVICE(dev), p, SYSBUS_DEVICE_GPIO_IRQ, 1);
+}
+
+/*
+ * Mock implementation of the sysbus device class.
+ * Including the sysbus source code is difficult because of the dependencies,
+ * so it is easier to define the type here.
+ */
+static const TypeInfo sysbus_device_type_info = {
+    .name = TYPE_SYS_BUS_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .abstract = true,
+    .class_size = sizeof(SysBusDeviceClass),
+    .class_init = sysbus_device_class_init,
+};
+
+void sysbus_mock_init(void)
+{
+    type_register_static(&sysbus_device_type_info);
+}
+
+/* Find the mmio region containing an address. */
+static MemoryRegion *find_region(SysBusDevice *dev, hwaddr addr)
+{
+    int i;
+
+    for (i = 0; i < dev->num_mmio; i++) {
+        if (dev->mmio[i].addr <= addr &&
+            (addr - dev->mmio[i].addr) < dev->mmio[i].memory->size) {
+
+            return dev->mmio[i].memory;
+        }
+    }
+
+    return NULL;
+}
+
+uint32_t sysbus_mmio_read_addr(DeviceState *dev, hwaddr addr, unsigned size)
+{
+    uint64_t value;
+    MemTxResult result;
+    MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+    assert(mem != NULL);
+    assert(mem->ops->read_with_attrs != NULL || mem->ops->read != NULL);
+
+    if (mem->ops->read_with_attrs != NULL) {
+        result = mem->ops->read_with_attrs(mem->opaque, addr - mem->addr,
+                                           &value, size,
+                                           MEMTXATTRS_UNSPECIFIED);
+        assert(result == MEMTX_OK);
+    } else {
+        value = mem->ops->read(mem->opaque, addr - mem->addr, size);
+    }
+
+    return (uint32_t)value;
+}
+
+void sysbus_mmio_write_addr(DeviceState *dev, hwaddr addr, uint64_t value,
+                            unsigned size)
+{
+    MemTxResult result;
+    MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+    assert(mem != NULL);
+    assert(mem->ops->write_with_attrs != NULL || mem->ops->write != NULL);
+    assert(!mem->readonly);
+
+    if (mem->ops->write_with_attrs != NULL) {
+        result = mem->ops->write_with_attrs(mem->opaque, addr - mem->addr,
+                                            value, size,
+                                            MEMTXATTRS_UNSPECIFIED);
+        g_assert(result == MEMTX_OK);
+    } else {
+        mem->ops->write(mem->opaque, addr - mem->addr, value, size);
+    }
+}
+
+void sysbus_dev_set_guest_mem(void *mem, size_t size)
+{
+    guest_mem = mem;
+    guest_mem_size = size;
+}
+
+MemTxResult sysbus_mmio_read_addr_raw(DeviceState *dev, hwaddr addr,
+                                      uint64_t *value, unsigned size)
+{
+    uint64_t tmp;
+    MemTxResult result;
+    MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+    assert(mem != NULL);
+
+    result = mem->ops->read_with_attrs(dev, addr - mem->addr, &tmp,
+                                       size,
+                                       MEMTXATTRS_UNSPECIFIED);
+    *value = tmp;
+    return result;
+}
+
+MemTxResult sysbus_mmio_write_addr_raw(DeviceState *dev, hwaddr addr,
+                                       uint64_t value, unsigned size)
+{
+    MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+    assert(mem != NULL);
+    assert(!mem->readonly);
+
+    return mem->ops->write_with_attrs(dev, addr - mem->addr, value,
+                                      size,
+                                      MEMTXATTRS_UNSPECIFIED);
+}
diff --git a/tests/unit/sysbus-mock.h b/tests/unit/sysbus-mock.h
new file mode 100644
index 0000000000..7a4c2e7b9a
--- /dev/null
+++ b/tests/unit/sysbus-mock.h
@@ -0,0 +1,82 @@
+/*
+ * System Bus Mock
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef SYSBUS_MOCK_H
+#define SYSBUS_MOCK_H
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/sysbus.h"
+
+/*
+ * sysbus_mock_init
+ *
+ * Initialize the sysbus mock implementation.
+ */
+void sysbus_mock_init(void);
+
+/*
+ * sysbus_mmio_read_addr
+ * @dev: device structure
+ * @addr: address to read from
+ *
+ * Read from an address in a mmio region and assert on errors.
+ */
+uint32_t sysbus_mmio_read_addr(DeviceState *dev, hwaddr addr, unsigned size);
+
+/*
+ * sysbus_mmio_write_addr
+ * @dev: device structure
+ * @addr: address to write to
+ * @value: value to write
+ *
+ * Write to an address in a mmio region and assert on errors.
+ */
+void sysbus_mmio_write_addr(DeviceState *dev, hwaddr addr, uint64_t value,
+                            unsigned size);
+
+/*
+ * sysbus_dev_set_guest_mem
+ *
+ * Set guest generic memory space.
+ */
+void sysbus_dev_set_guest_mem(void *mem, size_t size);
+
+
+/*
+ * sysbus_mmio_read_addr_raw
+ * @dev: device structure
+ * @addr: address to write to
+ * @size: access size
+ *
+ * Read from an address in a mmio region and return errors.
+ *
+ * Returns: MEMTX_OK if the access was successful, MEMTX_ERROR otherwise
+ */
+MemTxResult sysbus_mmio_read_addr_raw(DeviceState *dev, hwaddr addr,
+                                      uint64_t *value, unsigned size);
+
+/*
+ * sysbus_mmio_write_addr_raw
+ * @dev: device structure
+ * @addr: address to write to
+ * @value: value to write
+ * @size: access size
+ *
+ * Write to an address in a mmio region and return errors.
+ *
+ * Returns: MEMTX_OK if the access was successful, MEMTX_ERROR otherwise
+ */
+MemTxResult sysbus_mmio_write_addr_raw(DeviceState *dev, hwaddr addr,
+                                       uint64_t value, unsigned size);
+
+#endif /* SYSBUS_MOCK_H */
-- 
2.46.0.rc2.264.g509ed76dc8-goog