[PATCH v4 8/9] hw/hexagon: Define hexagon "virt" machine

Brian Cain posted 9 patches 3 days, 14 hours ago
[PATCH v4 8/9] hw/hexagon: Define hexagon "virt" machine
Posted by Brian Cain 3 days, 14 hours ago
From: Brian Cain <bcain@quicinc.com>

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 configs/devices/hexagon-softmmu/default.mak |   1 +
 configs/targets/hexagon-softmmu.mak         |   1 +
 include/hw/hexagon/virt.h                   |  43 ++
 hw/hexagon/virt.c                           | 456 ++++++++++++++++++++
 target/hexagon/cpu.c                        |   2 +
 hw/hexagon/Kconfig                          |  10 +
 hw/hexagon/meson.build                      |   2 +
 tests/qemu-iotests/testenv.py               |   1 +
 8 files changed, 516 insertions(+)
 create mode 100644 include/hw/hexagon/virt.h
 create mode 100644 hw/hexagon/virt.c

diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak
index 08e709aea72..37b4f9f3237 100644
--- a/configs/devices/hexagon-softmmu/default.mak
+++ b/configs/devices/hexagon-softmmu/default.mak
@@ -3,5 +3,6 @@
 # Uncomment the following lines to disable these optional devices:
 
 # Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_HEX_VIRT=y
 # CONFIG_HEX_DSP=y
 # CONFIG_L2VIC=y
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak
index fdfa29b4f39..a77c100f0c5 100644
--- a/configs/targets/hexagon-softmmu.mak
+++ b/configs/targets/hexagon-softmmu.mak
@@ -5,3 +5,4 @@ TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml
 TARGET_LONG_BITS=32
 TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
 TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
