'How does QEMU risc-v put FDT address in register a1?

From RISC-V OpenSBI's source code and documents, in OpenSBI firmware a1 preserves FDT address from the prior booting stage, which I guess is QEMU if the following command is used to boot OpenSBI firmware:

qemu-system-riscv64 -M virt -m 256M -nographic -bios build/platform/generic/firmware/fw_payload.bin

and OpenSBI firmware is built with

make PLATFORM=generic CROSS_COMPILE=riscv64-linux-gnu-

fw_base.S in OpenSBI's source code will use the value of a1 to invoke fw_platform_init, which assumes a1 contains the FDT address.

My question is when and how a1 is set before fw_base.S



Solution 1:[1]

a1 is set by function riscv_setup_rom_reset_vec(...) which in qemu/hw/riscv/boot.c inserts some instructions to set FDT address:

    /* 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  */
        0,
        0,
        0x00028067,                  /*     jr     t0 */
        start_addr,                  /* start: .dword */
        start_addr_hi32,
        fdt_load_addr,               /* fdt_laddr: .dword */
        0x00000000,
                                     /* fw_dyn: */
    };
    if (riscv_is_32bit(harts)) {
        reset_vec[3] = 0x0202a583;   /*     lw     a1, 32(t0) */
        reset_vec[4] = 0x0182a283;   /*     lw     t0, 24(t0) */
    } else {
        reset_vec[3] = 0x0202b583;   /*     ld     a1, 32(t0) */
        reset_vec[4] = 0x0182b283;   /*     ld     t0, 24(t0) */
    }

When qemu runs, it will first jump to 0x1000 to execute these instructions and then jump to _start.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Xujie Shen