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