+TARGET_NEED_FDT=y
diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h
new file mode 100644
index 00000000000..a54eac5cf00
--- /dev/null
+++ b/include/hw/hexagon/virt.h
@@ -0,0 +1,43 @@
+/*
+ * Definitions for hexagon virt board.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_HEXAGONVIRT_H
+#define HW_HEXAGONVIRT_H
+
+#include "hw/core/boards.h"
+#include "target/hexagon/cpu.h"
+
+struct HexagonVirtMachineState {
+    /*< private >*/
+    MachineState parent_obj;
+
+    int fdt_size;
+    MemoryRegion *sys;
+    MemoryRegion cfgtable;
+    MemoryRegion ram;
+    MemoryRegion tcm;
+    MemoryRegion vtcm;
+    MemoryRegion bios;
+    DeviceState *l2vic;
+    Clock *apb_clk;
+};
+
+void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
+
+enum {
+    VIRT_UART0,
+    VIRT_QTMR0,
+    VIRT_QTMR1,
+    VIRT_GPT,
+    VIRT_MMIO,
+    VIRT_FDT,
+};
+
+#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
+OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE)
+
+#endif /* HW_HEXAGONVIRT_H */
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
new file mode 100644
index 00000000000..49ee1aad2fe
--- /dev/null
+++ b/hw/hexagon/virt.c
@@ -0,0 +1,456 @@
+/*
+ * Hexagon virt emulation
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hexagon/virt.h"
+#include "elf.h"
+#include "hw/char/pl011.h"
+#include "hw/core/clock.h"
+#include "hw/core/sysbus-fdt.h"
+#include "hw/hexagon/hexagon.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hw/hexagon/hexagon_tlb.h"
+#include "hw/core/loader.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-clock.h"
+#include "hw/core/register.h"
+#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
+#include "qemu/units.h"
+#include "elf.h"
+#include "machine_cfg_v68n_1024.h.inc"
+#include "system/address-spaces.h"
+#include "system/device_tree.h"
+#include "system/reset.h"
+#include "system/system.h"
+#include <libfdt.h>
+
+static const int VIRTIO_DEV_COUNT = 8;
+
+static const MemMapEntry base_memmap[] = {
+    [VIRT_UART0] = { 0x10000000, 0x00000200 },
+    [VIRT_MMIO] = { 0x11000000, 0x1000000, },
+    [VIRT_GPT] = { 0xab000000, 0x00001000 },
+    [VIRT_FDT] = { 0x99800000, 0x00400000 },
+};
+
+static const int irqmap[] = {
+    [VIRT_MMIO] = 18, /* ...to 18 + VIRTIO_DEV_COUNT - 1 */
+    [VIRT_GPT] = 12,
+    [VIRT_UART0] = 15,
+    [VIRT_QTMR0] = 2,
+    [VIRT_QTMR1] = 4,
+};
+
+
+static void create_fdt(HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    void *fdt = create_device_tree(&vms->fdt_size);
+    uint8_t rng_seed[32];
+
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    ms->fdt = fdt;
+
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
+    qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+    qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
+}
+
+static void fdt_add_hvx(HexagonVirtMachineState *vms,
+                        const struct hexagon_machine_config *m_cfg,
+                        Error **errp)
+{
+    const MachineState *ms = MACHINE(vms);
+    uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
+    if (vtcm_size_bytes > 0) {
+        memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes,
+                               errp);
+        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16,
+                                    &vms->vtcm);
+
+        qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
+        qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
+                                "qcom,hexagon_vtcm");
+
+        assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
+        qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
+                               m_cfg->cfgtable.vtcm_base << 16,
+                               vtcm_size_bytes);
+    }
+
+    if (m_cfg->cfgtable.ext_contexts > 0) {
+        qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
+        qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
+                                "qcom,hexagon-hvx");
+        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
+                               m_cfg->cfgtable.ext_contexts);
+        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
+                               m_cfg->cfgtable.hvx_vec_log_length);
+    }
+}
+
+static int32_t irq_hvm_ic_phandle = -1;
+static void fdt_add_hvm_pic_node(HexagonVirtMachineState *vms,
+                                 const struct hexagon_machine_config *m_cfg)
+{
+    MachineState *ms = MACHINE(vms);
+    irq_hvm_ic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+
+    qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent",
+                          irq_hvm_ic_phandle);
+
+    qemu_fdt_add_subnode(ms->fdt, "/soc/interrupt-controller");
+    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
+                          "#address-cells", 2);
+    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
+                          "#interrupt-cells", 2);
+    qemu_fdt_setprop_string(ms->fdt, "/soc/interrupt-controller", "compatible",
+                            "qcom,h2-pic,hvm-pic");
+    qemu_fdt_setprop(ms->fdt, "/soc/interrupt-controller",
+                     "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", "phandle",
+                          irq_hvm_ic_phandle);
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(vms->l2vic), 1,
+                    m_cfg->cfgtable.fastl2vic_base << 16);
+}
+
+
+static void fdt_add_gpt_node(HexagonVirtMachineState *vms)
+{
+    g_autofree char *name = NULL;
+    MachineState *ms = MACHINE(vms);
+
+    name = g_strdup_printf("/soc/gpt@%" PRIx64,
+                           (int64_t)base_memmap[VIRT_GPT].base);
+    qemu_fdt_add_subnode(ms->fdt, name);
+    qemu_fdt_setprop_string(ms->fdt, name, "compatible",
+                            "qcom,h2-timer,hvm-timer");
+    qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqmap[VIRT_GPT], 0);
+    qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0,
+                           base_memmap[VIRT_GPT].base,
+                           base_memmap[VIRT_GPT].size);
+}
+
+static int32_t clock_phandle = -1;
+static void fdt_add_clocks(const HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    clock_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+    qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
+    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock");
+    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
+    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000);
+    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
+                            "clk24mhz");
+    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clock_phandle);
+}
+
+static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart)
+{
+    char *nodename;
+    hwaddr base = base_memmap[uart].base;
+    hwaddr size = base_memmap[uart].size;
+    assert(uart == 0);
+    int irq = irqmap[VIRT_UART0 + uart];
+    const char compat[] = "arm,pl011\0arm,primecell";
+    const char clocknames[] = "uartclk\0apb_pclk";
+    MachineState *ms = MACHINE(vms);
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_new(TYPE_PL011);
+    s = SYS_BUS_DEVICE(dev);
+    qdev_prop_set_chr(dev, "chardev", serial_hd(0));
+    qdev_connect_clock_in(dev, "clk", vms->apb_clk);
+    sysbus_realize_and_unref(s, &error_fatal);
+    sysbus_mmio_map(s, 0, base);
+    if (vms->l2vic) {
+        sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->l2vic, irq));
+    }
+
+    nodename = g_strdup_printf("/pl011@%" PRIx64, base);
+    qemu_fdt_add_subnode(ms->fdt, nodename);
+
+    /* Note that we can't use setprop_string because of the embedded NUL */
+    qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
+    qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
+    if (vms->l2vic) {
+        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
+                               32 + irq, 0);
+        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+                              irq_hvm_ic_phandle);
+    }
+    qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clock_phandle,
+                           clock_phandle);
+    qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
+                     sizeof(clocknames));
+
+    qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+    qemu_fdt_add_subnode(ms->fdt, "/aliases");
+    qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
+
+    g_free(nodename);
+}
+
+static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    qemu_fdt_add_subnode(ms->fdt, "/cpus");
+    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
+    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
+
+    /* cpu nodes */
+    for (int num = ms->smp.cpus - 1; num >= 0; num--) {
+        char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
+        qemu_fdt_add_subnode(ms->fdt, nodename);
+        qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
+        qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
+        qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
+                              qemu_fdt_alloc_phandle(ms->fdt));
+        g_free(nodename);
+    }
+}
+
+
+static void fdt_add_virtio_devices(const HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    /* VirtIO MMIO devices */
+    for (int i = 0; i < VIRTIO_DEV_COUNT; i++) {
+        char *nodename;
+        int irq = irqmap[VIRT_MMIO] + i;
+        size_t size = base_memmap[VIRT_MMIO].size;
+        hwaddr base = base_memmap[VIRT_MMIO].base + i * size;
+
+        nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
+        qemu_fdt_add_subnode(ms->fdt, nodename);
+        qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "virtio,mmio");
+        qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 1,
+                                     size);
+        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0);
+        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+                              irq_hvm_ic_phandle);
+
+        sysbus_create_simple(
+            "virtio-mmio", base,
+            qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_MMIO] + i));
+
+        g_free(nodename);
+    }
+}
+
+static void virt_instance_init(Object *obj)
+{
+    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
+
+    create_fdt(vms);
+}
+
+void hexagon_load_fdt(const HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
+    uint32_t fdtsize = vms->fdt_size;
+
+    g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
+    /* copy in the device tree */
+    rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
+                          &address_space_memory);
+    qemu_register_reset_nosnapshotload(
+        qemu_fdt_randomize_seeds,
+        rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
+}
+
+static uint64_t load_kernel(const HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    uint64_t entry = 0;
+    if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL,
+                         NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory,
+                         false, NULL) > 0) {
+        return entry;
+    }
+    error_report("error loading '%s'", ms->kernel_filename);
+    exit(1);
+}
+
+static uint64_t load_bios(HexagonVirtMachineState *vms)
+{
+    MachineState *ms = MACHINE(vms);
+    uint64_t bios_addr = 0x0;  /* Load BIOS at reset vector address 0x0 */
+    int bios_size;
+
+    bios_size = load_image_targphys(ms->firmware ?: "",
+                                    bios_addr, 64 * 1024, NULL);
+    if (bios_size < 0) {
+        error_report("Could not load BIOS '%s'", ms->firmware ?: "");
+        exit(1);
+    }
+
+    return bios_addr;  /* Return entry point at address 0x0 */
+}
+
+static void do_cpu_reset(void *opaque)
+{
+    HexagonCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);
+    cpu_reset(cs);
+}
+
+static void virt_init(MachineState *ms)
+{
+    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
+    const struct hexagon_machine_config *m_cfg = &v68n_1024;
+    const char *cpu_model;
+    DeviceState *gsregs_dev;
+    DeviceState *tlb_dev;
+    HexagonCPU *cpu_0;
+
+    qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
+
+    vms->sys = get_system_memory();
+
+    /* Create APB clock for peripherals */
+    vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
+    clock_set_hz(vms->apb_clk, 24000000);
+
+    memory_region_init_ram(&vms->ram, NULL, "ddr.ram", ms->ram_size,
+                           &error_fatal);
+    memory_region_add_subregion(vms->sys, 0x0, &vms->ram);
+
+    if (m_cfg->l2tcm_size) {
+        memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size,
+                               &error_fatal);
+        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16,
+                                    &vms->tcm);
+    }
+
+    memory_region_init_rom(&vms->cfgtable, NULL, "config_table.rom",
+                           sizeof(m_cfg->cfgtable), &error_fatal);
+    memory_region_add_subregion(vms->sys, m_cfg->cfgbase, &vms->cfgtable);
+    fdt_add_hvx(vms, m_cfg, &error_fatal);
+    cpu_model = ms->cpu_type;
+
+    if (!cpu_model) {
+        cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
+    }
+
+    gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
+    object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev));
+    qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
+    qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal);
+
+    tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
+    object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev));
+    qdev_prop_set_uint32(tlb_dev, "num-entries",
+                         m_cfg->cfgtable.jtlb_size_entries);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal);
+
+    cpu_0 = NULL;
+    for (int i = 0; i < ms->smp.cpus; i++) {
+        HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
+        qemu_register_reset(do_cpu_reset, cpu);
+
+        if (i == 0) {
+            cpu_0 = cpu;
+            if (ms->kernel_filename) {
+                uint64_t entry = load_kernel(vms);
+                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
+            } else if (ms->firmware) {
+                uint64_t entry = load_bios(vms);
+                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
+            }
+        }
+        qdev_prop_set_uint32(DEVICE(cpu), "htid", i);
+        qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
+        object_property_set_link(OBJECT(cpu), "global-regs",
+                                 OBJECT(gsregs_dev), &error_fatal);
+        object_property_set_link(OBJECT(cpu), "tlb",
+                                 OBJECT(tlb_dev), &error_fatal);
+
+        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal)) {
+            return;
+        }
+    }
+    /* TODO: enable l2vic when l2vic device arrives */
+    if (object_class_by_name("l2vic")) {
+        vms->l2vic = sysbus_create_varargs(
+            "l2vic", m_cfg->l2vic_base,
+            qdev_get_gpio_in(DEVICE(cpu_0), 0),
+            qdev_get_gpio_in(DEVICE(cpu_0), 1),
+            qdev_get_gpio_in(DEVICE(cpu_0), 2),
+            qdev_get_gpio_in(DEVICE(cpu_0), 3),
+            qdev_get_gpio_in(DEVICE(cpu_0), 4),
+            qdev_get_gpio_in(DEVICE(cpu_0), 5),
+            qdev_get_gpio_in(DEVICE(cpu_0), 6),
+            qdev_get_gpio_in(DEVICE(cpu_0), 7), NULL);
+
+        fdt_add_hvm_pic_node(vms, m_cfg);
+        fdt_add_virtio_devices(vms);
+        fdt_add_gpt_node(vms);
+    }
+
+    fdt_add_cpu_nodes(vms);
+    fdt_add_clocks(vms);
+    fdt_add_uart(vms, VIRT_UART0);
+
+    rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
+                          sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
+                          &address_space_memory);
+
+
+    hexagon_load_fdt(vms);
+}
+
+
+static void virt_class_init(ObjectClass *oc, const void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "Hexagon Virtual Machine";
+    mc->init = virt_init;
+    mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73");
+    mc->default_ram_size = 4 * GiB;
+    mc->max_cpus = 8;
+    mc->default_cpus = 8;
+    mc->is_default = false;
+    mc->default_kernel_irqchip_split = false;
+    mc->block_default_type = IF_VIRTIO;
+    mc->default_boot_order = NULL;
+    mc->no_cdrom = 1;
+    mc->numa_mem_supported = false;
+    mc->default_nic = "virtio-mmio-bus";
+}
+
+
+static const TypeInfo virt_machine_types[] = { {
+    .name = TYPE_HEXAGON_VIRT_MACHINE,
+    .parent = TYPE_MACHINE,
+    .instance_size = sizeof(HexagonVirtMachineState),
+    .class_init = virt_class_init,
+    .instance_init = virt_instance_init,
+} };
+
+DEFINE_TYPES(virt_machine_types)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 355abb4fd24..01781a76caf 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -68,6 +68,8 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
 
 static const Property hexagon_cpu_properties[] = {
 #if !defined(CONFIG_USER_ONLY)
+    DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
+                     HexagonTLBState *),
     DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff),
     DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
         TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
