]>
Commit | Line | Data |
---|---|---|
5b4beba1 MC |
1 | /* |
2 | * QEMU RISC-V Spike Board | |
3 | * | |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu | |
5 | * Copyright (c) 2017-2018 SiFive, Inc. | |
6 | * | |
7 | * This provides a RISC-V Board with the following devices: | |
8 | * | |
9 | * 0) HTIF Console and Poweroff | |
10 | * 1) CLINT (Timer and IPI) | |
5b4beba1 MC |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms and conditions of the GNU General Public License, | |
14 | * version 2 or later, as published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope it will be useful, but WITHOUT | |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
19 | * more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along with | |
22 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
23 | */ | |
24 | ||
25 | #include "qemu/osdep.h" | |
5b4beba1 MC |
26 | #include "qemu/error-report.h" |
27 | #include "qapi/error.h" | |
5b4beba1 MC |
28 | #include "hw/boards.h" |
29 | #include "hw/loader.h" | |
30 | #include "hw/sysbus.h" | |
31 | #include "target/riscv/cpu.h" | |
5b4beba1 | 32 | #include "hw/riscv/riscv_hart.h" |
5b4beba1 | 33 | #include "hw/riscv/spike.h" |
0ac24d56 | 34 | #include "hw/riscv/boot.h" |
a7172791 | 35 | #include "hw/riscv/numa.h" |
70eb9f9c | 36 | #include "hw/char/riscv_htif.h" |
cc63a182 | 37 | #include "hw/intc/riscv_aclint.h" |
5b4beba1 | 38 | #include "chardev/char.h" |
5b4beba1 | 39 | #include "sysemu/device_tree.h" |
46517dd4 | 40 | #include "sysemu/sysemu.h" |
5aec3247 | 41 | |
719b718c DHB |
42 | #include <libfdt.h> |
43 | ||
73261285 | 44 | static const MemMapEntry spike_memmap[] = { |
9eb8b14a | 45 | [SPIKE_MROM] = { 0x1000, 0xf000 }, |
8d8897ac | 46 | [SPIKE_HTIF] = { 0x1000000, 0x1000 }, |
5b4beba1 MC |
47 | [SPIKE_CLINT] = { 0x2000000, 0x10000 }, |
48 | [SPIKE_DRAM] = { 0x80000000, 0x0 }, | |
49 | }; | |
50 | ||
73261285 | 51 | static void create_fdt(SpikeState *s, const MemMapEntry *memmap, |
71d68c48 BM |
52 | uint64_t mem_size, const char *cmdline, |
53 | bool is_32_bit, bool htif_custom_base) | |
5b4beba1 MC |
54 | { |
55 | void *fdt; | |
a7172791 AP |
56 | uint64_t addr, size; |
57 | unsigned long clint_addr; | |
58 | int cpu, socket; | |
59 | MachineState *mc = MACHINE(s); | |
60 | uint32_t *clint_cells; | |
61 | uint32_t cpu_phandle, intc_phandle, phandle = 1; | |
62 | char *name, *mem_name, *clint_name, *clust_name; | |
63 | char *core_name, *cpu_name, *intc_name; | |
7cfbb17f BM |
64 | static const char * const clint_compat[2] = { |
65 | "sifive,clint0", "riscv,clint0" | |
66 | }; | |
5b4beba1 MC |
67 | |
68 | fdt = s->fdt = create_device_tree(&s->fdt_size); | |
69 | if (!fdt) { | |
70 | error_report("create_device_tree() failed"); | |
71 | exit(1); | |
72 | } | |
73 | ||
74 | qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu"); | |
75 | qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev"); | |
76 | qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); | |
77 | qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); | |
78 | ||
79 | qemu_fdt_add_subnode(fdt, "/htif"); | |
80 | qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0"); | |
71d68c48 | 81 | if (htif_custom_base) { |
8d8897ac AP |
82 | qemu_fdt_setprop_cells(fdt, "/htif", "reg", |
83 | 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size); | |
84 | } | |
5b4beba1 MC |
85 | |
86 | qemu_fdt_add_subnode(fdt, "/soc"); | |
87 | qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); | |
117caacf | 88 | qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); |
5b4beba1 MC |
89 | qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); |
90 | qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); | |
91 | ||
5b4beba1 | 92 | qemu_fdt_add_subnode(fdt, "/cpus"); |
2a8756ed | 93 | qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", |
b8fb878a | 94 | RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); |
5b4beba1 MC |
95 | qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); |
96 | qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); | |
a7172791 AP |
97 | qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); |
98 | ||
99 | for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { | |
100 | clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); | |
101 | qemu_fdt_add_subnode(fdt, clust_name); | |
102 | ||
103 | clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); | |
5b4beba1 | 104 | |
a7172791 AP |
105 | for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { |
106 | cpu_phandle = phandle++; | |
107 | ||
108 | cpu_name = g_strdup_printf("/cpus/cpu@%d", | |
109 | s->soc[socket].hartid_base + cpu); | |
110 | qemu_fdt_add_subnode(fdt, cpu_name); | |
bd62c13e AF |
111 | if (is_32_bit) { |
112 | qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32"); | |
113 | } else { | |
114 | qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv48"); | |
115 | } | |
a7172791 AP |
116 | name = riscv_isa_string(&s->soc[socket].harts[cpu]); |
117 | qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", name); | |
118 | g_free(name); | |
119 | qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); | |
120 | qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); | |
121 | qemu_fdt_setprop_cell(fdt, cpu_name, "reg", | |
122 | s->soc[socket].hartid_base + cpu); | |
123 | qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); | |
124 | riscv_socket_fdt_write_id(mc, fdt, cpu_name, socket); | |
125 | qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); | |
5b4beba1 | 126 | |
a7172791 AP |
127 | intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); |
128 | qemu_fdt_add_subnode(fdt, intc_name); | |
129 | intc_phandle = phandle++; | |
130 | qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle); | |
131 | qemu_fdt_setprop_string(fdt, intc_name, "compatible", | |
132 | "riscv,cpu-intc"); | |
133 | qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); | |
134 | qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); | |
135 | ||
136 | clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); | |
137 | clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); | |
138 | clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); | |
139 | clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); | |
140 | ||
141 | core_name = g_strdup_printf("%s/core%d", clust_name, cpu); | |
142 | qemu_fdt_add_subnode(fdt, core_name); | |
143 | qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); | |
144 | ||
145 | g_free(core_name); | |
146 | g_free(intc_name); | |
147 | g_free(cpu_name); | |
148 | } | |
149 | ||
150 | addr = memmap[SPIKE_DRAM].base + riscv_socket_mem_offset(mc, socket); | |
151 | size = riscv_socket_mem_size(mc, socket); | |
152 | mem_name = g_strdup_printf("/memory@%lx", (long)addr); | |
153 | qemu_fdt_add_subnode(fdt, mem_name); | |
154 | qemu_fdt_setprop_cells(fdt, mem_name, "reg", | |
155 | addr >> 32, addr, size >> 32, size); | |
156 | qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory"); | |
157 | riscv_socket_fdt_write_id(mc, fdt, mem_name, socket); | |
158 | g_free(mem_name); | |
159 | ||
160 | clint_addr = memmap[SPIKE_CLINT].base + | |
161 | (memmap[SPIKE_CLINT].size * socket); | |
162 | clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); | |
163 | qemu_fdt_add_subnode(fdt, clint_name); | |
7cfbb17f BM |
164 | qemu_fdt_setprop_string_array(fdt, clint_name, "compatible", |
165 | (char **)&clint_compat, ARRAY_SIZE(clint_compat)); | |
a7172791 AP |
166 | qemu_fdt_setprop_cells(fdt, clint_name, "reg", |
167 | 0x0, clint_addr, 0x0, memmap[SPIKE_CLINT].size); | |
168 | qemu_fdt_setprop(fdt, clint_name, "interrupts-extended", | |
169 | clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); | |
170 | riscv_socket_fdt_write_id(mc, fdt, clint_name, socket); | |
171 | ||
172 | g_free(clint_name); | |
173 | g_free(clint_cells); | |
174 | g_free(clust_name); | |
5b4beba1 | 175 | } |
a7172791 AP |
176 | |
177 | riscv_socket_fdt_write_distance_matrix(mc, fdt); | |
5b4beba1 | 178 | |
6d3b9c02 BM |
179 | qemu_fdt_add_subnode(fdt, "/chosen"); |
180 | qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif"); | |
181 | ||
58303fc0 | 182 | if (cmdline && *cmdline) { |
7c28f4da MC |
183 | qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); |
184 | } | |
cd69e3a6 AF |
185 | } |
186 | ||
71d68c48 BM |
187 | static bool spike_test_elf_image(char *filename) |
188 | { | |
189 | Error *err = NULL; | |
190 | ||
191 | load_elf_hdr(filename, NULL, NULL, &err); | |
192 | if (err) { | |
193 | error_free(err); | |
194 | return false; | |
195 | } else { | |
196 | return true; | |
197 | } | |
198 | } | |
199 | ||
cd69e3a6 AF |
200 | static void spike_board_init(MachineState *machine) |
201 | { | |
73261285 | 202 | const MemMapEntry *memmap = spike_memmap; |
a7172791 | 203 | SpikeState *s = SPIKE_MACHINE(machine); |
cd69e3a6 | 204 | MemoryRegion *system_memory = get_system_memory(); |
cd69e3a6 | 205 | MemoryRegion *mask_rom = g_new(MemoryRegion, 1); |
71d68c48 BM |
206 | target_ulong firmware_end_addr = memmap[SPIKE_DRAM].base; |
207 | target_ulong kernel_start_addr; | |
208 | char *firmware_name; | |
66b1205b | 209 | uint32_t fdt_load_addr; |
dc144fe1 | 210 | uint64_t kernel_entry; |
a7172791 AP |
211 | char *soc_name; |
212 | int i, base_hartid, hart_count; | |
71d68c48 | 213 | bool htif_custom_base = false; |
cd69e3a6 | 214 | |
a7172791 AP |
215 | /* Check socket count limit */ |
216 | if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { | |
217 | error_report("number of sockets/nodes should be less than %d", | |
218 | SPIKE_SOCKETS_MAX); | |
219 | exit(1); | |
220 | } | |
221 | ||
222 | /* Initialize sockets */ | |
223 | for (i = 0; i < riscv_socket_count(machine); i++) { | |
224 | if (!riscv_socket_check_hartids(machine, i)) { | |
225 | error_report("discontinuous hartids in socket%d", i); | |
226 | exit(1); | |
227 | } | |
228 | ||
229 | base_hartid = riscv_socket_first_hartid(machine, i); | |
230 | if (base_hartid < 0) { | |
231 | error_report("can't find hartid base for socket%d", i); | |
232 | exit(1); | |
233 | } | |
234 | ||
235 | hart_count = riscv_socket_hart_count(machine, i); | |
236 | if (hart_count < 0) { | |
237 | error_report("can't find hart count for socket%d", i); | |
238 | exit(1); | |
239 | } | |
240 | ||
241 | soc_name = g_strdup_printf("soc%d", i); | |
242 | object_initialize_child(OBJECT(machine), soc_name, &s->soc[i], | |
243 | TYPE_RISCV_HART_ARRAY); | |
244 | g_free(soc_name); | |
245 | object_property_set_str(OBJECT(&s->soc[i]), "cpu-type", | |
246 | machine->cpu_type, &error_abort); | |
247 | object_property_set_int(OBJECT(&s->soc[i]), "hartid-base", | |
248 | base_hartid, &error_abort); | |
249 | object_property_set_int(OBJECT(&s->soc[i]), "num-harts", | |
250 | hart_count, &error_abort); | |
4bcfc391 | 251 | sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal); |
a7172791 AP |
252 | |
253 | /* Core Local Interruptor (timer and IPI) for each socket */ | |
b8fb878a | 254 | riscv_aclint_swi_create( |
a7172791 | 255 | memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size, |
b8fb878a AP |
256 | base_hartid, hart_count, false); |
257 | riscv_aclint_mtimer_create( | |
258 | memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size + | |
259 | RISCV_ACLINT_SWI_SIZE, | |
260 | RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, | |
261 | RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, | |
262 | RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); | |
a7172791 | 263 | } |
cd69e3a6 AF |
264 | |
265 | /* register system main memory (actual RAM) */ | |
cd69e3a6 | 266 | memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base, |
11ec06f9 | 267 | machine->ram); |
cd69e3a6 | 268 | |
cd69e3a6 AF |
269 | /* boot rom */ |
270 | memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", | |
271 | memmap[SPIKE_MROM].size, &error_fatal); | |
272 | memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, | |
273 | mask_rom); | |
274 | ||
71d68c48 BM |
275 | /* Find firmware */ |
276 | firmware_name = riscv_find_firmware(machine->firmware, | |
277 | riscv_default_firmware_name(&s->soc[0])); | |
278 | ||
279 | /* | |
280 | * Test the given firmware or kernel file to see if it is an ELF image. | |
281 | * If it is an ELF, we assume it contains the symbols required for | |
282 | * the HTIF console, otherwise we fall back to use the custom base | |
283 | * passed from device tree for the HTIF console. | |
284 | */ | |
285 | if (!firmware_name && !machine->kernel_filename) { | |
286 | htif_custom_base = true; | |
287 | } else { | |
288 | if (firmware_name) { | |
289 | htif_custom_base = !spike_test_elf_image(firmware_name); | |
290 | } | |
291 | if (!htif_custom_base && machine->kernel_filename) { | |
292 | htif_custom_base = !spike_test_elf_image(machine->kernel_filename); | |
293 | } | |
294 | } | |
295 | ||
296 | /* Load firmware */ | |
297 | if (firmware_name) { | |
298 | firmware_end_addr = riscv_load_firmware(firmware_name, | |
299 | memmap[SPIKE_DRAM].base, | |
300 | htif_symbol_callback); | |
301 | g_free(firmware_name); | |
302 | } | |
5b8a9863 | 303 | |
8d8897ac | 304 | /* Load kernel */ |
cd69e3a6 | 305 | if (machine->kernel_filename) { |
a8259b53 | 306 | kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], |
38bc4e34 AF |
307 | firmware_end_addr); |
308 | ||
dc144fe1 | 309 | kernel_entry = riscv_load_kernel(machine->kernel_filename, |
38bc4e34 | 310 | kernel_start_addr, |
dc144fe1 | 311 | htif_symbol_callback); |
dc144fe1 AP |
312 | } else { |
313 | /* | |
314 | * If dynamic firmware is used, it doesn't know where is the next mode | |
315 | * if kernel argument is not set. | |
316 | */ | |
317 | kernel_entry = 0; | |
cd69e3a6 AF |
318 | } |
319 | ||
8d8897ac AP |
320 | /* Create device tree */ |
321 | create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, | |
71d68c48 | 322 | riscv_is_32bit(&s->soc[0]), htif_custom_base); |
8d8897ac AP |
323 | |
324 | /* Load initrd */ | |
325 | if (machine->kernel_filename && machine->initrd_filename) { | |
326 | hwaddr start; | |
327 | hwaddr end = riscv_load_initrd(machine->initrd_filename, | |
328 | machine->ram_size, kernel_entry, | |
329 | &start); | |
330 | qemu_fdt_setprop_cell(s->fdt, "/chosen", | |
331 | "linux,initrd-start", start); | |
332 | qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end", | |
333 | end); | |
334 | } | |
335 | ||
66b1205b AP |
336 | /* Compute the fdt load address in dram */ |
337 | fdt_load_addr = riscv_load_fdt(memmap[SPIKE_DRAM].base, | |
338 | machine->ram_size, s->fdt); | |
719b718c DHB |
339 | |
340 | /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ | |
341 | machine->fdt = s->fdt; | |
342 | ||
43cf723a | 343 | /* load the reset vector */ |
a8259b53 | 344 | riscv_setup_rom_reset_vec(machine, &s->soc[0], memmap[SPIKE_DRAM].base, |
78936771 | 345 | memmap[SPIKE_MROM].base, |
dc144fe1 | 346 | memmap[SPIKE_MROM].size, kernel_entry, |
6934f15b | 347 | fdt_load_addr); |
cd69e3a6 AF |
348 | |
349 | /* initialize HTIF using symbols found in load_kernel */ | |
71d68c48 BM |
350 | htif_mm_init(system_memory, serial_hd(0), memmap[SPIKE_HTIF].base, |
351 | htif_custom_base); | |
a7172791 | 352 | } |
cd69e3a6 | 353 | |
a7172791 AP |
354 | static void spike_machine_instance_init(Object *obj) |
355 | { | |
cd69e3a6 | 356 | } |
5b4beba1 | 357 | |
a7172791 | 358 | static void spike_machine_class_init(ObjectClass *oc, void *data) |
cd69e3a6 | 359 | { |
a7172791 AP |
360 | MachineClass *mc = MACHINE_CLASS(oc); |
361 | ||
362 | mc->desc = "RISC-V Spike board"; | |
cd69e3a6 | 363 | mc->init = spike_board_init; |
a7172791 | 364 | mc->max_cpus = SPIKE_CPUS_MAX; |
ea0ac7f6 | 365 | mc->is_default = true; |
dc4d4aae | 366 | mc->default_cpu_type = TYPE_RISCV_CPU_BASE; |
a7172791 AP |
367 | mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; |
368 | mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; | |
369 | mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; | |
370 | mc->numa_mem_supported = true; | |
11ec06f9 | 371 | mc->default_ram_id = "riscv.spike.ram"; |
a7172791 AP |
372 | } |
373 | ||
374 | static const TypeInfo spike_machine_typeinfo = { | |
375 | .name = MACHINE_TYPE_NAME("spike"), | |
376 | .parent = TYPE_MACHINE, | |
377 | .class_init = spike_machine_class_init, | |
378 | .instance_init = spike_machine_instance_init, | |
379 | .instance_size = sizeof(SpikeState), | |
380 | }; | |
381 | ||
382 | static void spike_machine_init_register_types(void) | |
383 | { | |
384 | type_register_static(&spike_machine_typeinfo); | |
5b4beba1 MC |
385 | } |
386 | ||
a7172791 | 387 | type_init(spike_machine_init_register_types) |