| 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) |
| 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" |
| 26 | #include "qemu/error-report.h" |
| 27 | #include "qapi/error.h" |
| 28 | #include "hw/core/boards.h" |
| 29 | #include "hw/core/loader.h" |
| 30 | #include "hw/core/sysbus.h" |
| 31 | #include "target/riscv/cpu.h" |
| 32 | #include "hw/riscv/riscv_hart.h" |
| 33 | #include "hw/riscv/spike.h" |
| 34 | #include "hw/riscv/boot.h" |
| 35 | #include "hw/riscv/fdt-common.h" |
| 36 | #include "hw/riscv/numa.h" |
| 37 | #include "hw/riscv/machines-qom.h" |
| 38 | #include "hw/char/riscv_htif.h" |
| 39 | #include "hw/intc/riscv_aclint.h" |
| 40 | #include "chardev/char.h" |
| 41 | #include "system/device_tree.h" |
| 42 | #include "system/system.h" |
| 43 | |
| 44 | #include <libfdt.h> |
| 45 | |
| 46 | static const MemMapEntry spike_memmap[] = { |
| 47 | [SPIKE_MROM] = { 0x1000, 0xf000 }, |
| 48 | [SPIKE_HTIF] = { 0x1000000, 0x1000 }, |
| 49 | [SPIKE_CLINT] = { 0x2000000, 0x10000 }, |
| 50 | [SPIKE_DRAM] = { 0x80000000, 0x0 }, |
| 51 | }; |
| 52 | |
| 53 | static void create_fdt(SpikeState *s, const MemMapEntry *memmap, |
| 54 | bool is_32_bit, bool htif_custom_base) |
| 55 | { |
| 56 | void *fdt; |
| 57 | int fdt_size; |
| 58 | unsigned long clint_addr; |
| 59 | int socket; |
| 60 | MachineState *ms = MACHINE(s); |
| 61 | uint32_t phandle = 1; |
| 62 | bool numa_enabled = riscv_numa_enabled(ms); |
| 63 | |
| 64 | fdt = ms->fdt = create_board_device_tree("ucbbar,spike-bare,qemu", |
| 65 | "ucbbar,spike-bare-dev", &fdt_size); |
| 66 | |
| 67 | qemu_fdt_add_subnode(fdt, "/htif"); |
| 68 | qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0"); |
| 69 | if (htif_custom_base) { |
| 70 | qemu_fdt_setprop_cells(fdt, "/htif", "reg", |
| 71 | 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size); |
| 72 | } |
| 73 | |
| 74 | fdt_create_cpu_socket_subnode(fdt, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); |
| 75 | |
| 76 | for (socket = (riscv_socket_count(ms) - 1); socket >= 0; socket--) { |
| 77 | g_autofree uint32_t *intc_phandles = g_new0(uint32_t, |
| 78 | s->soc[socket].num_harts); |
| 79 | hwaddr memaddr = memmap[SPIKE_DRAM].base + |
| 80 | riscv_socket_mem_offset(ms, socket); |
| 81 | uint64_t memsize = riscv_socket_mem_size(ms, socket); |
| 82 | |
| 83 | create_fdt_socket_cpus(fdt, (&s->soc[socket])->harts, socket, |
| 84 | s->soc[socket].num_harts, |
| 85 | s->soc[socket].hartid_base, |
| 86 | &phandle, intc_phandles, numa_enabled, |
| 87 | is_32_bit); |
| 88 | |
| 89 | create_fdt_socket_memory(fdt, memaddr, memsize, socket, |
| 90 | riscv_numa_enabled(ms)); |
| 91 | |
| 92 | clint_addr = memmap[SPIKE_CLINT].base + |
| 93 | (memmap[SPIKE_CLINT].size * socket); |
| 94 | create_fdt_socket_clint(fdt, clint_addr, memmap[SPIKE_CLINT].size, |
| 95 | socket, intc_phandles, |
| 96 | s->soc[socket].num_harts, numa_enabled); |
| 97 | } |
| 98 | |
| 99 | riscv_socket_fdt_write_distance_matrix(ms); |
| 100 | |
| 101 | qemu_fdt_add_subnode(fdt, "/chosen"); |
| 102 | qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif"); |
| 103 | } |
| 104 | |
| 105 | static bool spike_test_elf_image(char *filename) |
| 106 | { |
| 107 | return load_elf_hdr(filename, NULL, NULL, NULL); |
| 108 | } |
| 109 | |
| 110 | static void spike_board_init(MachineState *machine) |
| 111 | { |
| 112 | const MemMapEntry *memmap = spike_memmap; |
| 113 | SpikeState *s = SPIKE_MACHINE(machine); |
| 114 | MemoryRegion *system_memory = get_system_memory(); |
| 115 | MemoryRegion *mask_rom = g_new(MemoryRegion, 1); |
| 116 | hwaddr firmware_end_addr = memmap[SPIKE_DRAM].base; |
| 117 | hwaddr firmware_load_addr = memmap[SPIKE_DRAM].base; |
| 118 | vaddr kernel_start_addr; |
| 119 | char *firmware_name; |
| 120 | uint64_t fdt_load_addr; |
| 121 | uint64_t kernel_entry; |
| 122 | char *soc_name; |
| 123 | int i, base_hartid, hart_count; |
| 124 | bool htif_custom_base = false; |
| 125 | RISCVBootInfo boot_info; |
| 126 | |
| 127 | /* Check socket count limit */ |
| 128 | if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { |
| 129 | error_report("number of sockets/nodes should be less than %d", |
| 130 | SPIKE_SOCKETS_MAX); |
| 131 | exit(1); |
| 132 | } |
| 133 | |
| 134 | /* Initialize sockets */ |
| 135 | for (i = 0; i < riscv_socket_count(machine); i++) { |
| 136 | if (!riscv_socket_check_hartids(machine, i)) { |
| 137 | error_report("discontinuous hartids in socket%d", i); |
| 138 | exit(1); |
| 139 | } |
| 140 | |
| 141 | base_hartid = riscv_socket_first_hartid(machine, i); |
| 142 | if (base_hartid < 0) { |
| 143 | error_report("can't find hartid base for socket%d", i); |
| 144 | exit(1); |
| 145 | } |
| 146 | |
| 147 | hart_count = riscv_socket_hart_count(machine, i); |
| 148 | if (hart_count < 0) { |
| 149 | error_report("can't find hart count for socket%d", i); |
| 150 | exit(1); |
| 151 | } |
| 152 | |
| 153 | soc_name = g_strdup_printf("soc%d", i); |
| 154 | object_initialize_child(OBJECT(machine), soc_name, &s->soc[i], |
| 155 | TYPE_RISCV_HART_ARRAY); |
| 156 | g_free(soc_name); |
| 157 | object_property_set_str(OBJECT(&s->soc[i]), "cpu-type", |
| 158 | machine->cpu_type, &error_abort); |
| 159 | object_property_set_int(OBJECT(&s->soc[i]), "hartid-base", |
| 160 | base_hartid, &error_abort); |
| 161 | object_property_set_int(OBJECT(&s->soc[i]), "num-harts", |
| 162 | hart_count, &error_abort); |
| 163 | sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal); |
| 164 | |
| 165 | /* Core Local Interruptor (timer and IPI) for each socket */ |
| 166 | riscv_aclint_swi_create( |
| 167 | memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size, |
| 168 | base_hartid, hart_count, false); |
| 169 | riscv_aclint_mtimer_create( |
| 170 | memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size + |
| 171 | RISCV_ACLINT_SWI_SIZE, |
| 172 | RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, |
| 173 | RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, |
| 174 | RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); |
| 175 | } |
| 176 | |
| 177 | /* register system main memory (actual RAM) */ |
| 178 | memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base, |
| 179 | machine->ram); |
| 180 | |
| 181 | /* boot rom */ |
| 182 | memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", |
| 183 | memmap[SPIKE_MROM].size, &error_fatal); |
| 184 | memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, |
| 185 | mask_rom); |
| 186 | |
| 187 | /* Find firmware */ |
| 188 | firmware_name = riscv_find_firmware(machine->firmware, |
| 189 | riscv_default_firmware_name(&s->soc[0])); |
| 190 | |
| 191 | /* |
| 192 | * Test the given firmware or kernel file to see if it is an ELF image. |
| 193 | * If it is an ELF, we assume it contains the symbols required for |
| 194 | * the HTIF console, otherwise we fall back to use the custom base |
| 195 | * passed from device tree for the HTIF console. |
| 196 | */ |
| 197 | if (!firmware_name && !machine->kernel_filename) { |
| 198 | htif_custom_base = true; |
| 199 | } else { |
| 200 | if (firmware_name) { |
| 201 | htif_custom_base = !spike_test_elf_image(firmware_name); |
| 202 | } |
| 203 | if (!htif_custom_base && machine->kernel_filename) { |
| 204 | htif_custom_base = !spike_test_elf_image(machine->kernel_filename); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | riscv_boot_info_init(&boot_info, &s->soc[0]); |
| 209 | |
| 210 | /* Load firmware */ |
| 211 | if (firmware_name) { |
| 212 | firmware_end_addr = riscv_load_firmware(machine, &boot_info, |
| 213 | firmware_name, |
| 214 | &firmware_load_addr, |
| 215 | htif_symbol_callback); |
| 216 | g_free(firmware_name); |
| 217 | } |
| 218 | |
| 219 | /* Create device tree */ |
| 220 | create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base); |
| 221 | |
| 222 | /* Load kernel */ |
| 223 | if (machine->kernel_filename) { |
| 224 | kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, |
| 225 | firmware_end_addr); |
| 226 | |
| 227 | riscv_load_kernel(machine, &boot_info, kernel_start_addr, |
| 228 | true, htif_symbol_callback); |
| 229 | kernel_entry = boot_info.image_low_addr; |
| 230 | } else { |
| 231 | /* |
| 232 | * If dynamic firmware is used, it doesn't know where is the next mode |
| 233 | * if kernel argument is not set. |
| 234 | */ |
| 235 | kernel_entry = 0; |
| 236 | } |
| 237 | |
| 238 | fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, |
| 239 | memmap[SPIKE_DRAM].size, |
| 240 | machine, &boot_info); |
| 241 | riscv_load_fdt(fdt_load_addr, machine->fdt); |
| 242 | |
| 243 | /* load the reset vector */ |
| 244 | riscv_setup_rom_reset_vec(machine, &s->soc[0], firmware_load_addr, |
| 245 | memmap[SPIKE_MROM].base, |
| 246 | memmap[SPIKE_MROM].size, kernel_entry, |
| 247 | fdt_load_addr); |
| 248 | |
| 249 | /* initialize HTIF using symbols found in load_kernel */ |
| 250 | htif_mm_init(system_memory, serial_hd(0), memmap[SPIKE_HTIF].base, |
| 251 | htif_custom_base); |
| 252 | } |
| 253 | |
| 254 | static void spike_set_signature(Object *obj, const char *val, Error **errp) |
| 255 | { |
| 256 | sig_file = g_strdup(val); |
| 257 | } |
| 258 | |
| 259 | static void spike_machine_instance_init(Object *obj) |
| 260 | { |
| 261 | } |
| 262 | |
| 263 | static void spike_machine_class_init(ObjectClass *oc, const void *data) |
| 264 | { |
| 265 | MachineClass *mc = MACHINE_CLASS(oc); |
| 266 | |
| 267 | mc->desc = "RISC-V Spike board"; |
| 268 | mc->init = spike_board_init; |
| 269 | mc->max_cpus = SPIKE_CPUS_MAX; |
| 270 | mc->default_cpu_type = TYPE_RISCV_CPU_MAX; |
| 271 | mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; |
| 272 | mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; |
| 273 | mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; |
| 274 | mc->numa_mem_supported = true; |
| 275 | /* platform instead of architectural choice */ |
| 276 | mc->cpu_cluster_has_numa_boundary = true; |
| 277 | mc->default_ram_id = "riscv.spike.ram"; |
| 278 | object_class_property_add_str(oc, "signature", NULL, spike_set_signature); |
| 279 | object_class_property_set_description(oc, "signature", |
| 280 | "File to write ACT test signature"); |
| 281 | object_class_static_property_add_uint8_ptr(oc, "signature-granularity", |
| 282 | &line_size, |
| 283 | OBJ_PROP_FLAG_WRITE); |
| 284 | object_class_property_set_description(oc, "signature-granularity", |
| 285 | "Size of each line in ACT signature " |
| 286 | "file"); |
| 287 | } |
| 288 | |
| 289 | static const TypeInfo spike_machine_typeinfo = { |
| 290 | .name = MACHINE_TYPE_NAME("spike"), |
| 291 | .parent = TYPE_MACHINE, |
| 292 | .class_init = spike_machine_class_init, |
| 293 | .instance_init = spike_machine_instance_init, |
| 294 | .instance_size = sizeof(SpikeState), |
| 295 | .interfaces = riscv32_64_machine_interfaces, |
| 296 | }; |
| 297 | |
| 298 | static void spike_machine_init_register_types(void) |
| 299 | { |
| 300 | type_register_static(&spike_machine_typeinfo); |
| 301 | } |
| 302 | |
| 303 | type_init(spike_machine_init_register_types) |