index 7b9577f68f7..dc74751d21e 100644
--- a/hw/hexagon/Kconfig
+++ b/hw/hexagon/Kconfig
@@ -3,3 +3,13 @@ config HEX_DSP
     default y
     depends on HEXAGON && TCG
     imply PTIMER
+
+config HEX_VIRT
+    bool
+    default y
+    depends on HEX_DSP && FDT
+    select DEVICE_TREE
+    select VIRTIO_MMIO
+    select PL011
+    select VIRTIO_BLK
+    select VIRTIO_SCSI
diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build
index f528d2bc4ab..5b6a5e11a17 100644
--- a/hw/hexagon/meson.build
+++ b/hw/hexagon/meson.build
@@ -4,3 +4,5 @@ hexagon_ss.add(files('hexagon_globalreg.c'))
 hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c'))
 
 hw_arch += {'hexagon': hexagon_ss}
+
+hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c'))
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
index c357e6ebf50..86bcdf7cfad 100644
--- a/tests/qemu-iotests/testenv.py
+++ b/tests/qemu-iotests/testenv.py
@@ -259,6 +259,7 @@ def __init__(self, source_dir: str, build_dir: str,
             ('arm', 'virt'),
             ('aarch64', 'virt'),
             ('avr', 'mega2560'),
+            ('hexagon', 'virt'),
             ('m68k', 'virt'),
             ('or1k', 'virt'),
             ('riscv32', 'virt'),
-- 
2.34.1

Re: [PATCH v4 8/9] hw/hexagon: Define hexagon "virt" machine
Posted by Sid Manning 2 days, 23 hours ago
On Tue, Apr 7, 2026 at 11:22 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:

> From: Brian Cain <bcain@quicinc.com>
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>  configs/devices/hexagon-softmmu/default.mak |   1 +
>  configs/targets/hexagon-softmmu.mak         |   1 +
>  include/hw/hexagon/virt.h                   |  43 ++
>  hw/hexagon/virt.c                           | 456 ++++++++++++++++++++
>  target/hexagon/cpu.c                        |   2 +
>  hw/hexagon/Kconfig                          |  10 +
>  hw/hexagon/meson.build                      |   2 +
>  tests/qemu-iotests/testenv.py               |   1 +
>  8 files changed, 516 insertions(+)
>  create mode 100644 include/hw/hexagon/virt.h
>  create mode 100644 hw/hexagon/virt.c
>
> diff --git a/configs/devices/hexagon-softmmu/default.mak
> b/configs/devices/hexagon-softmmu/default.mak
> index 08e709aea72..37b4f9f3237 100644
> --- a/configs/devices/hexagon-softmmu/default.mak
> +++ b/configs/devices/hexagon-softmmu/default.mak
> @@ -3,5 +3,6 @@
>  # Uncomment the following lines to disable these optional devices:
>
>  # Boards are selected by default, uncomment to keep out of the build.
> +# CONFIG_HEX_VIRT=y
>  # CONFIG_HEX_DSP=y
>  # CONFIG_L2VIC=y
> diff --git a/configs/targets/hexagon-softmmu.mak
> b/configs/targets/hexagon-softmmu.mak
> index fdfa29b4f39..a77c100f0c5 100644
> --- a/configs/targets/hexagon-softmmu.mak
> +++ b/configs/targets/hexagon-softmmu.mak
> @@ -5,3 +5,4 @@ TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml
>  TARGET_LONG_BITS=32
>  TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
>  TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
> +TARGET_NEED_FDT=y
> diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h
> new file mode 100644
> index 00000000000..a54eac5cf00
> --- /dev/null
> +++ b/include/hw/hexagon/virt.h
> @@ -0,0 +1,43 @@
> +/*
> + * Definitions for hexagon virt board.
> + *
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_HEXAGONVIRT_H
> +#define HW_HEXAGONVIRT_H
> +
> +#include "hw/core/boards.h"
> +#include "target/hexagon/cpu.h"
> +
> +struct HexagonVirtMachineState {
> +    /*< private >*/
> +    MachineState parent_obj;
> +
> +    int fdt_size;
> +    MemoryRegion *sys;
> +    MemoryRegion cfgtable;
> +    MemoryRegion ram;
> +    MemoryRegion tcm;
> +    MemoryRegion vtcm;
> +    MemoryRegion bios;
> +    DeviceState *l2vic;
> +    Clock *apb_clk;
> +};
> +
> +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
> +
> +enum {
> +    VIRT_UART0,
> +    VIRT_QTMR0,
> +    VIRT_QTMR1,
> +    VIRT_GPT,
> +    VIRT_MMIO,
> +    VIRT_FDT,
> +};
> +
> +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
> +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE)
> +
> +#endif /* HW_HEXAGONVIRT_H */
> diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
> new file mode 100644
> index 00000000000..49ee1aad2fe
> --- /dev/null
> +++ b/hw/hexagon/virt.c
> @@ -0,0 +1,456 @@
> +/*
> + * Hexagon virt emulation
> + *
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/hexagon/virt.h"
> +#include "elf.h"
> +#include "hw/char/pl011.h"
> +#include "hw/core/clock.h"
> +#include "hw/core/sysbus-fdt.h"
> +#include "hw/hexagon/hexagon.h"
> +#include "hw/hexagon/hexagon_globalreg.h"
> +#include "hw/hexagon/hexagon_tlb.h"
> +#include "hw/core/loader.h"
> +#include "hw/core/qdev-properties.h"
> +#include "hw/core/qdev-clock.h"
> +#include "hw/core/register.h"
> +#include "qemu/error-report.h"
> +#include "qemu/guest-random.h"
> +#include "qemu/units.h"
> +#include "elf.h"
> +#include "machine_cfg_v68n_1024.h.inc"
> +#include "system/address-spaces.h"
> +#include "system/device_tree.h"
> +#include "system/reset.h"
> +#include "system/system.h"
> +#include <libfdt.h>
> +
> +static const int VIRTIO_DEV_COUNT = 8;
> +
> +static const MemMapEntry base_memmap[] = {
> +    [VIRT_UART0] = { 0x10000000, 0x00000200 },
> +    [VIRT_MMIO] = { 0x11000000, 0x1000000, },
> +    [VIRT_GPT] = { 0xab000000, 0x00001000 },
> +    [VIRT_FDT] = { 0x99800000, 0x00400000 },
> +};
> +
> +static const int irqmap[] = {
> +    [VIRT_MMIO] = 18, /* ...to 18 + VIRTIO_DEV_COUNT - 1 */
> +    [VIRT_GPT] = 12,
> +    [VIRT_UART0] = 15,
> +    [VIRT_QTMR0] = 2,
> +    [VIRT_QTMR1] = 4,
> +};
> +
> +
> +static void create_fdt(HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    void *fdt = create_device_tree(&vms->fdt_size);
> +    uint8_t rng_seed[32];
> +
> +    if (!fdt) {
> +        error_report("create_device_tree() failed");
> +        exit(1);
> +    }
> +
> +    ms->fdt = fdt;
> +
> +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
> +    qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
> +    qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
> +
> +    qemu_fdt_add_subnode(fdt, "/soc");
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
> +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> +
> +    qemu_fdt_add_subnode(fdt, "/chosen");
> +    qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
> +    qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed,
> sizeof(rng_seed));
> +}
> +
> +static void fdt_add_hvx(HexagonVirtMachineState *vms,
> +                        const struct hexagon_machine_config *m_cfg,
> +                        Error **errp)
> +{
> +    const MachineState *ms = MACHINE(vms);
> +    uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
> +    if (vtcm_size_bytes > 0) {
> +        memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram",
> vtcm_size_bytes,
> +                               errp);
> +        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base
> << 16,
> +                                    &vms->vtcm);
> +
> +        qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
> +        qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
> +                                "qcom,hexagon_vtcm");
> +
> +        assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
> +                               m_cfg->cfgtable.vtcm_base << 16,
> +                               vtcm_size_bytes);
> +    }
> +
> +    if (m_cfg->cfgtable.ext_contexts > 0) {
> +        qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
> +        qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
> +                                "qcom,hexagon-hvx");
> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
> +                               m_cfg->cfgtable.ext_contexts);
> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
> +                               m_cfg->cfgtable.hvx_vec_log_length);
> +    }
> +}
> +
> +static int32_t irq_hvm_ic_phandle = -1;
> +static void fdt_add_hvm_pic_node(HexagonVirtMachineState *vms,
> +                                 const struct hexagon_machine_config
> *m_cfg)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    irq_hvm_ic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
> +
> +    qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent",
> +                          irq_hvm_ic_phandle);
> +
> +    qemu_fdt_add_subnode(ms->fdt, "/soc/interrupt-controller");
> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
> +                          "#address-cells", 2);
> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
> +                          "#interrupt-cells", 2);
> +    qemu_fdt_setprop_string(ms->fdt, "/soc/interrupt-controller",
> "compatible",
> +                            "qcom,h2-pic,hvm-pic");
> +    qemu_fdt_setprop(ms->fdt, "/soc/interrupt-controller",
> +                     "interrupt-controller", NULL, 0);
> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", "phandle",
> +                          irq_hvm_ic_phandle);
> +
> +    sysbus_mmio_map(SYS_BUS_DEVICE(vms->l2vic), 1,
> +                    m_cfg->cfgtable.fastl2vic_base << 16);
> +}
> +
> +
> +static void fdt_add_gpt_node(HexagonVirtMachineState *vms)
> +{
> +    g_autofree char *name = NULL;
> +    MachineState *ms = MACHINE(vms);
> +
> +    name = g_strdup_printf("/soc/gpt@%" PRIx64,
> +                           (int64_t)base_memmap[VIRT_GPT].base);
> +    qemu_fdt_add_subnode(ms->fdt, name);
> +    qemu_fdt_setprop_string(ms->fdt, name, "compatible",
> +                            "qcom,h2-timer,hvm-timer");
> +    qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqmap[VIRT_GPT],
> 0);
> +    qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0,
> +                           base_memmap[VIRT_GPT].base,
> +                           base_memmap[VIRT_GPT].size);
> +}
> +
> +static int32_t clock_phandle = -1;
> +static void fdt_add_clocks(const HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    clock_phandle = qemu_fdt_alloc_phandle(ms->fdt);
> +    qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
> +    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible",
> "fixed-clock");
> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency",
> 24000000);
> +    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
> +                            "clk24mhz");
> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clock_phandle);
> +}
> +
> +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart)
> +{
> +    char *nodename;
> +    hwaddr base = base_memmap[uart].base;
> +    hwaddr size = base_memmap[uart].size;
> +    assert(uart == 0);
> +    int irq = irqmap[VIRT_UART0 + uart];
> +    const char compat[] = "arm,pl011\0arm,primecell";
> +    const char clocknames[] = "uartclk\0apb_pclk";
> +    MachineState *ms = MACHINE(vms);
> +    DeviceState *dev;
> +    SysBusDevice *s;
> +
> +    dev = qdev_new(TYPE_PL011);
> +    s = SYS_BUS_DEVICE(dev);
> +    qdev_prop_set_chr(dev, "chardev", serial_hd(0));
> +    qdev_connect_clock_in(dev, "clk", vms->apb_clk);
> +    sysbus_realize_and_unref(s, &error_fatal);
> +    sysbus_mmio_map(s, 0, base);
> +    if (vms->l2vic) {
> +        sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->l2vic, irq));
> +    }
> +
> +    nodename = g_strdup_printf("/pl011@%" PRIx64, base);
> +    qemu_fdt_add_subnode(ms->fdt, nodename);
> +
> +    /* Note that we can't use setprop_string because of the embedded NUL
> */
> +    qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat,
> sizeof(compat));
> +    qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
> +    if (vms->l2vic) {
> +        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
> +                               32 + irq, 0);
> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
> +                              irq_hvm_ic_phandle);
> +    }
> +    qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clock_phandle,
> +                           clock_phandle);
> +    qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
> +                     sizeof(clocknames));
> +
> +    qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
> +    qemu_fdt_add_subnode(ms->fdt, "/aliases");
> +    qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
> +
> +    g_free(nodename);
> +}
> +
> +static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    qemu_fdt_add_subnode(ms->fdt, "/cpus");
> +    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
> +    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
> +
> +    /* cpu nodes */
> +    for (int num = ms->smp.cpus - 1; num >= 0; num--) {
> +        char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
> +        qemu_fdt_add_subnode(ms->fdt, nodename);
> +        qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
> +                              qemu_fdt_alloc_phandle(ms->fdt));
> +        g_free(nodename);
> +    }
> +}
> +
> +
> +static void fdt_add_virtio_devices(const HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    /* VirtIO MMIO devices */
> +    for (int i = 0; i < VIRTIO_DEV_COUNT; i++) {
> +        char *nodename;
> +        int irq = irqmap[VIRT_MMIO] + i;
> +        size_t size = base_memmap[VIRT_MMIO].size;
> +        hwaddr base = base_memmap[VIRT_MMIO].base + i * size;
> +
> +        nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
> +        qemu_fdt_add_subnode(ms->fdt, nodename);
> +        qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
> "virtio,mmio");
> +        qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 1,
> +                                     size);
> +        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0);
> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
> +                              irq_hvm_ic_phandle);
> +
> +        sysbus_create_simple(
> +            "virtio-mmio", base,
> +            qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_MMIO] + i));
> +
> +        g_free(nodename);
> +    }
> +}
> +
> +static void virt_instance_init(Object *obj)
> +{
> +    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
> +
> +    create_fdt(vms);
> +}
> +
> +void hexagon_load_fdt(const HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
> +    uint32_t fdtsize = vms->fdt_size;
> +
> +    g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
> +    /* copy in the device tree */
> +    rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
> +                          &address_space_memory);
> +    qemu_register_reset_nosnapshotload(
> +        qemu_fdt_randomize_seeds,
> +        rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
> +}
> +
> +static uint64_t load_kernel(const HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    uint64_t entry = 0;
> +    if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry,
> NULL,
> +                         NULL, NULL, 0, EM_HEXAGON, 0, 0,
> &address_space_memory,
> +                         false, NULL) > 0) {
> +        return entry;
> +    }
> +    error_report("error loading '%s'", ms->kernel_filename);
> +    exit(1);
> +}
> +
> +static uint64_t load_bios(HexagonVirtMachineState *vms)
> +{
> +    MachineState *ms = MACHINE(vms);
> +    uint64_t bios_addr = 0x0;  /* Load BIOS at reset vector address 0x0 */
> +    int bios_size;
> +
> +    bios_size = load_image_targphys(ms->firmware ?: "",
> +                                    bios_addr, 64 * 1024, NULL);
> +    if (bios_size < 0) {
> +        error_report("Could not load BIOS '%s'", ms->firmware ?: "");
> +        exit(1);
> +    }
> +
> +    return bios_addr;  /* Return entry point at address 0x0 */
> +}
> +
> +static void do_cpu_reset(void *opaque)
> +{
> +    HexagonCPU *cpu = opaque;
> +    CPUState *cs = CPU(cpu);
> +    cpu_reset(cs);
> +}
> +
> +static void virt_init(MachineState *ms)
> +{
> +    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
> +    const struct hexagon_machine_config *m_cfg = &v68n_1024;
>
This is v68 but in virt_class_init  mc->default_cpu_type is set to v73,
should that be 68?


+    const char *cpu_model;
> +    DeviceState *gsregs_dev;
> +    DeviceState *tlb_dev;
> +    HexagonCPU *cpu_0;
> +
> +    qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs",
> ms->kernel_cmdline);
> +
> +    vms->sys = get_system_memory();
> +
> +    /* Create APB clock for peripherals */
> +    vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
> +    clock_set_hz(vms->apb_clk, 24000000);
> +
> +    memory_region_init_ram(&vms->ram, NULL, "ddr.ram", ms->ram_size,
> +                           &error_fatal);
> +    memory_region_add_subregion(vms->sys, 0x0, &vms->ram);
> +
> +    if (m_cfg->l2tcm_size) {
> +        memory_region_init_ram(&vms->tcm, NULL, "tcm.ram",
> m_cfg->l2tcm_size,
> +                               &error_fatal);
> +        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base
> << 16,
> +                                    &vms->tcm);
> +    }
> +
> +    memory_region_init_rom(&vms->cfgtable, NULL, "config_table.rom",
> +                           sizeof(m_cfg->cfgtable), &error_fatal);
> +    memory_region_add_subregion(vms->sys, m_cfg->cfgbase, &vms->cfgtable);
> +    fdt_add_hvx(vms, m_cfg, &error_fatal);
> +    cpu_model = ms->cpu_type;
> +
> +    if (!cpu_model) {
> +        cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
> +    }
> +
> +    gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
> +    object_property_add_child(OBJECT(ms), "global-regs",
> OBJECT(gsregs_dev));
> +    qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
> +    qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal);
> +
> +    tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
> +    object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev));
> +    qdev_prop_set_uint32(tlb_dev, "num-entries",
> +                         m_cfg->cfgtable.jtlb_size_entries);
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal);
> +
> +    cpu_0 = NULL;
> +    for (int i = 0; i < ms->smp.cpus; i++) {
> +        HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
> +        qemu_register_reset(do_cpu_reset, cpu);
> +
> +        if (i == 0) {
> +            cpu_0 = cpu;
> +            if (ms->kernel_filename) {
> +                uint64_t entry = load_kernel(vms);
> +                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr",
> entry);
> +            } else if (ms->firmware) {
> +                uint64_t entry = load_bios(vms);
> +                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr",
> entry);
> +            }
> +        }
> +        qdev_prop_set_uint32(DEVICE(cpu), "htid", i);
> +        qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
> +        object_property_set_link(OBJECT(cpu), "global-regs",
> +                                 OBJECT(gsregs_dev), &error_fatal);
> +        object_property_set_link(OBJECT(cpu), "tlb",
> +                                 OBJECT(tlb_dev), &error_fatal);
> +
> +        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal)) {
> +            return;
> +        }
> +    }
> +    /* TODO: enable l2vic when l2vic device arrives */
> +    if (object_class_by_name("l2vic")) {
> +        vms->l2vic = sysbus_create_varargs(
> +            "l2vic", m_cfg->l2vic_base,
> +            qdev_get_gpio_in(DEVICE(cpu_0), 0),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 1),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 2),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 3),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 4),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 5),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 6),
> +            qdev_get_gpio_in(DEVICE(cpu_0), 7), NULL);
> +
> +        fdt_add_hvm_pic_node(vms, m_cfg);
> +        fdt_add_virtio_devices(vms);
> +        fdt_add_gpt_node(vms);
> +    }
> +
> +    fdt_add_cpu_nodes(vms);
> +    fdt_add_clocks(vms);
> +    fdt_add_uart(vms, VIRT_UART0);
> +
> +    rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
> +                          sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
> +                          &address_space_memory);
> +
> +
> +    hexagon_load_fdt(vms);
> +}
> +
> +
> +static void virt_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "Hexagon Virtual Machine";
> +    mc->init = virt_init;
> +    mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73");
> +    mc->default_ram_size = 4 * GiB;
> +    mc->max_cpus = 8;
> +    mc->default_cpus = 8;
> +    mc->is_default = false;
> +    mc->default_kernel_irqchip_split = false;
> +    mc->block_default_type = IF_VIRTIO;
> +    mc->default_boot_order = NULL;
> +    mc->no_cdrom = 1;
> +    mc->numa_mem_supported = false;
> +    mc->default_nic = "virtio-mmio-bus";
> +}
> +
> +
> +static const TypeInfo virt_machine_types[] = { {
> +    .name = TYPE_HEXAGON_VIRT_MACHINE,
> +    .parent = TYPE_MACHINE,
> +    .instance_size = sizeof(HexagonVirtMachineState),
> +    .class_init = virt_class_init,
> +    .instance_init = virt_instance_init,
> +} };
> +
> +DEFINE_TYPES(virt_machine_types)
> diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
> index 355abb4fd24..01781a76caf 100644
> --- a/target/hexagon/cpu.c
> +++ b/target/hexagon/cpu.c
> @@ -68,6 +68,8 @@ static ObjectClass *hexagon_cpu_class_by_name(const char
> *cpu_model)
>
>  static const Property hexagon_cpu_properties[] = {
>  #if !defined(CONFIG_USER_ONLY)
> +    DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
> +                     HexagonTLBState *),
>      DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr,
> 0xffffffff),
>      DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
>          TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
> diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
> index 7b9577f68f7..dc74751d21e 100644
> --- a/hw/hexagon/Kconfig
> +++ b/hw/hexagon/Kconfig
> @@ -3,3 +3,13 @@ config HEX_DSP
>      default y
>      depends on HEXAGON && TCG
>      imply PTIMER
> +
> +config HEX_VIRT
> +    bool
> +    default y
> +    depends on HEX_DSP && FDT
> +    select DEVICE_TREE
> +    select VIRTIO_MMIO
> +    select PL011
> +    select VIRTIO_BLK
> +    select VIRTIO_SCSI
> diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build
> index f528d2bc4ab..5b6a5e11a17 100644
> --- a/hw/hexagon/meson.build
> +++ b/hw/hexagon/meson.build
> @@ -4,3 +4,5 @@ hexagon_ss.add(files('hexagon_globalreg.c'))
>  hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c'))
>
>  hw_arch += {'hexagon': hexagon_ss}
> +
> +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c'))
> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
> index c357e6ebf50..86bcdf7cfad 100644
> --- a/tests/qemu-iotests/testenv.py
> +++ b/tests/qemu-iotests/testenv.py
> @@ -259,6 +259,7 @@ def __init__(self, source_dir: str, build_dir: str,
>              ('arm', 'virt'),
>              ('aarch64', 'virt'),
>              ('avr', 'mega2560'),
> +            ('hexagon', 'virt'),
>              ('m68k', 'virt'),
>              ('or1k', 'virt'),
>              ('riscv32', 'virt'),
> --
> 2.34.1
>
>
Re: [PATCH v4 8/9] hw/hexagon: Define hexagon "virt" machine
Posted by Brian Cain 1 day, 22 hours ago
On Wed, Apr 8, 2026 at 2:22 PM Sid Manning <sid.manning@oss.qualcomm.com> wrote:
>
>
>
> On Tue, Apr 7, 2026 at 11:22 PM Brian Cain <brian.cain@oss.qualcomm.com> wrote:
>>
>> From: Brian Cain <bcain@quicinc.com>
>>
>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>> ---
>>  configs/devices/hexagon-softmmu/default.mak |   1 +
>>  configs/targets/hexagon-softmmu.mak         |   1 +
>>  include/hw/hexagon/virt.h                   |  43 ++
>>  hw/hexagon/virt.c                           | 456 ++++++++++++++++++++
>>  target/hexagon/cpu.c                        |   2 +
>>  hw/hexagon/Kconfig                          |  10 +
>>  hw/hexagon/meson.build                      |   2 +
>>  tests/qemu-iotests/testenv.py               |   1 +
>>  8 files changed, 516 insertions(+)
>>  create mode 100644 include/hw/hexagon/virt.h
>>  create mode 100644 hw/hexagon/virt.c
>>
>> diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak
>> index 08e709aea72..37b4f9f3237 100644
>> --- a/configs/devices/hexagon-softmmu/default.mak
>> +++ b/configs/devices/hexagon-softmmu/default.mak
>> @@ -3,5 +3,6 @@
>>  # Uncomment the following lines to disable these optional devices:
>>
>>  # Boards are selected by default, uncomment to keep out of the build.
>> +# CONFIG_HEX_VIRT=y
>>  # CONFIG_HEX_DSP=y
>>  # CONFIG_L2VIC=y
>> diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak
>> index fdfa29b4f39..a77c100f0c5 100644
>> --- a/configs/targets/hexagon-softmmu.mak
>> +++ b/configs/targets/hexagon-softmmu.mak
>> @@ -5,3 +5,4 @@ TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml
>>  TARGET_LONG_BITS=32
>>  TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
>>  TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
>> +TARGET_NEED_FDT=y
>> diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h
>> new file mode 100644
>> index 00000000000..a54eac5cf00
>> --- /dev/null
>> +++ b/include/hw/hexagon/virt.h
>> @@ -0,0 +1,43 @@
>> +/*
>> + * Definitions for hexagon virt board.
>> + *
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef HW_HEXAGONVIRT_H
>> +#define HW_HEXAGONVIRT_H
>> +
>> +#include "hw/core/boards.h"
>> +#include "target/hexagon/cpu.h"
>> +
>> +struct HexagonVirtMachineState {
>> +    /*< private >*/
>> +    MachineState parent_obj;
>> +
>> +    int fdt_size;
>> +    MemoryRegion *sys;
>> +    MemoryRegion cfgtable;
>> +    MemoryRegion ram;
>> +    MemoryRegion tcm;
>> +    MemoryRegion vtcm;
>> +    MemoryRegion bios;
>> +    DeviceState *l2vic;
>> +    Clock *apb_clk;
>> +};
>> +
>> +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
>> +
>> +enum {
>> +    VIRT_UART0,
>> +    VIRT_QTMR0,
>> +    VIRT_QTMR1,
>> +    VIRT_GPT,
>> +    VIRT_MMIO,
>> +    VIRT_FDT,
>> +};
>> +
>> +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
>> +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE)
>> +
>> +#endif /* HW_HEXAGONVIRT_H */
>> diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
>> new file mode 100644
>> index 00000000000..49ee1aad2fe
>> --- /dev/null
>> +++ b/hw/hexagon/virt.c
>> @@ -0,0 +1,456 @@
>> +/*
>> + * Hexagon virt emulation
>> + *
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "hw/hexagon/virt.h"
>> +#include "elf.h"
>> +#include "hw/char/pl011.h"
>> +#include "hw/core/clock.h"
>> +#include "hw/core/sysbus-fdt.h"
>> +#include "hw/hexagon/hexagon.h"
>> +#include "hw/hexagon/hexagon_globalreg.h"
>> +#include "hw/hexagon/hexagon_tlb.h"
>> +#include "hw/core/loader.h"
>> +#include "hw/core/qdev-properties.h"
>> +#include "hw/core/qdev-clock.h"
>> +#include "hw/core/register.h"
>> +#include "qemu/error-report.h"
>> +#include "qemu/guest-random.h"
>> +#include "qemu/units.h"
>> +#include "elf.h"
>> +#include "machine_cfg_v68n_1024.h.inc"
>> +#include "system/address-spaces.h"
>> +#include "system/device_tree.h"
>> +#include "system/reset.h"
>> +#include "system/system.h"
>> +#include <libfdt.h>
>> +
>> +static const int VIRTIO_DEV_COUNT = 8;
>> +
>> +static const MemMapEntry base_memmap[] = {
>> +    [VIRT_UART0] = { 0x10000000, 0x00000200 },
>> +    [VIRT_MMIO] = { 0x11000000, 0x1000000, },
>> +    [VIRT_GPT] = { 0xab000000, 0x00001000 },
>> +    [VIRT_FDT] = { 0x99800000, 0x00400000 },
>> +};
>> +
>> +static const int irqmap[] = {
>> +    [VIRT_MMIO] = 18, /* ...to 18 + VIRTIO_DEV_COUNT - 1 */
>> +    [VIRT_GPT] = 12,
>> +    [VIRT_UART0] = 15,
>> +    [VIRT_QTMR0] = 2,
>> +    [VIRT_QTMR1] = 4,
>> +};
>> +
>> +
>> +static void create_fdt(HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    void *fdt = create_device_tree(&vms->fdt_size);
>> +    uint8_t rng_seed[32];
>> +
>> +    if (!fdt) {
>> +        error_report("create_device_tree() failed");
>> +        exit(1);
>> +    }
>> +
>> +    ms->fdt = fdt;
>> +
>> +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
>> +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
>> +    qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
>> +    qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
>> +
>> +    qemu_fdt_add_subnode(fdt, "/soc");
>> +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
>> +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
>> +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
>> +
>> +    qemu_fdt_add_subnode(fdt, "/chosen");
>> +    qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
>> +    qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
>> +}
>> +
>> +static void fdt_add_hvx(HexagonVirtMachineState *vms,
>> +                        const struct hexagon_machine_config *m_cfg,
>> +                        Error **errp)
>> +{
>> +    const MachineState *ms = MACHINE(vms);
>> +    uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
>> +    if (vtcm_size_bytes > 0) {
>> +        memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes,
>> +                               errp);
>> +        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16,
>> +                                    &vms->vtcm);
>> +
>> +        qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
>> +        qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
>> +                                "qcom,hexagon_vtcm");
>> +
>> +        assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
>> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
>> +                               m_cfg->cfgtable.vtcm_base << 16,
>> +                               vtcm_size_bytes);
>> +    }
>> +
>> +    if (m_cfg->cfgtable.ext_contexts > 0) {
>> +        qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
>> +        qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
>> +                                "qcom,hexagon-hvx");
>> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
>> +                               m_cfg->cfgtable.ext_contexts);
>> +        qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
>> +                               m_cfg->cfgtable.hvx_vec_log_length);
>> +    }
>> +}
>> +
>> +static int32_t irq_hvm_ic_phandle = -1;
>> +static void fdt_add_hvm_pic_node(HexagonVirtMachineState *vms,
>> +                                 const struct hexagon_machine_config *m_cfg)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    irq_hvm_ic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
>> +
>> +    qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent",
>> +                          irq_hvm_ic_phandle);
>> +
>> +    qemu_fdt_add_subnode(ms->fdt, "/soc/interrupt-controller");
>> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
>> +                          "#address-cells", 2);
>> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller",
>> +                          "#interrupt-cells", 2);
>> +    qemu_fdt_setprop_string(ms->fdt, "/soc/interrupt-controller", "compatible",
>> +                            "qcom,h2-pic,hvm-pic");
>> +    qemu_fdt_setprop(ms->fdt, "/soc/interrupt-controller",
>> +                     "interrupt-controller", NULL, 0);
>> +    qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", "phandle",
>> +                          irq_hvm_ic_phandle);
>> +
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(vms->l2vic), 1,
>> +                    m_cfg->cfgtable.fastl2vic_base << 16);
>> +}
>> +
>> +
>> +static void fdt_add_gpt_node(HexagonVirtMachineState *vms)
>> +{
>> +    g_autofree char *name = NULL;
>> +    MachineState *ms = MACHINE(vms);
>> +
>> +    name = g_strdup_printf("/soc/gpt@%" PRIx64,
>> +                           (int64_t)base_memmap[VIRT_GPT].base);
>> +    qemu_fdt_add_subnode(ms->fdt, name);
>> +    qemu_fdt_setprop_string(ms->fdt, name, "compatible",
>> +                            "qcom,h2-timer,hvm-timer");
>> +    qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqmap[VIRT_GPT], 0);
>> +    qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0,
>> +                           base_memmap[VIRT_GPT].base,
>> +                           base_memmap[VIRT_GPT].size);
>> +}
>> +
>> +static int32_t clock_phandle = -1;
>> +static void fdt_add_clocks(const HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    clock_phandle = qemu_fdt_alloc_phandle(ms->fdt);
>> +    qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
>> +    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock");
>> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
>> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000);
>> +    qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
>> +                            "clk24mhz");
>> +    qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clock_phandle);
>> +}
>> +
>> +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart)
>> +{
>> +    char *nodename;
>> +    hwaddr base = base_memmap[uart].base;
>> +    hwaddr size = base_memmap[uart].size;
>> +    assert(uart == 0);
>> +    int irq = irqmap[VIRT_UART0 + uart];
>> +    const char compat[] = "arm,pl011\0arm,primecell";
>> +    const char clocknames[] = "uartclk\0apb_pclk";
>> +    MachineState *ms = MACHINE(vms);
>> +    DeviceState *dev;
>> +    SysBusDevice *s;
>> +
>> +    dev = qdev_new(TYPE_PL011);
>> +    s = SYS_BUS_DEVICE(dev);
>> +    qdev_prop_set_chr(dev, "chardev", serial_hd(0));
>> +    qdev_connect_clock_in(dev, "clk", vms->apb_clk);
>> +    sysbus_realize_and_unref(s, &error_fatal);
>> +    sysbus_mmio_map(s, 0, base);
>> +    if (vms->l2vic) {
>> +        sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->l2vic, irq));
>> +    }
>> +
>> +    nodename = g_strdup_printf("/pl011@%" PRIx64, base);
>> +    qemu_fdt_add_subnode(ms->fdt, nodename);
>> +
>> +    /* Note that we can't use setprop_string because of the embedded NUL */
>> +    qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
>> +    qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
>> +    if (vms->l2vic) {
>> +        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
>> +                               32 + irq, 0);
>> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
>> +                              irq_hvm_ic_phandle);
>> +    }
>> +    qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clock_phandle,
>> +                           clock_phandle);
>> +    qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
>> +                     sizeof(clocknames));
>> +
>> +    qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
>> +    qemu_fdt_add_subnode(ms->fdt, "/aliases");
>> +    qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
>> +
>> +    g_free(nodename);
>> +}
>> +
>> +static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    qemu_fdt_add_subnode(ms->fdt, "/cpus");
>> +    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
>> +    qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
>> +
>> +    /* cpu nodes */
>> +    for (int num = ms->smp.cpus - 1; num >= 0; num--) {
>> +        char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
>> +        qemu_fdt_add_subnode(ms->fdt, nodename);
>> +        qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
>> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
>> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
>> +                              qemu_fdt_alloc_phandle(ms->fdt));
>> +        g_free(nodename);
>> +    }
>> +}
>> +
>> +
>> +static void fdt_add_virtio_devices(const HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    /* VirtIO MMIO devices */
>> +    for (int i = 0; i < VIRTIO_DEV_COUNT; i++) {
>> +        char *nodename;
>> +        int irq = irqmap[VIRT_MMIO] + i;
>> +        size_t size = base_memmap[VIRT_MMIO].size;
>> +        hwaddr base = base_memmap[VIRT_MMIO].base + i * size;
>> +
>> +        nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
>> +        qemu_fdt_add_subnode(ms->fdt, nodename);
>> +        qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "virtio,mmio");
>> +        qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 1,
>> +                                     size);
>> +        qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0);
>> +        qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
>> +                              irq_hvm_ic_phandle);
>> +
>> +        sysbus_create_simple(
>> +            "virtio-mmio", base,
>> +            qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_MMIO] + i));
>> +
>> +        g_free(nodename);
>> +    }
>> +}
>> +
>> +static void virt_instance_init(Object *obj)
>> +{
>> +    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
>> +
>> +    create_fdt(vms);
>> +}
>> +
>> +void hexagon_load_fdt(const HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
>> +    uint32_t fdtsize = vms->fdt_size;
>> +
>> +    g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
>> +    /* copy in the device tree */
>> +    rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
>> +                          &address_space_memory);
>> +    qemu_register_reset_nosnapshotload(
>> +        qemu_fdt_randomize_seeds,
>> +        rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
>> +}
>> +
>> +static uint64_t load_kernel(const HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    uint64_t entry = 0;
>> +    if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL,
>> +                         NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory,
>> +                         false, NULL) > 0) {
>> +        return entry;
>> +    }
>> +    error_report("error loading '%s'", ms->kernel_filename);
>> +    exit(1);
>> +}
>> +
>> +static uint64_t load_bios(HexagonVirtMachineState *vms)
>> +{
>> +    MachineState *ms = MACHINE(vms);
>> +    uint64_t bios_addr = 0x0;  /* Load BIOS at reset vector address 0x0 */
>> +    int bios_size;
>> +
>> +    bios_size = load_image_targphys(ms->firmware ?: "",
>> +                                    bios_addr, 64 * 1024, NULL);
>> +    if (bios_size < 0) {
>> +        error_report("Could not load BIOS '%s'", ms->firmware ?: "");
>> +        exit(1);
>> +    }
>> +
>> +    return bios_addr;  /* Return entry point at address 0x0 */
>> +}
>> +
>> +static void do_cpu_reset(void *opaque)
>> +{
>> +    HexagonCPU *cpu = opaque;
>> +    CPUState *cs = CPU(cpu);
>> +    cpu_reset(cs);
>> +}
>> +
>> +static void virt_init(MachineState *ms)
>> +{
>> +    HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
>> +    const struct hexagon_machine_config *m_cfg = &v68n_1024;
>
> This is v68 but in virt_class_init  mc->default_cpu_type is set to v73, should that be 68?

Fixed these, thanks Sid!

https://github.com/quic/qemu/tree/bcain/sysemu_review  e5f9d63 has the
requested changes, will give it a ~couple of days and send a new round
of patches for review w/the fixes.

Here's a range-diff:

 1:  d2341f42a80 !  1:  846fe3f0958 hw/hexagon: Add globalreg model
    @@ Commit message
         interface to the system registers.

         Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
    +    Reviewed-by: Sid Manning <sid.manning@oss.qualcomm.com>

      ## include/hw/hexagon/hexagon_globalreg.h (new) ##
     @@
    @@ hw/hexagon/hexagon_globalreg.c (new)
     +    s->regs[HEX_SREG_PMUCNT7] = INVALID_REG_VAL;
     +}
     +
    -+static void hexagon_globalreg_realize(DeviceState *dev, Error **errp)
    -+{
    -+}
    -+
     +void hexagon_globalreg_reset(HexagonGlobalRegState *s)
     +{
     +    do_hexagon_globalreg_reset(s);
    @@ hw/hexagon/hexagon_globalreg.c (new)
     +    DeviceClass *dc = DEVICE_CLASS(klass);
     +    ResettableClass *rc = RESETTABLE_CLASS(klass);
     +
    -+    dc->realize = hexagon_globalreg_realize;
     +    rc->phases.hold = hexagon_globalreg_reset_hold;
     +    dc->vmsd = &vmstate_hexagon_globalreg;
     +    dc->user_creatable = false;
 2:  21483a7154e =  2:  e3ed41133e7 hw/hexagon: Add global register tracing
 3:  76011b26056 =  3:  735792b3493 hw/hexagon: Add machine configs for sysemu
 4:  8b58db4d79b =  4:  9bbc363926a hw/hexagon: Add v68, sa8775-cdsp0 defs
 5:  50c227946bc =  5:  db09e484815 hw/hexagon: Add support for cfgbase
 6:  58150cb26eb =  6:  ad4b71cf913 hw/hexagon: Modify "Standalone" symbols
 7:  d6a21abad25 =  7:  81f67470f1f target/hexagon: add build config for softmmu
 8:  fdfbf4bd5f5 !  8:  48c194c1b33 hw/hexagon: Define hexagon "virt" machine
    @@ hw/hexagon/virt.c (new)
     +    cpu_model = ms->cpu_type;
     +
     +    if (!cpu_model) {
    -+        cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
    ++        cpu_model = HEXAGON_CPU_TYPE_NAME("v68");
     +    }
     +
     +    gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
    @@ hw/hexagon/virt.c (new)
     +
     +    mc->desc = "Hexagon Virtual Machine";
     +    mc->init = virt_init;
    -+    mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73");
    ++    mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v68");
     +    mc->default_ram_size = 4 * GiB;
     +    mc->max_cpus = 8;
     +    mc->default_cpus = 8;
 9:  8e81fac5f64 =  9:  e5f9d632610 tests/qtest: Add hexagon boot-serial-test





>> +    const char *cpu_model;
>> +    DeviceState *gsregs_dev;
>> +    DeviceState *tlb_dev;
>> +    HexagonCPU *cpu_0;
>> +
>> +    qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
>> +
>> +    vms->sys = get_system_memory();
>> +
>> +    /* Create APB clock for peripherals */
>> +    vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
>> +    clock_set_hz(vms->apb_clk, 24000000);
>> +
>> +    memory_region_init_ram(&vms->ram, NULL, "ddr.ram", ms->ram_size,
>> +                           &error_fatal);
>> +    memory_region_add_subregion(vms->sys, 0x0, &vms->ram);
>> +
>> +    if (m_cfg->l2tcm_size) {
>> +        memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size,
>> +                               &error_fatal);
>> +        memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16,
>> +                                    &vms->tcm);
>> +    }
>> +
>> +    memory_region_init_rom(&vms->cfgtable, NULL, "config_table.rom",
>> +                           sizeof(m_cfg->cfgtable), &error_fatal);
>> +    memory_region_add_subregion(vms->sys, m_cfg->cfgbase, &vms->cfgtable);
>> +    fdt_add_hvx(vms, m_cfg, &error_fatal);
>> +    cpu_model = ms->cpu_type;
>> +
>> +    if (!cpu_model) {
>> +        cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
>> +    }
>> +
>> +    gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
>> +    object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev));
>> +    qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
>> +    qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal);
>> +
>> +    tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
>> +    object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev));
>> +    qdev_prop_set_uint32(tlb_dev, "num-entries",
>> +                         m_cfg->cfgtable.jtlb_size_entries);
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal);
>> +
>> +    cpu_0 = NULL;
>> +    for (int i = 0; i < ms->smp.cpus; i++) {
>> +        HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
>> +        qemu_register_reset(do_cpu_reset, cpu);
>> +
>> +        if (i == 0) {
>> +            cpu_0 = cpu;
>> +            if (ms->kernel_filename) {
>> +                uint64_t entry = load_kernel(vms);
>> +                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
>> +            } else if (ms->firmware) {
>> +                uint64_t entry = load_bios(vms);
>> +                qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry);
>> +            }
>> +        }
>> +        qdev_prop_set_uint32(DEVICE(cpu), "htid", i);
>> +        qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
>> +        object_property_set_link(OBJECT(cpu), "global-regs",
>> +                                 OBJECT(gsregs_dev), &error_fatal);
>> +        object_property_set_link(OBJECT(cpu), "tlb",
>> +                                 OBJECT(tlb_dev), &error_fatal);
>> +
>> +        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal)) {
>> +            return;
>> +        }
>> +    }
>> +    /* TODO: enable l2vic when l2vic device arrives */
>> +    if (object_class_by_name("l2vic")) {
>> +        vms->l2vic = sysbus_create_varargs(
>> +            "l2vic", m_cfg->l2vic_base,
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 0),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 1),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 2),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 3),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 4),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 5),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 6),
>> +            qdev_get_gpio_in(DEVICE(cpu_0), 7), NULL);
>> +
>> +        fdt_add_hvm_pic_node(vms, m_cfg);
>> +        fdt_add_virtio_devices(vms);
>> +        fdt_add_gpt_node(vms);
>> +    }
>> +
>> +    fdt_add_cpu_nodes(vms);
>> +    fdt_add_clocks(vms);
>> +    fdt_add_uart(vms, VIRT_UART0);
>> +
>> +    rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
>> +                          sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
>> +                          &address_space_memory);
>> +
>> +
>> +    hexagon_load_fdt(vms);
>> +}
>> +
>> +
>> +static void virt_class_init(ObjectClass *oc, const void *data)
>> +{
>> +    MachineClass *mc = MACHINE_CLASS(oc);
>> +
>> +    mc->desc = "Hexagon Virtual Machine";
>> +    mc->init = virt_init;
>> +    mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73");
>> +    mc->default_ram_size = 4 * GiB;
>> +    mc->max_cpus = 8;
>> +    mc->default_cpus = 8;
>> +    mc->is_default = false;
>> +    mc->default_kernel_irqchip_split = false;
>> +    mc->block_default_type = IF_VIRTIO;
>> +    mc->default_boot_order = NULL;
>> +    mc->no_cdrom = 1;
>> +    mc->numa_mem_supported = false;
>> +    mc->default_nic = "virtio-mmio-bus";
>> +}
>> +
>> +
>> +static const TypeInfo virt_machine_types[] = { {
>> +    .name = TYPE_HEXAGON_VIRT_MACHINE,
>> +    .parent = TYPE_MACHINE,
>> +    .instance_size = sizeof(HexagonVirtMachineState),
>> +    .class_init = virt_class_init,
>> +    .instance_init = virt_instance_init,
>> +} };
>> +
>> +DEFINE_TYPES(virt_machine_types)
>> diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
>> index 355abb4fd24..01781a76caf 100644
>> --- a/target/hexagon/cpu.c
>> +++ b/target/hexagon/cpu.c
>> @@ -68,6 +68,8 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
>>
>>  static const Property hexagon_cpu_properties[] = {
>>  #if !defined(CONFIG_USER_ONLY)
>> +    DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
>> +                     HexagonTLBState *),
>>      DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff),
>>      DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs,
>>          TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *),
>> diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
>> index 7b9577f68f7..dc74751d21e 100644
>> --- a/hw/hexagon/Kconfig
>> +++ b/hw/hexagon/Kconfig
>> @@ -3,3 +3,13 @@ config HEX_DSP
>>      default y
>>      depends on HEXAGON && TCG
>>      imply PTIMER
>> +
>> +config HEX_VIRT
>> +    bool
>> +    default y
>> +    depends on HEX_DSP && FDT
>> +    select DEVICE_TREE
>> +    select VIRTIO_MMIO
>> +    select PL011
>> +    select VIRTIO_BLK
>> +    select VIRTIO_SCSI
>> diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build
>> index f528d2bc4ab..5b6a5e11a17 100644
>> --- a/hw/hexagon/meson.build
>> +++ b/hw/hexagon/meson.build
>> @@ -4,3 +4,5 @@ hexagon_ss.add(files('hexagon_globalreg.c'))
>>  hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c'))
>>
>>  hw_arch += {'hexagon': hexagon_ss}
>> +
>> +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c'))
>> diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
>> index c357e6ebf50..86bcdf7cfad 100644
>> --- a/tests/qemu-iotests/testenv.py
>> +++ b/tests/qemu-iotests/testenv.py
>> @@ -259,6 +259,7 @@ def __init__(self, source_dir: str, build_dir: str,
>>              ('arm', 'virt'),
>>              ('aarch64', 'virt'),
>>              ('avr', 'mega2560'),
>> +            ('hexagon', 'virt'),
>>              ('m68k', 'virt'),
>>              ('or1k', 'virt'),
>>              ('riscv32', 'virt'),
>> --
>> 2.34.1
>>