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'),