aboutsummaryrefslogtreecommitdiff
path: root/hw/riscv/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/riscv/boot.c')
-rw-r--r--hw/riscv/boot.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index adb421b91b..4c6c101ff1 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -25,13 +25,19 @@
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/riscv/boot.h"
+#include "hw/riscv/boot_opensbi.h"
#include "elf.h"
+#include "sysemu/device_tree.h"
#include "sysemu/qtest.h"
+#include <libfdt.h>
+
#if defined(TARGET_RISCV32)
# define KERNEL_BOOT_ADDRESS 0x80400000
+#define fw_dynamic_info_data(__val) cpu_to_le32(__val)
#else
# define KERNEL_BOOT_ADDRESS 0x80200000
+#define fw_dynamic_info_data(__val) cpu_to_le64(__val)
#endif
void riscv_find_and_load_firmware(MachineState *machine,
@@ -155,3 +161,104 @@ hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
return *start + size;
}
+
+uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
+{
+ uint32_t temp, fdt_addr;
+ hwaddr dram_end = dram_base + mem_size;
+ int fdtsize = fdt_totalsize(fdt);
+
+ if (fdtsize <= 0) {
+ error_report("invalid device-tree");
+ exit(1);
+ }
+
+ /*
+ * We should put fdt as far as possible to avoid kernel/initrd overwriting
+ * its content. But it should be addressable by 32 bit system as well.
+ * Thus, put it at an aligned address that less than fdt size from end of
+ * dram or 4GB whichever is lesser.
+ */
+ temp = MIN(dram_end, 4096 * MiB);
+ fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
+
+ fdt_pack(fdt);
+ /* copy in the device tree */
+ qemu_fdt_dumpdtb(fdt, fdtsize);
+
+ rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr,
+ &address_space_memory);
+
+ return fdt_addr;
+}
+
+void riscv_rom_copy_firmware_info(hwaddr rom_base, hwaddr rom_size,
+ uint32_t reset_vec_size, uint64_t kernel_entry)
+{
+ struct fw_dynamic_info dinfo;
+ size_t dinfo_len;
+
+ dinfo.magic = fw_dynamic_info_data(FW_DYNAMIC_INFO_MAGIC_VALUE);
+ dinfo.version = fw_dynamic_info_data(FW_DYNAMIC_INFO_VERSION);
+ dinfo.next_mode = fw_dynamic_info_data(FW_DYNAMIC_INFO_NEXT_MODE_S);
+ dinfo.next_addr = fw_dynamic_info_data(kernel_entry);
+ dinfo.options = 0;
+ dinfo.boot_hart = 0;
+ dinfo_len = sizeof(dinfo);
+
+ /**
+ * copy the dynamic firmware info. This information is specific to
+ * OpenSBI but doesn't break any other firmware as long as they don't
+ * expect any certain value in "a2" register.
+ */
+ if (dinfo_len > (rom_size - reset_vec_size)) {
+ error_report("not enough space to store dynamic firmware info");
+ exit(1);
+ }
+
+ rom_add_blob_fixed_as("mrom.finfo", &dinfo, dinfo_len,
+ rom_base + reset_vec_size,
+ &address_space_memory);
+}
+
+void riscv_setup_rom_reset_vec(hwaddr start_addr, hwaddr rom_base,
+ hwaddr rom_size, uint64_t kernel_entry,
+ uint32_t fdt_load_addr, void *fdt)
+{
+ int i;
+ uint32_t start_addr_hi32 = 0x00000000;
+
+ #if defined(TARGET_RISCV64)
+ start_addr_hi32 = start_addr >> 32;
+ #endif
+ /* reset vector */
+ uint32_t reset_vec[10] = {
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */
+ 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */
+ 0xf1402573, /* csrr a0, mhartid */
+#if defined(TARGET_RISCV32)
+ 0x0202a583, /* lw a1, 32(t0) */
+ 0x0182a283, /* lw t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+ 0x0202b583, /* ld a1, 32(t0) */
+ 0x0182b283, /* ld t0, 24(t0) */
+#endif
+ 0x00028067, /* jr t0 */
+ start_addr, /* start: .dword */
+ start_addr_hi32,
+ fdt_load_addr, /* fdt_laddr: .dword */
+ 0x00000000,
+ /* fw_dyn: */
+ };
+
+ /* copy in the reset vector in little_endian byte order */
+ for (i = 0; i < ARRAY_SIZE(reset_vec); i++) {
+ reset_vec[i] = cpu_to_le32(reset_vec[i]);
+ }
+ rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
+ rom_base, &address_space_memory);
+ riscv_rom_copy_firmware_info(rom_base, rom_size, sizeof(reset_vec),
+ kernel_entry);
+
+ return;
+}