Reputation: 3628
How does the default bios (OpenSBI) in qemu-system-riscv
pass the Device Tree Blob to a guest?
I can see from the documentation for qemu-system-arm
'virt' platform that QEMU passes the address of the Device Tree Blob (dtb) to guests in r0 when using the Linux Boot Protocol, or at a hard-coded address for bare-metal guest kernels.
The source code in hw/riscv/virt.c
within the QEMU repo shows that a Device Tree Blob is generated for the RISC-V 'virt' platform (Via the create_fdt
function), however none of the documentation I can find describes how this is passed to the guest. Any help is appreciated.
Upvotes: 4
Views: 3225
Reputation: 509
How does the default bios (OpenSBI) in qemu-system-riscv pass the Device Tree Blob to a guest?
It doesn't. The mechanism is similar to all qemu "virt" machines. It's qemu responsibility to generate and load compiled dtb in this particular case.
Actually you can rely only on RAM and FLASH addresses all other information should be taken from Qemu generated DTB.
Citing from qemu/hw/riscv/boot.c:
riscv_load_fdt /* * 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 16MB aligned address that less than fdt size from the * end of dram or 3GB whichever is lesser. */
OpenSBI expects the fdt addr passed via "a1", unless you compiled OpenSBI with dtb build-in FW_FDT_PATH, see:
opensbi/docs/firmware/fw.md
opensbi/docs/firmware/fw_payload.md
The address of fdt cannot be set directly (OpenSBI v0.9) currently during compile time.
The address of fdt passed to next mode however can be changed with FW_PAYLOAD_FDT_ADDR.
So FW_FDT_PATH and FW_PAYLOAD_FDT_ADDR affect fdt respectively.
Qemu setups reset vector in riscv_setup_rom_reset_vec and among other things sets fdt_load_addr
What happens next depends on OpenSBI mode (dtb can be reallocated by OpenSBI), but you can look at sbi_hart_switch_mode in opensbi/sbi_hart.c:
register unsigned long a0 asm("a0") = arg0;
register unsigned long a1 asm("a1") = arg1;
__asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
__builtin_unreachable();
Finally see https://www.sifive.com/blog/all-aboard-part-6-booting-a-risc-v-linux-kernel for RISC-V kernel boot proccess:
Early Boot in Linux
When Linux boots, it expects the system to be in the following state:
a0 contains a unique per-hart ID. We currently map these to Linux CPU IDs,
so they're expected to be contiguous and close to 0.
a1 contains a pointer to the device tree, represented as a binary flattened
device tree (DTB).
Update:
For example you can see the following with:
build-qemu/qemu-system-riscv64 -machine virt -m 2G -nographic -bios opensbi/build/platform/generic/firmware/fw_jump.bin
And OpenSBI compiled with FW_OPTIONS=0x2:
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000002000000-0x000000000200ffff (I)
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff ()
Domain0 Region02 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200000
Domain0 Next Arg1 : 0x0000000082200000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Upvotes: 5