diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e046fff5dd8ab0de7778f299f4c0feca88088f52..b2091406849961e161cf6d6b216a2e600fc2c91b 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -87,6 +87,13 @@
#include "hw/char/pl011.h"
#include "qemu/guest-random.h"
#include "qapi/qmp/qdict.h"
+#include "qemu/log.h"
+#ifdef CONFIG_UB
+#include "hw/ub/ub.h"
+#include "hw/ub/ub_ubc.h"
+#include "hw/ub/hisi/ubc.h"
+#include "hw/ub/hisi/ub_fm.h"
+#endif // CONFIG_UB
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -1714,7 +1721,17 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
0x0, vms->iommu_phandle, 0x0, bdf,
bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf);
}
+#ifdef CONFIG_UB
+static void create_ub(VirtMachineState *vms)
+{
+ DeviceState *ubc;
+ ubc = qdev_new(TYPE_BUS_CONTROLLER);
+ qdev_prop_set_uint32(ubc, "ub-bus-controller-msgq-reg-size", UBC_MSGQ_REG_SIZE);
+ qdev_prop_set_uint32(ubc, "ub-bus-controller-fm-msgq-reg-size", FM_MSGQ_REG_SIZE);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ubc), &error_fatal);
+}
+#endif // CONFIG_UB
static void create_pcie(VirtMachineState *vms)
{
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
@@ -2874,6 +2891,9 @@ static void machvirt_init(MachineState *machine)
create_rtc(vms);
create_pcie(vms);
+#ifdef CONFIG_UB
+ create_ub(vms);
+#endif // CONFIG_UB
if (!has_ged) {
create_gpio_devices(vms, VIRT_GPIO, sysmem);
diff --git a/hw/ub/meson.build b/hw/ub/meson.build
index 21c3f0ea6c208b2c473af9746c698cedf681194a..b6d5f4beffcab8640fdf52cafdb98c3f847c7230 100644
--- a/hw/ub/meson.build
+++ b/hw/ub/meson.build
@@ -1,5 +1,6 @@
ub_ss = ss.source_set()
ub_ss.add(files(
+ 'ub_ubc.c',
))
system_ss.add_all(when: 'CONFIG_HW_UB', if_true: ub_ss)
subdir('hisi')
diff --git a/hw/ub/ub_ubc.c b/hw/ub/ub_ubc.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0bc907ec8011cc2aaaf04f160e595542f9c5b7d
--- /dev/null
+++ b/hw/ub/ub_ubc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ */
+#include
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/arm/virt.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/ub/ub.h"
+#include "hw/ub/ub_ubc.h"
+#include "migration/vmstate.h"
+
+static uint64_t ub_msgq_reg_read(void *opaque, hwaddr addr, unsigned len)
+{
+ BusControllerState *s = opaque;
+ uint64_t val;
+
+ switch (len) {
+ case BYTE_SIZE:
+ val = ub_get_byte(s->msgq_reg + addr);
+ break;
+ case WORD_SIZE:
+ val = ub_get_word(s->msgq_reg + addr);
+ break;
+ case DWORD_SIZE:
+ val = ub_get_long(s->msgq_reg + addr);
+ break;
+ default:
+ qemu_log("invalid argument len 0x%x\n", len);
+ val = ~0x0;
+ break;
+ }
+
+ return val;
+}
+
+static void ub_msgq_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned len)
+{
+ BusControllerState *s = opaque;
+
+ switch (len) {
+ case BYTE_SIZE:
+ ub_set_byte(s->msgq_reg + addr, val);
+ break;
+ case WORD_SIZE:
+ ub_set_word(s->msgq_reg + addr, val);
+ break;
+ case DWORD_SIZE:
+ ub_set_long(s->msgq_reg + addr, val);
+ break;
+ default:
+ /* As length is under guest control, handle illegal values. */
+ qemu_log("invalid argument len 0x%x val 0x%lx\n", len, val);
+ return;
+ }
+}
+
+static const MemoryRegionOps ub_msgq_reg_ops = {
+ .read = ub_msgq_reg_read,
+ .write = ub_msgq_reg_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ub_fm_msgq_reg_ops = {
+
+};
+
+static void ub_reg_alloc(DeviceState *dev)
+{
+ BusControllerState *s = BUS_CONTROLLER(dev);
+
+ s->msgq_reg = g_malloc0(s->msgq_reg_size);
+ s->fm_msgq_reg = g_malloc0(s->fm_msgq_reg_size);
+ qemu_log("alloc ub reg mem size: msgq_reg %u, "
+ "fm_msgq_reg %u\n",
+ s->msgq_reg_size, s->fm_msgq_reg_size);
+}
+
+static void ub_reg_free(DeviceState *dev)
+{
+ BusControllerState *s = BUS_CONTROLLER(dev);
+
+ g_free(s->msgq_reg);
+ g_free(s->fm_msgq_reg);
+ qemu_log("free ub reg mem\n");
+}
+
+static void ub_bus_controller_realize(DeviceState *dev, Error **errp)
+{
+ BusControllerState *s = BUS_CONTROLLER(dev);
+ SysBusDevice *sysdev = SYS_BUS_DEVICE(dev);
+ static uint8_t NO = 0;
+ char *name = g_strdup_printf("ubus.%u", NO);
+
+ sysdev->parent_obj.id = g_strdup_printf("ubc.%u", NO++);
+ /* for msgq reg */
+ memory_region_init_io(&s->msgq_reg_mem, OBJECT(s), &ub_msgq_reg_ops,
+ s, TYPE_BUS_CONTROLLER, s->msgq_reg_size);
+ sysbus_init_mmio(sysdev, &s->msgq_reg_mem);
+ /* for fm msgq reg */
+ memory_region_init_io(&s->fm_msgq_reg_mem, OBJECT(s), &ub_fm_msgq_reg_ops,
+ s, TYPE_BUS_CONTROLLER, s->fm_msgq_reg_size);
+ sysbus_init_mmio(sysdev, &s->fm_msgq_reg_mem);
+ ub_reg_alloc(dev);
+ /* for ub controller mmio */
+ memory_region_init(&s->io_mmio, OBJECT(s), "UB_MMIO", UINT64_MAX);
+ sysbus_init_mmio(sysdev, &s->io_mmio);
+
+ g_free(name);
+}
+
+static void ub_bus_controller_unrealize(DeviceState *dev)
+{
+ BusControllerState *s = BUS_CONTROLLER(dev);
+ SysBusDevice *sysdev = SYS_BUS_DEVICE(dev);
+ g_free(sysdev->parent_obj.id);
+ QLIST_REMOVE(s, node);
+ ub_reg_free(dev);
+}
+
+static bool ub_bus_controller_needed(void *opaque)
+{
+ BusControllerState *s = opaque;
+ return s->mig_enabled;
+}
+
+static Property ub_bus_controller_properties[] = {
+ DEFINE_PROP_UINT32("ub-bus-controller-msgq-reg-size", BusControllerState,
+ msgq_reg_size, 0),
+ DEFINE_PROP_UINT32("ub-bus-controller-fm-msgq-reg-size", BusControllerState,
+ fm_msgq_reg_size, 0),
+ DEFINE_PROP_BOOL("ub-bus-controller-migration-enabled", BusControllerState,
+ mig_enabled, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+const VMStateDescription vmstate_ub_bus_controller = {
+ .name = TYPE_BUS_CONTROLLER,
+ .needed = ub_bus_controller_needed,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ /* support migration later */
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ub_bus_controller_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ device_class_set_props(dc, ub_bus_controller_properties);
+ dc->realize = ub_bus_controller_realize;
+ dc->unrealize = ub_bus_controller_unrealize;
+ dc->vmsd = &vmstate_ub_bus_controller;
+}
+
+static void ub_bus_controller_instance_init(Object *obj)
+{
+ /* do nothing now */
+}
+
+static void ub_bus_controller_instance_finalize(Object *obj)
+{
+ /* do nothing now */
+}
+static const TypeInfo ub_bus_controller_type_info = {
+ .name = TYPE_BUS_CONTROLLER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BusControllerState),
+ .instance_init = ub_bus_controller_instance_init,
+ .instance_finalize = ub_bus_controller_instance_finalize,
+ .class_size = sizeof(BusControllerClass),
+ .class_init = ub_bus_controller_class_init,
+};
+
+static void ub_bus_controller_register_types(void)
+{
+ type_register_static(&ub_bus_controller_type_info);
+}
+type_init(ub_bus_controller_register_types)
diff --git a/include/hw/ub/hisi/ub_fm.h b/include/hw/ub/hisi/ub_fm.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd606227a6e099417660de1dc99ebd9428f2be96
--- /dev/null
+++ b/include/hw/ub/hisi/ub_fm.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ */
+
+#ifndef UB_HISI_FM_H
+#define UB_HISI_FM_H
+#include "hw/ub/hisi/ubc.h"
+
+#define FM_MSGQ_REG_OFFSET (UBC_MSGQ_REG_OFFSET + UBC_MSGQ_REG_SIZE)
+#define FM_MSGQ_REG_SIZE 0x100000 /* 1MiB */
+
+#endif
diff --git a/include/hw/ub/hisi/ubc.h b/include/hw/ub/hisi/ubc.h
new file mode 100644
index 0000000000000000000000000000000000000000..fdaeae7b3e2ecc43b5b9300fbfe316cfa7d60284
--- /dev/null
+++ b/include/hw/ub/hisi/ubc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ */
+
+#ifndef HISI_UBC_H
+#define HISI_UBC_H
+
+/*
+ * Address space layout of the UB controller
+ * References: LinQuickCV100 Programming User Guide
+ *
+ * +----------------------------+ BA_ADDR+0xFFFF_FFFF
+ * | UMMU REG (3G) |
+ * +----------------------------+ BA_ADDR+0x4000_0000
+ * | ... |
+ * +----------------------------+
+ * | UB MSGQ(32M) only 2MB used |
+ * +----------------------------+ BA_ADDR+0x1000_0000
+ * | Local Register (256M) |
+ * +----------------------------+ BA_ADDR+0x0000_0000
+*/
+#define BASE_REG_SIZE 0x100000000 /* 4GiB */
+#define LOCAL_REG_SIZE 0x10000000 /* 256MiB */
+#define UBC_MSGQ_REG_SIZE 0x100000 /* 1MiB */
+#define UMMU_REG_SIZE 0xC0000000 /* 3GiB */
+#define UMMU_REG_OFFSET 0x40000000
+#define UBC_MSGQ_REG_OFFSET LOCAL_REG_SIZE
+#define LOCAL_REG_OFFSET 0
+#define SINGLE_UMMU_REG_SIZE 0x5000 /* 20KiB */
+#define SINGLE_UMMU_PMU_REG_SIZE 0x1000 /* 4KiB */
+#define UBC_INTERRUPT_ID_START 0x1FFF
+#define UBC_INTERRUPT_ID_CNT 0x1000
+#define VENDER_ID_HUAWEI 0xCC08
+
+#endif
diff --git a/include/hw/ub/ub.h b/include/hw/ub/ub.h
new file mode 100644
index 0000000000000000000000000000000000000000..4e3ed8a919a3c84b198b0ab259b6d66058bfc83c
--- /dev/null
+++ b/include/hw/ub/ub.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ */
+
+#ifndef UB_H
+#define UB_H
+#include
+#include "qemu/typedefs.h"
+#include "exec/memory.h"
+
+#define BYTE_SIZE 1
+#define WORD_SIZE 2
+#define DWORD_SIZE 4
+
+static inline void ub_set_byte(uint8_t *config, uint8_t val)
+{
+ *config = val;
+}
+
+static inline uint8_t ub_get_byte(const uint8_t *config)
+{
+ return *config;
+}
+
+static inline void ub_set_word(uint8_t *config, uint16_t val)
+{
+ stw_le_p(config, val);
+}
+
+static inline uint16_t ub_get_word(const uint8_t *config)
+{
+ return lduw_le_p(config);
+}
+
+static inline void ub_set_long(uint8_t *config, uint32_t val)
+{
+ stl_le_p(config, val);
+}
+
+static inline uint32_t ub_get_long(const uint8_t *config)
+{
+ return ldl_le_p(config);
+}
+
+static inline void ub_set_quad(uint8_t *config, uint64_t val)
+{
+ stq_le_p(config, val);
+}
+
+static inline uint64_t ub_get_quad(const uint8_t *config)
+{
+ return ldq_le_p(config);
+}
+
+#endif
diff --git a/include/hw/ub/ub_ubc.h b/include/hw/ub/ub_ubc.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e791fbcf310b151825e21136ec9f19eb6ece527
--- /dev/null
+++ b/include/hw/ub/ub_ubc.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ */
+
+#ifndef UB_UBC_H
+#define UB_UBC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_BUS_CONTROLLER "ub-bus-controller"
+OBJECT_DECLARE_TYPE(BusControllerState, BusControllerClass, BUS_CONTROLLER)
+
+typedef struct BusControllerState BusControllerState;
+struct BusControllerState {
+ SysBusDevice busdev;
+
+ MemoryRegion msgq_reg_mem; /* ubc msgq */
+ uint32_t msgq_reg_size;
+ uint8_t *msgq_reg;
+ MemoryRegion fm_msgq_reg_mem; /* fm msgq */
+ uint32_t fm_msgq_reg_size;
+ uint8_t *fm_msgq_reg;
+ MemoryRegion io_mmio; /* ub mmio hpa memory region */
+ uint32_t mmio_size;
+ bool mig_enabled;
+ QLIST_ENTRY(BusControllerState) node;
+};
+
+struct BusControllerClass {
+ SysBusDeviceClass parent_class;
+};
+
+#endif