10ac24d56SAlistair Francis /* 20ac24d56SAlistair Francis * QEMU RISC-V Boot Helper 30ac24d56SAlistair Francis * 40ac24d56SAlistair Francis * Copyright (c) 2017 SiFive, Inc. 50ac24d56SAlistair Francis * Copyright (c) 2019 Alistair Francis <alistair.francis@wdc.com> 60ac24d56SAlistair Francis * 70ac24d56SAlistair Francis * This program is free software; you can redistribute it and/or modify it 80ac24d56SAlistair Francis * under the terms and conditions of the GNU General Public License, 90ac24d56SAlistair Francis * version 2 or later, as published by the Free Software Foundation. 100ac24d56SAlistair Francis * 110ac24d56SAlistair Francis * This program is distributed in the hope it will be useful, but WITHOUT 120ac24d56SAlistair Francis * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 130ac24d56SAlistair Francis * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 140ac24d56SAlistair Francis * more details. 150ac24d56SAlistair Francis * 160ac24d56SAlistair Francis * You should have received a copy of the GNU General Public License along with 170ac24d56SAlistair Francis * this program. If not, see <http://www.gnu.org/licenses/>. 180ac24d56SAlistair Francis */ 190ac24d56SAlistair Francis 200ac24d56SAlistair Francis #include "qemu/osdep.h" 212c65db5eSPaolo Bonzini #include "qemu/datadir.h" 220ac24d56SAlistair Francis #include "qemu/units.h" 230ac24d56SAlistair Francis #include "qemu/error-report.h" 240ac24d56SAlistair Francis #include "exec/cpu-defs.h" 2512e9493dSMarkus Armbruster #include "hw/boards.h" 260ac24d56SAlistair Francis #include "hw/loader.h" 270ac24d56SAlistair Francis #include "hw/riscv/boot.h" 28dc144fe1SAtish Patra #include "hw/riscv/boot_opensbi.h" 290ac24d56SAlistair Francis #include "elf.h" 3043cf723aSAtish Patra #include "sysemu/device_tree.h" 3175ea2529SAlistair Francis #include "sysemu/qtest.h" 32ad40be27SYifei Jiang #include "sysemu/kvm.h" 3364c75db3SJason A. Donenfeld #include "sysemu/reset.h" 340ac24d56SAlistair Francis 3543cf723aSAtish Patra #include <libfdt.h> 3643cf723aSAtish Patra 37a8259b53SAlistair Francis bool riscv_is_32bit(RISCVHartArrayState *harts) 38c4077842SAlistair Francis { 39db23e5d9SRichard Henderson return harts->harts[0].env.misa_mxl_max == MXL_RV32; 40c4077842SAlistair Francis } 41c4077842SAlistair Francis 42bf357e1dSAlistair Francis /* 43bf357e1dSAlistair Francis * Return the per-socket PLIC hart topology configuration string 44bf357e1dSAlistair Francis * (caller must free with g_free()) 45bf357e1dSAlistair Francis */ 46bf357e1dSAlistair Francis char *riscv_plic_hart_config_string(int hart_count) 47bf357e1dSAlistair Francis { 48bf357e1dSAlistair Francis g_autofree const char **vals = g_new(const char *, hart_count + 1); 49bf357e1dSAlistair Francis int i; 50bf357e1dSAlistair Francis 51bf357e1dSAlistair Francis for (i = 0; i < hart_count; i++) { 52bf357e1dSAlistair Francis CPUState *cs = qemu_get_cpu(i); 53bf357e1dSAlistair Francis CPURISCVState *env = &RISCV_CPU(cs)->env; 54bf357e1dSAlistair Francis 55ad40be27SYifei Jiang if (kvm_enabled()) { 56ad40be27SYifei Jiang vals[i] = "S"; 57ad40be27SYifei Jiang } else if (riscv_has_ext(env, RVS)) { 58bf357e1dSAlistair Francis vals[i] = "MS"; 59bf357e1dSAlistair Francis } else { 60bf357e1dSAlistair Francis vals[i] = "M"; 61bf357e1dSAlistair Francis } 62bf357e1dSAlistair Francis } 63bf357e1dSAlistair Francis vals[i] = NULL; 64bf357e1dSAlistair Francis 65bf357e1dSAlistair Francis /* g_strjoinv() obliges us to cast away const here */ 66bf357e1dSAlistair Francis return g_strjoinv(",", (char **)vals); 67bf357e1dSAlistair Francis } 68bf357e1dSAlistair Francis 69a8259b53SAlistair Francis target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, 7038bc4e34SAlistair Francis target_ulong firmware_end_addr) { 713ed2b8acSAlistair Francis if (riscv_is_32bit(harts)) { 7238bc4e34SAlistair Francis return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB); 7338bc4e34SAlistair Francis } else { 7438bc4e34SAlistair Francis return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB); 7538bc4e34SAlistair Francis } 7638bc4e34SAlistair Francis } 7738bc4e34SAlistair Francis 789d3f7108SDaniel Henrique Barboza const char *riscv_default_firmware_name(RISCVHartArrayState *harts) 799d3f7108SDaniel Henrique Barboza { 809d3f7108SDaniel Henrique Barboza if (riscv_is_32bit(harts)) { 819d3f7108SDaniel Henrique Barboza return RISCV32_BIOS_BIN; 829d3f7108SDaniel Henrique Barboza } 839d3f7108SDaniel Henrique Barboza 849d3f7108SDaniel Henrique Barboza return RISCV64_BIOS_BIN; 859d3f7108SDaniel Henrique Barboza } 869d3f7108SDaniel Henrique Barboza 878f619626SBin Meng static char *riscv_find_bios(const char *bios_filename) 88808faef7SDaniel Henrique Barboza { 89808faef7SDaniel Henrique Barboza char *filename; 90808faef7SDaniel Henrique Barboza 918f619626SBin Meng filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_filename); 92808faef7SDaniel Henrique Barboza if (filename == NULL) { 93808faef7SDaniel Henrique Barboza if (!qtest_enabled()) { 94808faef7SDaniel Henrique Barboza /* 95808faef7SDaniel Henrique Barboza * We only ship OpenSBI binary bios images in the QEMU source. 96808faef7SDaniel Henrique Barboza * For machines that use images other than the default bios, 97808faef7SDaniel Henrique Barboza * running QEMU test will complain hence let's suppress the error 98808faef7SDaniel Henrique Barboza * report for QEMU testing. 99808faef7SDaniel Henrique Barboza */ 1008f619626SBin Meng error_report("Unable to find the RISC-V BIOS \"%s\"", 1018f619626SBin Meng bios_filename); 102808faef7SDaniel Henrique Barboza exit(1); 103808faef7SDaniel Henrique Barboza } 104808faef7SDaniel Henrique Barboza } 105808faef7SDaniel Henrique Barboza 106808faef7SDaniel Henrique Barboza return filename; 107808faef7SDaniel Henrique Barboza } 108808faef7SDaniel Henrique Barboza 1098f619626SBin Meng char *riscv_find_firmware(const char *firmware_filename, 1108f619626SBin Meng const char *default_machine_firmware) 1118f619626SBin Meng { 1128f619626SBin Meng char *filename = NULL; 1138f619626SBin Meng 1148f619626SBin Meng if ((!firmware_filename) || (!strcmp(firmware_filename, "default"))) { 1158f619626SBin Meng /* 1168f619626SBin Meng * The user didn't specify -bios, or has specified "-bios default". 1178f619626SBin Meng * That means we are going to load the OpenSBI binary included in 1188f619626SBin Meng * the QEMU source. 1198f619626SBin Meng */ 1208f619626SBin Meng filename = riscv_find_bios(default_machine_firmware); 1218f619626SBin Meng } else if (strcmp(firmware_filename, "none")) { 1228f619626SBin Meng filename = riscv_find_bios(firmware_filename); 1238f619626SBin Meng } 1248f619626SBin Meng 1258f619626SBin Meng return filename; 1268f619626SBin Meng } 1278f619626SBin Meng 128e66c531eSAlistair Francis target_ulong riscv_find_and_load_firmware(MachineState *machine, 129fdd1bda4SAlistair Francis const char *default_machine_firmware, 13002777ac3SAnup Patel hwaddr firmware_load_addr, 13102777ac3SAnup Patel symbol_fn_t sym_cb) 132fdd1bda4SAlistair Francis { 1338f619626SBin Meng char *firmware_filename; 134e66c531eSAlistair Francis target_ulong firmware_end_addr = firmware_load_addr; 135fdd1bda4SAlistair Francis 1368f619626SBin Meng firmware_filename = riscv_find_firmware(machine->firmware, 1378f619626SBin Meng default_machine_firmware); 138fdd1bda4SAlistair Francis 1393aa9004fSAlistair Francis if (firmware_filename) { 140fdd1bda4SAlistair Francis /* If not "none" load the firmware */ 141e66c531eSAlistair Francis firmware_end_addr = riscv_load_firmware(firmware_filename, 142e66c531eSAlistair Francis firmware_load_addr, sym_cb); 143fdd1bda4SAlistair Francis g_free(firmware_filename); 144fdd1bda4SAlistair Francis } 145e66c531eSAlistair Francis 146e66c531eSAlistair Francis return firmware_end_addr; 147fdd1bda4SAlistair Francis } 148fdd1bda4SAlistair Francis 149b3042223SAlistair Francis target_ulong riscv_load_firmware(const char *firmware_filename, 15002777ac3SAnup Patel hwaddr firmware_load_addr, 15102777ac3SAnup Patel symbol_fn_t sym_cb) 152b3042223SAlistair Francis { 153af975131SJamie Iles uint64_t firmware_entry, firmware_end; 154af975131SJamie Iles ssize_t firmware_size; 155b3042223SAlistair Francis 1561db0c57aSDaniel Henrique Barboza g_assert(firmware_filename != NULL); 1571db0c57aSDaniel Henrique Barboza 15802777ac3SAnup Patel if (load_elf_ram_sym(firmware_filename, NULL, NULL, NULL, 159e66c531eSAlistair Francis &firmware_entry, NULL, &firmware_end, NULL, 16002777ac3SAnup Patel 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { 161e66c531eSAlistair Francis return firmware_end; 162b3042223SAlistair Francis } 163b3042223SAlistair Francis 164e66c531eSAlistair Francis firmware_size = load_image_targphys_as(firmware_filename, 16582e69054SPaolo Bonzini firmware_load_addr, 16682e69054SPaolo Bonzini current_machine->ram_size, NULL); 167e66c531eSAlistair Francis 168e66c531eSAlistair Francis if (firmware_size > 0) { 169e66c531eSAlistair Francis return firmware_load_addr + firmware_size; 170b3042223SAlistair Francis } 171b3042223SAlistair Francis 172b3042223SAlistair Francis error_report("could not load firmware '%s'", firmware_filename); 173b3042223SAlistair Francis exit(1); 174b3042223SAlistair Francis } 175b3042223SAlistair Francis 17660c1f05eSDaniel Henrique Barboza target_ulong riscv_load_kernel(MachineState *machine, 17762c5bc34SDaniel Henrique Barboza RISCVHartArrayState *harts, 17838bc4e34SAlistair Francis target_ulong kernel_start_addr, 179*487d73fcSDaniel Henrique Barboza bool load_initrd, 18038bc4e34SAlistair Francis symbol_fn_t sym_cb) 1810ac24d56SAlistair Francis { 18260c1f05eSDaniel Henrique Barboza const char *kernel_filename = machine->kernel_filename; 1837e322a7fSJessica Clarke uint64_t kernel_load_base, kernel_entry; 184*487d73fcSDaniel Henrique Barboza void *fdt = machine->fdt; 1850ac24d56SAlistair Francis 1861db0c57aSDaniel Henrique Barboza g_assert(kernel_filename != NULL); 1871db0c57aSDaniel Henrique Barboza 1887e322a7fSJessica Clarke /* 1897e322a7fSJessica Clarke * NB: Use low address not ELF entry point to ensure that the fw_dynamic 1907e322a7fSJessica Clarke * behaviour when loading an ELF matches the fw_payload, fw_jump and BBL 1917e322a7fSJessica Clarke * behaviour, as well as fw_dynamic with a raw binary, all of which jump to 1927e322a7fSJessica Clarke * the (expected) load address load address. This allows kernels to have 1937e322a7fSJessica Clarke * separate SBI and ELF entry points (used by FreeBSD, for example). 1947e322a7fSJessica Clarke */ 1956478dd74SZhuang, Siwei (Data61, Kensington NSW) if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, 1967e322a7fSJessica Clarke NULL, &kernel_load_base, NULL, NULL, 0, 1976478dd74SZhuang, Siwei (Data61, Kensington NSW) EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { 19862c5bc34SDaniel Henrique Barboza kernel_entry = kernel_load_base; 19962c5bc34SDaniel Henrique Barboza goto out; 2000ac24d56SAlistair Francis } 2010ac24d56SAlistair Francis 202395fd695SAlistair Francis if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, 203395fd695SAlistair Francis NULL, NULL, NULL) > 0) { 20462c5bc34SDaniel Henrique Barboza goto out; 2050ac24d56SAlistair Francis } 2060ac24d56SAlistair Francis 20738bc4e34SAlistair Francis if (load_image_targphys_as(kernel_filename, kernel_start_addr, 20882e69054SPaolo Bonzini current_machine->ram_size, NULL) > 0) { 20962c5bc34SDaniel Henrique Barboza kernel_entry = kernel_start_addr; 21062c5bc34SDaniel Henrique Barboza goto out; 211395fd695SAlistair Francis } 212395fd695SAlistair Francis 213395fd695SAlistair Francis error_report("could not load kernel '%s'", kernel_filename); 214395fd695SAlistair Francis exit(1); 21562c5bc34SDaniel Henrique Barboza 21662c5bc34SDaniel Henrique Barboza out: 21762c5bc34SDaniel Henrique Barboza /* 21862c5bc34SDaniel Henrique Barboza * For 32 bit CPUs 'kernel_entry' can be sign-extended by 21962c5bc34SDaniel Henrique Barboza * load_elf_ram_sym(). 22062c5bc34SDaniel Henrique Barboza */ 22162c5bc34SDaniel Henrique Barboza if (riscv_is_32bit(harts)) { 22262c5bc34SDaniel Henrique Barboza kernel_entry = extract64(kernel_entry, 0, 32); 22362c5bc34SDaniel Henrique Barboza } 22462c5bc34SDaniel Henrique Barboza 225*487d73fcSDaniel Henrique Barboza if (load_initrd && machine->initrd_filename) { 226*487d73fcSDaniel Henrique Barboza riscv_load_initrd(machine, kernel_entry); 227*487d73fcSDaniel Henrique Barboza } 228*487d73fcSDaniel Henrique Barboza 229*487d73fcSDaniel Henrique Barboza if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { 230*487d73fcSDaniel Henrique Barboza qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", 231*487d73fcSDaniel Henrique Barboza machine->kernel_cmdline); 232*487d73fcSDaniel Henrique Barboza } 233*487d73fcSDaniel Henrique Barboza 23462c5bc34SDaniel Henrique Barboza return kernel_entry; 235395fd695SAlistair Francis } 236395fd695SAlistair Francis 2371f991461SDaniel Henrique Barboza void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) 2380ac24d56SAlistair Francis { 2391f991461SDaniel Henrique Barboza const char *filename = machine->initrd_filename; 2401f991461SDaniel Henrique Barboza uint64_t mem_size = machine->ram_size; 2411f991461SDaniel Henrique Barboza void *fdt = machine->fdt; 242b9a65476SDaniel Henrique Barboza hwaddr start, end; 243af975131SJamie Iles ssize_t size; 2440ac24d56SAlistair Francis 2451db0c57aSDaniel Henrique Barboza g_assert(filename != NULL); 2461db0c57aSDaniel Henrique Barboza 2470ac24d56SAlistair Francis /* 2480ac24d56SAlistair Francis * We want to put the initrd far enough into RAM that when the 2490ac24d56SAlistair Francis * kernel is uncompressed it will not clobber the initrd. However 2500ac24d56SAlistair Francis * on boards without much RAM we must ensure that we still leave 2510ac24d56SAlistair Francis * enough room for a decent sized initrd, and on boards with large 2520ac24d56SAlistair Francis * amounts of RAM we must avoid the initrd being so far up in RAM 2530ac24d56SAlistair Francis * that it is outside lowmem and inaccessible to the kernel. 2540ac24d56SAlistair Francis * So for boards with less than 256MB of RAM we put the initrd 2550ac24d56SAlistair Francis * halfway into RAM, and for boards with 256MB of RAM or more we put 2560ac24d56SAlistair Francis * the initrd at 128MB. 2570ac24d56SAlistair Francis */ 258b9a65476SDaniel Henrique Barboza start = kernel_entry + MIN(mem_size / 2, 128 * MiB); 2590ac24d56SAlistair Francis 260b9a65476SDaniel Henrique Barboza size = load_ramdisk(filename, start, mem_size - start); 2610ac24d56SAlistair Francis if (size == -1) { 262b9a65476SDaniel Henrique Barboza size = load_image_targphys(filename, start, mem_size - start); 2630ac24d56SAlistair Francis if (size == -1) { 2640ac24d56SAlistair Francis error_report("could not load ramdisk '%s'", filename); 2650ac24d56SAlistair Francis exit(1); 2660ac24d56SAlistair Francis } 2670ac24d56SAlistair Francis } 2680ac24d56SAlistair Francis 269b9a65476SDaniel Henrique Barboza /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ 270b9a65476SDaniel Henrique Barboza if (fdt) { 271b9a65476SDaniel Henrique Barboza end = start + size; 272b9a65476SDaniel Henrique Barboza qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); 273b9a65476SDaniel Henrique Barboza qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); 274b9a65476SDaniel Henrique Barboza } 2750ac24d56SAlistair Francis } 27643cf723aSAtish Patra 277bc2c0153SDaniel Henrique Barboza /* 2784b402886SDaniel Henrique Barboza * This function makes an assumption that the DRAM interval 2794b402886SDaniel Henrique Barboza * 'dram_base' + 'dram_size' is contiguous. 280bc2c0153SDaniel Henrique Barboza * 2814b402886SDaniel Henrique Barboza * Considering that 'dram_end' is the lowest value between 2824b402886SDaniel Henrique Barboza * the end of the DRAM block and MachineState->ram_size, the 2834b402886SDaniel Henrique Barboza * FDT location will vary according to 'dram_base': 2844b402886SDaniel Henrique Barboza * 2854b402886SDaniel Henrique Barboza * - if 'dram_base' is less that 3072 MiB, the FDT will be 2864b402886SDaniel Henrique Barboza * put at the lowest value between 3072 MiB and 'dram_end'; 2874b402886SDaniel Henrique Barboza * 2884b402886SDaniel Henrique Barboza * - if 'dram_base' is higher than 3072 MiB, the FDT will be 2894b402886SDaniel Henrique Barboza * put at 'dram_end'. 290bc2c0153SDaniel Henrique Barboza * 291bc2c0153SDaniel Henrique Barboza * The FDT is fdt_packed() during the calculation. 292bc2c0153SDaniel Henrique Barboza */ 2934b402886SDaniel Henrique Barboza uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, 2944b402886SDaniel Henrique Barboza MachineState *ms) 29566b1205bSAtish Patra { 2964b402886SDaniel Henrique Barboza int ret = fdt_pack(ms->fdt); 2974b402886SDaniel Henrique Barboza hwaddr dram_end, temp; 298909f7da6SDaniel Henrique Barboza int fdtsize; 29966b1205bSAtish Patra 300909f7da6SDaniel Henrique Barboza /* Should only fail if we've built a corrupted tree */ 301909f7da6SDaniel Henrique Barboza g_assert(ret == 0); 302909f7da6SDaniel Henrique Barboza 3034b402886SDaniel Henrique Barboza fdtsize = fdt_totalsize(ms->fdt); 30466b1205bSAtish Patra if (fdtsize <= 0) { 30566b1205bSAtish Patra error_report("invalid device-tree"); 30666b1205bSAtish Patra exit(1); 30766b1205bSAtish Patra } 30866b1205bSAtish Patra 30966b1205bSAtish Patra /* 3104b402886SDaniel Henrique Barboza * A dram_size == 0, usually from a MemMapEntry[].size element, 3114b402886SDaniel Henrique Barboza * means that the DRAM block goes all the way to ms->ram_size. 3124b402886SDaniel Henrique Barboza */ 3134b402886SDaniel Henrique Barboza dram_end = dram_base; 3144b402886SDaniel Henrique Barboza dram_end += dram_size ? MIN(ms->ram_size, dram_size) : ms->ram_size; 3154b402886SDaniel Henrique Barboza 3164b402886SDaniel Henrique Barboza /* 31766b1205bSAtish Patra * We should put fdt as far as possible to avoid kernel/initrd overwriting 31866b1205bSAtish Patra * its content. But it should be addressable by 32 bit system as well. 319ec2c62daSAlistair Francis * Thus, put it at an 2MB aligned address that less than fdt size from the 3201a475d39SAtish Patra * end of dram or 3GB whichever is lesser. 32166b1205bSAtish Patra */ 322faee5441SDylan Jhong temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; 323bc2c0153SDaniel Henrique Barboza 324bc2c0153SDaniel Henrique Barboza return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); 325bc2c0153SDaniel Henrique Barboza } 326bc2c0153SDaniel Henrique Barboza 327bc2c0153SDaniel Henrique Barboza /* 328bc2c0153SDaniel Henrique Barboza * 'fdt_addr' is received as hwaddr because boards might put 329bc2c0153SDaniel Henrique Barboza * the FDT beyond 32-bit addressing boundary. 330bc2c0153SDaniel Henrique Barboza */ 331bc2c0153SDaniel Henrique Barboza void riscv_load_fdt(hwaddr fdt_addr, void *fdt) 332bc2c0153SDaniel Henrique Barboza { 333bc2c0153SDaniel Henrique Barboza uint32_t fdtsize = fdt_totalsize(fdt); 33466b1205bSAtish Patra 33566b1205bSAtish Patra /* copy in the device tree */ 33666b1205bSAtish Patra qemu_fdt_dumpdtb(fdt, fdtsize); 33766b1205bSAtish Patra 33866b1205bSAtish Patra rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, 33966b1205bSAtish Patra &address_space_memory); 34064c75db3SJason A. Donenfeld qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, 34164c75db3SJason A. Donenfeld rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); 34266b1205bSAtish Patra } 34366b1205bSAtish Patra 34478936771SAlistair Francis void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, 34578936771SAlistair Francis hwaddr rom_size, uint32_t reset_vec_size, 34678936771SAlistair Francis uint64_t kernel_entry) 347dc144fe1SAtish Patra { 348dc144fe1SAtish Patra struct fw_dynamic_info dinfo; 349dc144fe1SAtish Patra size_t dinfo_len; 350dc144fe1SAtish Patra 35178936771SAlistair Francis if (sizeof(dinfo.magic) == 4) { 35278936771SAlistair Francis dinfo.magic = cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE); 35378936771SAlistair Francis dinfo.version = cpu_to_le32(FW_DYNAMIC_INFO_VERSION); 35478936771SAlistair Francis dinfo.next_mode = cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S); 35578936771SAlistair Francis dinfo.next_addr = cpu_to_le32(kernel_entry); 35678936771SAlistair Francis } else { 35778936771SAlistair Francis dinfo.magic = cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE); 35878936771SAlistair Francis dinfo.version = cpu_to_le64(FW_DYNAMIC_INFO_VERSION); 35978936771SAlistair Francis dinfo.next_mode = cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S); 36078936771SAlistair Francis dinfo.next_addr = cpu_to_le64(kernel_entry); 36178936771SAlistair Francis } 362dc144fe1SAtish Patra dinfo.options = 0; 363dc144fe1SAtish Patra dinfo.boot_hart = 0; 364dc144fe1SAtish Patra dinfo_len = sizeof(dinfo); 365dc144fe1SAtish Patra 366dc144fe1SAtish Patra /** 367dc144fe1SAtish Patra * copy the dynamic firmware info. This information is specific to 368dc144fe1SAtish Patra * OpenSBI but doesn't break any other firmware as long as they don't 369dc144fe1SAtish Patra * expect any certain value in "a2" register. 370dc144fe1SAtish Patra */ 371dc144fe1SAtish Patra if (dinfo_len > (rom_size - reset_vec_size)) { 372dc144fe1SAtish Patra error_report("not enough space to store dynamic firmware info"); 373dc144fe1SAtish Patra exit(1); 374dc144fe1SAtish Patra } 375dc144fe1SAtish Patra 376dc144fe1SAtish Patra rom_add_blob_fixed_as("mrom.finfo", &dinfo, dinfo_len, 377dc144fe1SAtish Patra rom_base + reset_vec_size, 378dc144fe1SAtish Patra &address_space_memory); 379dc144fe1SAtish Patra } 380dc144fe1SAtish Patra 381a8259b53SAlistair Francis void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, 3823ed2b8acSAlistair Francis hwaddr start_addr, 38378936771SAlistair Francis hwaddr rom_base, hwaddr rom_size, 38478936771SAlistair Francis uint64_t kernel_entry, 3856934f15bSDaniel Henrique Barboza uint64_t fdt_load_addr) 38643cf723aSAtish Patra { 38743cf723aSAtish Patra int i; 3888590f536SAtish Patra uint32_t start_addr_hi32 = 0x00000000; 389faee5441SDylan Jhong uint32_t fdt_load_addr_hi32 = 0x00000000; 39043cf723aSAtish Patra 3913ed2b8acSAlistair Francis if (!riscv_is_32bit(harts)) { 3928590f536SAtish Patra start_addr_hi32 = start_addr >> 32; 393faee5441SDylan Jhong fdt_load_addr_hi32 = fdt_load_addr >> 32; 39478936771SAlistair Francis } 39543cf723aSAtish Patra /* reset vector */ 39666b1205bSAtish Patra uint32_t reset_vec[10] = { 397dc144fe1SAtish Patra 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */ 398dc144fe1SAtish Patra 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */ 39943cf723aSAtish Patra 0xf1402573, /* csrr a0, mhartid */ 40078936771SAlistair Francis 0, 40178936771SAlistair Francis 0, 40243cf723aSAtish Patra 0x00028067, /* jr t0 */ 40343cf723aSAtish Patra start_addr, /* start: .dword */ 4048590f536SAtish Patra start_addr_hi32, 40566b1205bSAtish Patra fdt_load_addr, /* fdt_laddr: .dword */ 406faee5441SDylan Jhong fdt_load_addr_hi32, 407dc144fe1SAtish Patra /* fw_dyn: */ 40843cf723aSAtish Patra }; 4093ed2b8acSAlistair Francis if (riscv_is_32bit(harts)) { 41078936771SAlistair Francis reset_vec[3] = 0x0202a583; /* lw a1, 32(t0) */ 41178936771SAlistair Francis reset_vec[4] = 0x0182a283; /* lw t0, 24(t0) */ 41278936771SAlistair Francis } else { 41378936771SAlistair Francis reset_vec[3] = 0x0202b583; /* ld a1, 32(t0) */ 41478936771SAlistair Francis reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ 41578936771SAlistair Francis } 41643cf723aSAtish Patra 41732c435a1SAlistair Francis if (!harts->harts[0].cfg.ext_icsr) { 41832c435a1SAlistair Francis /* 41932c435a1SAlistair Francis * The Zicsr extension has been disabled, so let's ensure we don't 42032c435a1SAlistair Francis * run the CSR instruction. Let's fill the address with a non 42132c435a1SAlistair Francis * compressed nop. 42232c435a1SAlistair Francis */ 42332c435a1SAlistair Francis reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */ 42432c435a1SAlistair Francis } 42532c435a1SAlistair Francis 42643cf723aSAtish Patra /* copy in the reset vector in little_endian byte order */ 42766b1205bSAtish Patra for (i = 0; i < ARRAY_SIZE(reset_vec); i++) { 42843cf723aSAtish Patra reset_vec[i] = cpu_to_le32(reset_vec[i]); 42943cf723aSAtish Patra } 43043cf723aSAtish Patra rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), 43143cf723aSAtish Patra rom_base, &address_space_memory); 43278936771SAlistair Francis riscv_rom_copy_firmware_info(machine, rom_base, rom_size, sizeof(reset_vec), 433dc144fe1SAtish Patra kernel_entry); 43443cf723aSAtish Patra } 435ad40be27SYifei Jiang 436ad40be27SYifei Jiang void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr) 437ad40be27SYifei Jiang { 438ad40be27SYifei Jiang CPUState *cs; 439ad40be27SYifei Jiang 440ad40be27SYifei Jiang for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { 441ad40be27SYifei Jiang RISCVCPU *riscv_cpu = RISCV_CPU(cs); 442ad40be27SYifei Jiang riscv_cpu->env.kernel_addr = kernel_addr; 443ad40be27SYifei Jiang riscv_cpu->env.fdt_addr = fdt_addr; 444ad40be27SYifei Jiang } 445ad40be27SYifei Jiang } 446a5b0249dSSunil V L 447a5b0249dSSunil V L void riscv_setup_firmware_boot(MachineState *machine) 448a5b0249dSSunil V L { 449a5b0249dSSunil V L if (machine->kernel_filename) { 450a5b0249dSSunil V L FWCfgState *fw_cfg; 451a5b0249dSSunil V L fw_cfg = fw_cfg_find(); 452a5b0249dSSunil V L 453a5b0249dSSunil V L assert(fw_cfg); 454a5b0249dSSunil V L /* 455a5b0249dSSunil V L * Expose the kernel, the command line, and the initrd in fw_cfg. 456a5b0249dSSunil V L * We don't process them here at all, it's all left to the 457a5b0249dSSunil V L * firmware. 458a5b0249dSSunil V L */ 459a5b0249dSSunil V L load_image_to_fw_cfg(fw_cfg, 460a5b0249dSSunil V L FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, 461a5b0249dSSunil V L machine->kernel_filename, 462a5b0249dSSunil V L true); 463a5b0249dSSunil V L load_image_to_fw_cfg(fw_cfg, 464a5b0249dSSunil V L FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, 465a5b0249dSSunil V L machine->initrd_filename, false); 466a5b0249dSSunil V L 467a5b0249dSSunil V L if (machine->kernel_cmdline) { 468a5b0249dSSunil V L fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 469a5b0249dSSunil V L strlen(machine->kernel_cmdline) + 1); 470a5b0249dSSunil V L fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, 471a5b0249dSSunil V L machine->kernel_cmdline); 472a5b0249dSSunil V L } 473a5b0249dSSunil V L } 474a5b0249dSSunil V L } 475