@samitouri / QOSamiQemu / commits / 88a8bc7f43

hw/hexagon: Define hexagon "virt" machine

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Brian Cain committed Jun 22, 2026 at 15:29 UTC 88a8bc7f43fffbb014c9958457e5dcd18b222ac3
7 files changed +391
configs/devices/hexagon-softmmu/default.mak
+1
@@ -3,4 +3,5 @@
3 # Uncomment the following lines to disable these optional devices:
4
5 # Boards are selected by default, uncomment to keep out of the build.
6 +# CONFIG_HEX_VIRT=y
7 # CONFIG_HEX_DSP=y
configs/targets/hexagon-softmmu.mak
+1
@@ -5,3 +5,4 @@ TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml
5 TARGET_LONG_BITS=32
6 TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
7 TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
8 +TARGET_NEED_FDT=y
hw/hexagon/Kconfig
+10
@@ -2,3 +2,13 @@ config HEX_DSP
2 bool
3 default y
4 depends on HEXAGON
5 +
6 +config HEX_VIRT
7 + bool
8 + default y
9 + depends on HEX_DSP && FDT
10 + select DEVICE_TREE
11 + select VIRTIO_MMIO
12 + select PL011
13 + select VIRTIO_BLK
14 + select VIRTIO_SCSI
hw/hexagon/meson.build
+1
@@ -2,5 +2,6 @@ hexagon_ss = ss.source_set()
2 hexagon_ss.add(files('hexagon_tlb.c'))
3 hexagon_ss.add(files('hexagon_globalreg.c'))
4 hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c'))
5 +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c'))
6
7 hw_arch += {'hexagon': hexagon_ss}
hw/hexagon/virt.c new
+347
@@ -0,0 +1,347 @@
1 +/*
2 + * Hexagon virt emulation
3 + *
4 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#include "qemu/osdep.h"
9 +#include "qapi/error.h"
10 +#include "hw/hexagon/virt.h"
11 +#include "elf.h"
12 +#include "hw/char/pl011.h"
13 +#include "hw/core/clock.h"
14 +#include "hw/core/sysbus-fdt.h"
15 +#include "hw/hexagon/hexagon.h"
16 +#include "hw/hexagon/hexagon_globalreg.h"
17 +#include "hw/hexagon/hexagon_tlb.h"
18 +#include "hw/core/loader.h"
19 +#include "hw/core/qdev-properties.h"
20 +#include "hw/core/qdev-clock.h"
21 +#include "hw/core/register.h"
22 +#include "qemu/error-report.h"
23 +#include "qemu/guest-random.h"
24 +#include "qemu/units.h"
25 +#include "machine_cfg_v68n_1024.h.inc"
26 +#include "system/address-spaces.h"
27 +#include "system/device_tree.h"
28 +#include "system/reset.h"
29 +#include "system/system.h"
30 +#include <libfdt.h>
31 +
32 +enum {
33 + VIRT_UART0,
34 + VIRT_FDT,
35 +};
36 +
37 +static const MemMapEntry base_memmap[] = {
38 + [VIRT_UART0] = { 0x10000000, 0x00000200 },
39 + [VIRT_FDT] = { 0x99800000, 0x00400000 },
40 +};
41 +
42 +
43 +static void create_fdt(HexagonVirtMachineState *vms)
44 +{
45 + MachineState *ms = MACHINE(vms);
46 + void *fdt = create_device_tree(&vms->fdt_size);
47 + uint8_t rng_seed[32];
48 +
49 + if (!fdt) {
50 + error_report("create_device_tree() failed");
51 + exit(1);
52 + }
53 +
54 + ms->fdt = fdt;
55 +
56 + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
57 + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
58 + qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
59 + qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
60 +
61 + qemu_fdt_add_subnode(fdt, "/soc");
62 + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
63 + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
64 + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
65 +
66 + qemu_fdt_add_subnode(fdt, "/chosen");
67 + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
68 + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
69 +}
70 +
71 +static void fdt_add_hvx(HexagonVirtMachineState *vms,
72 + const struct hexagon_machine_config *m_cfg)
73 +{
74 + const MachineState *ms = MACHINE(vms);
75 + uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
76 + if (vtcm_size_bytes > 0) {
77 + memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes,
78 + &error_fatal);
79 + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16,
80 + &vms->vtcm);
81 +
82 + qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
83 + qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
84 + "qcom,hexagon_vtcm");
85 +
86 + assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
87 + qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
88 + m_cfg->cfgtable.vtcm_base << 16,
89 + vtcm_size_bytes);
90 + }
91 +
92 + if (m_cfg->cfgtable.ext_contexts > 0) {
93 + qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
94 + qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
95 + "qcom,hexagon-hvx");
96 + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
97 + m_cfg->cfgtable.ext_contexts);
98 + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
99 + m_cfg->cfgtable.hvx_vec_log_length);
100 + }
101 +}
102 +
103 +static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms)
104 +{
105 + MachineState *ms = MACHINE(vms);
106 + int32_t clk_phandle = qemu_fdt_alloc_phandle(ms->fdt);
107 +
108 + qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
109 + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock");
110 + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
111 + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000);
112 + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
113 + "clk24mhz");
114 + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clk_phandle);
115 +
116 + return clk_phandle;
117 +}
118 +
119 +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart,
120 + int32_t clk_phandle)
121 +{
122 + char *nodename;
123 + hwaddr base = base_memmap[uart].base;
124 + hwaddr size = base_memmap[uart].size;
125 + assert(uart == 0);
126 + const char compat[] = "arm,pl011\0arm,primecell";
127 + const char clocknames[] = "uartclk\0apb_pclk";
128 + MachineState *ms = MACHINE(vms);
129 + DeviceState *dev;
130 + SysBusDevice *s;
131 +
132 + dev = qdev_new(TYPE_PL011);
133 + s = SYS_BUS_DEVICE(dev);
134 + qdev_prop_set_chr(dev, "chardev", serial_hd(0));
135 + qdev_connect_clock_in(dev, "clk", vms->apb_clk);
136 + sysbus_realize_and_unref(s, &error_fatal);
137 + sysbus_mmio_map(s, 0, base);
138 +
139 + nodename = g_strdup_printf("/pl011@%" PRIx64, base);
140 + qemu_fdt_add_subnode(ms->fdt, nodename);
141 +
142 + /* Note that we can't use setprop_string because of the embedded NUL */
143 + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
144 + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
145 + qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle,
146 + clk_phandle);
147 + qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
148 + sizeof(clocknames));
149 +
150 + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
151 + qemu_fdt_add_subnode(ms->fdt, "/aliases");
152 + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
153 +
154 + g_free(nodename);
155 +}
156 +
157 +static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
158 +{
159 + MachineState *ms = MACHINE(vms);
160 + qemu_fdt_add_subnode(ms->fdt, "/cpus");
161 + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
162 + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
163 +
164 + /* cpu nodes */
165 + for (int num = ms->smp.cpus - 1; num >= 0; num--) {
166 + char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
167 + qemu_fdt_add_subnode(ms->fdt, nodename);
168 + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
169 + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
170 + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
171 + qemu_fdt_alloc_phandle(ms->fdt));
172 + g_free(nodename);
173 + }
174 +}
175 +
176 +
177 +
178 +static void virt_instance_init(Object *obj)
179 +{
180 + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj);
181 +
182 + create_fdt(vms);
183 +}
184 +
185 +void hexagon_load_fdt(const HexagonVirtMachineState *vms)
186 +{
187 + MachineState *ms = MACHINE(vms);
188 + hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
189 + uint32_t fdtsize = vms->fdt_size;
190 +
191 + g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
192 + /* copy in the device tree */
193 + rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
194 + &address_space_memory);
195 + qemu_register_reset_nosnapshotload(
196 + qemu_fdt_randomize_seeds,
197 + rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
198 +}
199 +
200 +static uint64_t load_kernel(const HexagonVirtMachineState *vms)
201 +{
202 + MachineState *ms = MACHINE(vms);
203 + uint64_t entry = 0;
204 + if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL,
205 + NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory,
206 + false, NULL) > 0) {
207 + return entry;
208 + }
209 + error_report("error loading '%s'", ms->kernel_filename);
210 + exit(1);
211 +}
212 +
213 +static uint64_t load_bios(HexagonVirtMachineState *vms)
214 +{
215 + MachineState *ms = MACHINE(vms);
216 + uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */
217 + int bios_size;
218 +
219 + bios_size = load_image_targphys(ms->firmware ?: "",
220 + bios_addr, 64 * 1024, NULL);
221 + if (bios_size < 0) {
222 + error_report("Could not load BIOS '%s'", ms->firmware ?: "");
223 + exit(1);
224 + }
225 +
226 + return bios_addr; /* Return entry point at address 0x0 */
227 +}
228 +
229 +static void do_cpu_reset(void *opaque)
230 +{
231 + HexagonCPU *cpu = opaque;
232 + CPUState *cs = CPU(cpu);
233 + cpu_reset(cs);
234 +}
235 +
236 +static void virt_init(MachineState *ms)
237 +{
238 + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
239 + const struct hexagon_machine_config *m_cfg = &v68n_1024;
240 + DeviceState *gsregs_dev;
241 + DeviceState *tlb_dev;
242 + DeviceState *cpu0;
243 + int32_t clk_phandle;
244 +
245 + qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
246 +
247 + vms->sys = get_system_memory();
248 +
249 + /* Create APB clock for peripherals */
250 + vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
251 + clock_set_hz(vms->apb_clk, 24000000);
252 +
253 + memory_region_init_ram(&vms->parent_obj.ram, NULL, "ddr.ram",
254 + ms->ram_size, &error_fatal);
255 + memory_region_add_subregion(vms->sys, 0x0, &vms->parent_obj.ram);
256 +
257 + if (m_cfg->l2tcm_size) {
258 + memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size,
259 + &error_fatal);
260 + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16,
261 + &vms->tcm);
262 + }
263 +
264 + memory_region_init_rom(&vms->parent_obj.cfgtable_rom, NULL,
265 + "config_table.rom", sizeof(m_cfg->cfgtable),
266 + &error_fatal);
267 + memory_region_add_subregion(vms->sys, m_cfg->cfgbase,
268 + &vms->parent_obj.cfgtable_rom);
269 + fdt_add_hvx(vms, m_cfg);
270 +
271 + gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
272 + object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev));
273 + qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
274 + qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
275 + sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal);
276 +
277 + tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
278 + object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev));
279 + qdev_prop_set_uint32(tlb_dev, "num-entries",
280 + m_cfg->cfgtable.jtlb_size_entries);
281 + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal);
282 +
283 + cpu0 = NULL;
284 + for (int i = 0; i < ms->smp.cpus; i++) {
285 + HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
286 + qemu_register_reset(do_cpu_reset, cpu);
287 +
288 + if (i == 0) {
289 + cpu0 = DEVICE(cpu);
290 + if (ms->kernel_filename) {
291 + uint64_t entry = load_kernel(vms);
292 + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry);
293 + } else if (ms->firmware) {
294 + uint64_t entry = load_bios(vms);
295 + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry);
296 + }
297 + }
298 + qdev_prop_set_uint32(DEVICE(cpu), "htid", i);
299 + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
300 + object_property_set_link(OBJECT(cpu), "global-regs",
301 + OBJECT(gsregs_dev), &error_fatal);
302 + object_property_set_link(OBJECT(cpu), "tlb",
303 + OBJECT(tlb_dev), &error_fatal);
304 +
305 + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal);
306 + }
307 + fdt_add_cpu_nodes(vms);
308 + clk_phandle = fdt_add_clocks(vms);
309 + fdt_add_uart(vms, VIRT_UART0, clk_phandle);
310 +
311 + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
312 + sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
313 + &address_space_memory);
314 +
315 + hexagon_load_fdt(vms);
316 +}
317 +
318 +
319 +static void virt_class_init(ObjectClass *oc, const void *data)
320 +{
321 + MachineClass *mc = MACHINE_CLASS(oc);
322 +
323 + mc->desc = "Hexagon Virtual Machine";
324 + mc->init = virt_init;
325 + mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v68");
326 + mc->default_ram_size = 4 * GiB;
327 + mc->max_cpus = 8;
328 + mc->default_cpus = 8;
329 + mc->is_default = false;
330 + mc->default_kernel_irqchip_split = false;
331 + mc->block_default_type = IF_VIRTIO;
332 + mc->default_boot_order = NULL;
333 + mc->no_cdrom = 1;
334 + mc->numa_mem_supported = false;
335 + mc->default_nic = "virtio-mmio-bus";
336 +}
337 +
338 +
339 +static const TypeInfo virt_machine_types[] = { {
340 + .name = TYPE_HEXAGON_VIRT_MACHINE,
341 + .parent = TYPE_HEXAGON_COMMON_MACHINE,
342 + .instance_size = sizeof(HexagonVirtMachineState),
343 + .class_init = virt_class_init,
344 + .instance_init = virt_instance_init,
345 +} };
346 +
347 +DEFINE_TYPES(virt_machine_types)
include/hw/hexagon/virt.h new
+30
@@ -0,0 +1,30 @@
1 +/*
2 + * Definitions for hexagon virt board.
3 + *
4 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#ifndef HW_HEXAGONVIRT_H
9 +#define HW_HEXAGONVIRT_H
10 +
11 +#include "hw/hexagon/hexagon.h"
12 +#include "target/hexagon/cpu.h"
13 +
14 +struct HexagonVirtMachineState {
15 + HexagonCommonMachineState parent_obj;
16 +
17 + int fdt_size;
18 + MemoryRegion *sys;
19 + MemoryRegion tcm;
20 + MemoryRegion vtcm;
21 + MemoryRegion bios;
22 + Clock *apb_clk;
23 +};
24 +
25 +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
26 +
27 +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
28 +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE)
29 +
30 +#endif /* HW_HEXAGONVIRT_H */
tests/qemu-iotests/testenv.py
+1
@@ -259,6 +259,7 @@ class TestEnv(contextlib.AbstractContextManager['TestEnv']):
259 ('arm', 'virt'),
260 ('aarch64', 'virt'),
261 ('avr', 'mega2560'),
262 + ('hexagon', 'virt'),
263 ('m68k', 'virt'),
264 ('or1k', 'virt'),
265 ('riscv32', 'virt'),