master
c 384 lines 13.2 KB
Raw
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/hex-subsys.h"
17 #include "hw/core/loader.h"
18 #include "hw/core/qdev-properties.h"
19 #include "hw/core/qdev-clock.h"
20 #include "hw/core/register.h"
21 #include "qemu/error-report.h"
22 #include "qemu/guest-random.h"
23 #include "qemu/units.h"
24 #include "machine_cfg_v68n_1024.h.inc"
25 #include "system/address-spaces.h"
26 #include "system/device_tree.h"
27 #include "system/reset.h"
28 #include "system/system.h"
29 #include <libfdt.h>
30
31 enum {
32 VIRT_UART0,
33 VIRT_MMIO,
34 VIRT_FDT,
35 };
36
37 /*
38 * Virtio IRQs run from VIRTIO_IRQ_BASE to
39 * VIRTIO_IRQ_BASE + VIRTIO_DEV_COUNT - 1
40 */
41 static const int VIRTIO_IRQ_BASE = 16;
42 static const int VIRT_UART0_IRQ = 15;
43
44 static const MemMapEntry base_memmap[] = {
45 [VIRT_UART0] = { 0x10000000, 0x00000200 },
46 [VIRT_MMIO] = { 0x11000000, 0x00001000 },
47 [VIRT_FDT] = { 0x99800000, 0x00400000 },
48 };
49
50
51 static void create_fdt(HexagonVirtMachineState *vms)
52 {
53 MachineState *ms = MACHINE(vms);
54 void *fdt = create_device_tree(&vms->fdt_size);
55 uint8_t rng_seed[32];
56
57 if (!fdt) {
58 error_report("create_device_tree() failed");
59 exit(1);
60 }
61
62 ms->fdt = fdt;
63
64 qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
65 qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
66 qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu");
67 qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150");
68
69 qemu_fdt_add_subnode(fdt, "/soc");
70 qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
71 qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
72 qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
73
74 qemu_fdt_add_subnode(fdt, "/chosen");
75 qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
76 qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
77 }
78
79 static int32_t fdt_add_l2vic(HexagonVirtMachineState *vms,
80 const struct hexagon_machine_config *m_cfg)
81 {
82 MachineState *ms = MACHINE(vms);
83 int32_t l2vic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
84 char *nodename = g_strdup_printf("/soc/interrupt-controller@%x",
85 m_cfg->l2vic_base);
86 const char compat[] = "qcom,h2-pic\0hvm-pic";
87
88 qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent", l2vic_phandle);
89
90 qemu_fdt_add_subnode(ms->fdt, nodename);
91 qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x0);
92 qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 0x1);
93 qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
94 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
95 m_cfg->l2vic_base, m_cfg->l2vic_size);
96 qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
97 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", l2vic_phandle);
98
99 g_free(nodename);
100 return l2vic_phandle;
101 }
102
103 static void fdt_add_hvx(HexagonVirtMachineState *vms,
104 const struct hexagon_machine_config *m_cfg)
105 {
106 const MachineState *ms = MACHINE(vms);
107 uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024;
108 if (vtcm_size_bytes > 0) {
109 qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm");
110 qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible",
111 "qcom,hexagon_vtcm");
112
113 assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t));
114 qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0,
115 m_cfg->cfgtable.vtcm_base << 16,
116 vtcm_size_bytes);
117 }
118
119 if (m_cfg->cfgtable.ext_contexts > 0) {
120 qemu_fdt_add_subnode(ms->fdt, "/soc/hvx");
121 qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible",
122 "qcom,hexagon-hvx");
123 qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts",
124 m_cfg->cfgtable.ext_contexts);
125 qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength",
126 m_cfg->cfgtable.hvx_vec_log_length);
127 }
128 }
129
130 static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms)
131 {
132 MachineState *ms = MACHINE(vms);
133 int32_t clk_phandle = qemu_fdt_alloc_phandle(ms->fdt);
134
135 qemu_fdt_add_subnode(ms->fdt, "/apb-pclk");
136 qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock");
137 qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0);
138 qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000);
139 qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names",
140 "clk24mhz");
141 qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clk_phandle);
142
143 return clk_phandle;
144 }
145
146 static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart,
147 int32_t clk_phandle, int32_t l2vic_phandle)
148 {
149 char *nodename;
150 hwaddr base = base_memmap[uart].base;
151 hwaddr size = base_memmap[uart].size;
152 assert(uart == 0);
153 const char compat[] = "arm,pl011\0arm,primecell";
154 const char clocknames[] = "uartclk\0apb_pclk";
155 MachineState *ms = MACHINE(vms);
156 DeviceState *dev;
157 SysBusDevice *s;
158
159 dev = qdev_new(TYPE_PL011);
160 s = SYS_BUS_DEVICE(dev);
161 qdev_prop_set_chr(dev, "chardev", serial_hd(0));
162 qdev_connect_clock_in(dev, "clk", vms->apb_clk);
163 sysbus_realize_and_unref(s, &error_fatal);
164 sysbus_mmio_map(s, 0, base);
165 sysbus_connect_irq(s, 0,
166 qdev_get_gpio_in(vms->parent_obj.l2vic, VIRT_UART0_IRQ));
167
168 nodename = g_strdup_printf("/pl011@%" PRIx64, base);
169 qemu_fdt_add_subnode(ms->fdt, nodename);
170
171 /* Note that we can't use setprop_string because of the embedded NUL */
172 qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat));
173 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
174 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", VIRT_UART0_IRQ);
175 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
176 l2vic_phandle);
177 qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle,
178 clk_phandle);
179 qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames,
180 sizeof(clocknames));
181
182 qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
183 qemu_fdt_add_subnode(ms->fdt, "/aliases");
184 qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename);
185
186 g_free(nodename);
187 }
188
189 static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms)
190 {
191 MachineState *ms = MACHINE(vms);
192 qemu_fdt_add_subnode(ms->fdt, "/cpus");
193 qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
194 qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
195
196 /* cpu nodes */
197 for (int num = ms->smp.cpus - 1; num >= 0; num--) {
198 char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
199 qemu_fdt_add_subnode(ms->fdt, nodename);
200 qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
201 qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
202 qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
203 qemu_fdt_alloc_phandle(ms->fdt));
204 g_free(nodename);
205 }
206 }
207
208 static void create_virtio_devices(HexagonVirtMachineState *vms,
209 int32_t l2vic_phandle)
210 {
211 MachineState *ms = MACHINE(vms);
212 hwaddr size = base_memmap[VIRT_MMIO].size;
213
214 for (int i = 0; i < VIRTIO_DEV_COUNT; i++) {
215 int irq = VIRTIO_IRQ_BASE + i;
216 hwaddr base = base_memmap[VIRT_MMIO].base + i * size;
217 char *nodename = g_strdup_printf("/soc/virtio_mmio@%" PRIx64, base);
218 DeviceState *dev = qdev_new("virtio-mmio");
219 SysBusDevice *s = SYS_BUS_DEVICE(dev);
220
221 object_property_add_child(OBJECT(MACHINE(vms)), "virtio-mmio[*]",
222 OBJECT(dev));
223 sysbus_realize_and_unref(s, &error_fatal);
224 sysbus_mmio_map(s, 0, base);
225 sysbus_connect_irq(s, 0,
226 qdev_get_gpio_in(vms->parent_obj.l2vic, irq));
227 vms->virtio_mmio[i] = dev;
228
229 qemu_fdt_add_subnode(ms->fdt, nodename);
230 qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
231 "virtio,mmio");
232 qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size);
233 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", irq);
234 qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
235 l2vic_phandle);
236
237 g_free(nodename);
238 }
239 }
240
241 void hexagon_load_fdt(const HexagonVirtMachineState *vms)
242 {
243 MachineState *ms = MACHINE(vms);
244 hwaddr fdt_addr = base_memmap[VIRT_FDT].base;
245 uint32_t fdtsize = vms->fdt_size;
246
247 g_assert(fdtsize <= base_memmap[VIRT_FDT].size);
248 /* copy in the device tree */
249 rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr,
250 &address_space_memory);
251 qemu_register_reset_nosnapshotload(
252 qemu_fdt_randomize_seeds,
253 rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize));
254 }
255
256 static uint64_t load_kernel(const HexagonVirtMachineState *vms)
257 {
258 MachineState *ms = MACHINE(vms);
259 uint64_t entry = 0;
260 if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL,
261 NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory,
262 false, NULL) > 0) {
263 return entry;
264 }
265 error_report("error loading '%s'", ms->kernel_filename);
266 exit(1);
267 }
268
269 static uint64_t load_bios(HexagonVirtMachineState *vms)
270 {
271 MachineState *ms = MACHINE(vms);
272 uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */
273 int bios_size;
274
275 bios_size = load_image_targphys(ms->firmware ?: "",
276 bios_addr, 64 * 1024, NULL);
277 if (bios_size < 0) {
278 error_report("Could not load BIOS '%s'", ms->firmware ?: "");
279 exit(1);
280 }
281
282 return bios_addr; /* Return entry point at address 0x0 */
283 }
284
285 static void do_cpu_reset(void *opaque)
286 {
287 HexagonCPU *cpu = opaque;
288 CPUState *cs = CPU(cpu);
289 cpu_reset(cs);
290 }
291
292 static void virt_init(MachineState *ms)
293 {
294 HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms);
295 const struct hexagon_machine_config *m_cfg = &v68n_1024;
296 int32_t clk_phandle;
297 int32_t l2vic_phandle;
298
299 create_fdt(vms);
300 qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline);
301
302 vms->sys = get_system_memory();
303
304 /* Create APB clock for peripherals */
305 vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk");
306 clock_set_hz(vms->apb_clk, 24000000);
307
308 hex_subsys_create(&vms->parent_obj, m_cfg, v68_rev);
309
310 if (m_cfg->l2tcm_size) {
311 memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size,
312 &error_fatal);
313 memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16,
314 &vms->tcm);
315 }
316
317 fdt_add_hvx(vms, m_cfg);
318
319 l2vic_phandle = fdt_add_l2vic(vms, m_cfg);
320 create_virtio_devices(vms, l2vic_phandle);
321
322 g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, ms->smp.cpus);
323
324 for (int i = 0; i < ms->smp.cpus; i++) {
325 HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
326 qemu_register_reset(do_cpu_reset, cpu);
327
328 if (i == 0) {
329 if (ms->kernel_filename) {
330 uint64_t entry = load_kernel(vms);
331 qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry);
332 } else if (ms->firmware) {
333 uint64_t entry = load_bios(vms);
334 qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry);
335 }
336 }
337 qdev_prop_set_uint32(DEVICE(cpu), "htid", i);
338 qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
339 hex_subsys_add_cpu(&vms->parent_obj, DEVICE(cpu));
340 cpus[i] = cpu;
341 }
342
343 hex_subsys_realize_cluster(&vms->parent_obj);
344
345 for (int i = 0; i < ms->smp.cpus; i++) {
346 hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i]), (i == 0));
347 }
348
349 fdt_add_cpu_nodes(vms);
350 clk_phandle = fdt_add_clocks(vms);
351 fdt_add_uart(vms, VIRT_UART0, clk_phandle, l2vic_phandle);
352
353 hexagon_load_fdt(vms);
354 }
355
356
357 static void virt_class_init(ObjectClass *oc, const void *data)
358 {
359 MachineClass *mc = MACHINE_CLASS(oc);
360
361 mc->desc = "Hexagon Virtual Machine";
362 mc->init = virt_init;
363 mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v68");
364 mc->default_ram_size = 4 * GiB;
365 mc->max_cpus = 8;
366 mc->default_cpus = 8;
367 mc->is_default = false;
368 mc->default_kernel_irqchip_split = false;
369 mc->block_default_type = IF_VIRTIO;
370 mc->default_boot_order = NULL;
371 mc->no_cdrom = 1;
372 mc->numa_mem_supported = false;
373 mc->default_nic = "virtio-mmio-bus";
374 }
375
376
377 static const TypeInfo virt_machine_types[] = { {
378 .name = TYPE_HEXAGON_VIRT_MACHINE,
379 .parent = TYPE_HEXAGON_COMMON_MACHINE,
380 .instance_size = sizeof(HexagonVirtMachineState),
381 .class_init = virt_class_init,
382 } };
383
384 DEFINE_TYPES(virt_machine_types)