diff options
author | Jessica Clarke <jrtc27@jrtc27.com> | 2021-12-14 03:24:56 +0000 |
---|---|---|
committer | Alistair Francis <alistair.francis@wdc.com> | 2021-12-20 14:53:31 +1000 |
commit | 7e322a7f23a60b0e181b55ef722fdf390ec4e463 (patch) | |
tree | d987ce236bc938d81cb5bc247bd8ef655a70fbb7 /hw | |
parent | 0643c12e4bc021ce5cb06aa1bfa02d25d8386b61 (diff) | |
download | qemu-7e322a7f23a60b0e181b55ef722fdf390ec4e463.zip qemu-7e322a7f23a60b0e181b55ef722fdf390ec4e463.tar.gz qemu-7e322a7f23a60b0e181b55ef722fdf390ec4e463.tar.bz2 |
hw/riscv: Use load address rather than entry point for fw_dynamic next_addr
The original BBL boot method had the kernel embedded as an opaque blob
that was blindly jumped to, which OpenSBI implemented as fw_payload.
OpenSBI then implemented fw_jump, which allows the payload to be loaded
elsewhere, but still blindly jumps to a fixed address at which the
kernel is to be loaded. Finally, OpenSBI introduced fw_dynamic, which
allows the previous stage to inform it where to jump to, rather than
having to blindly guess like fw_jump, or embed the payload as part of
the build like fw_payload. When used with an opaque binary (i.e. the
output of objcopy -O binary), it matches the behaviour of the previous
methods. However, when used with an ELF, QEMU currently passes on the
ELF's entry point address, which causes a discrepancy compared with all
the other boot methods if that entry point is not the first instruction
in the binary.
This difference specific to fw_dynamic with an ELF is not apparent when
booting Linux, since its entry point is the first instruction in the
binary. However, FreeBSD has a separate ELF entry point, following the
calling convention used by its bootloader, that differs from the first
instruction in the binary, used for the legacy SBI entry point, and so
the specific combination of QEMU's default fw_dynamic firmware with
booting FreeBSD as an ELF rather than a raw binary does not work.
Thus, align the behaviour when loading an ELF with the behaviour when
loading a raw binary; namely, use the base address of the loaded kernel
in place of the entry point.
The uImage code is left as-is in using the U-Boot header's entry point,
since the calling convention for that entry point is the same as the SBI
one and it mirrors what U-Boot will do.
Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-Id: <20211214032456.70203-1-jrtc27@jrtc27.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/riscv/boot.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 519fa45..f672643 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -151,12 +151,19 @@ target_ulong riscv_load_kernel(const char *kernel_filename, target_ulong kernel_start_addr, symbol_fn_t sym_cb) { - uint64_t kernel_entry; + uint64_t kernel_load_base, kernel_entry; + /* + * NB: Use low address not ELF entry point to ensure that the fw_dynamic + * behaviour when loading an ELF matches the fw_payload, fw_jump and BBL + * behaviour, as well as fw_dynamic with a raw binary, all of which jump to + * the (expected) load address load address. This allows kernels to have + * separate SBI and ELF entry points (used by FreeBSD, for example). + */ if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, NULL, NULL, 0, + NULL, &kernel_load_base, NULL, NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { - return kernel_entry; + return kernel_load_base; } if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, |