qemu-system-riscv64 virt平台ROM代码启动分析
- RISC-V
- 2024-04-12
- 379热度
- 0评论
为什么下面qemu启动elf时,text地址要从0x80000000开始?
qemu-system-riscv64 -machine virt -cpu c910v -nographic -smp 1 -bios none -kernel xxx.elf
从memory mapping角度
下面是qemu virt平台的memory mapping
https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
static const MemMapEntry virt_memmap[] = {
[VIRT_DEBUG] = { 0x0, 0x100 },
[VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 },
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
[VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) },
[VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) },
[VIRT_UART0] = { 0x10000000, 0x100 },
[VIRT_VIRTIO] = { 0x10001000, 0x1000 },
[VIRT_FW_CFG] = { 0x10100000, 0x18 },
[VIRT_FLASH] = { 0x20000000, 0x4000000 },
[VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE },
[VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE },
[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },
[VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
[VIRT_DRAM] = { 0x80000000, 0x0 },
};
- MROM: 是virt平台的rom化启动代码,所以qemu启动时会默认从该地址开始运行。
- DRAM:DRAM的映射地址为0x80000000,从这里大概能猜出来elf的地址为什么要那么设置了。
qemu启动时,使用-kernel参数带上elf,与实际硬件平台的差异相比,就会默认根据elf的程序入口地址将elf加载到对应DRAM内存上,如下图所示。
跟踪启动流程
上面是启动qemu后的初始化代码,连接上gdb后,可以使用layout asm查看汇编指令,并且使用si可进行单步调试。可见qemu启动就会跳转到0x1000进行执行,即对应上面memory mapping的VIRT_MROM。上面代码主要逻辑是,跳转到t0,传递参数a0/a1/a2。下面来看看具体的值。
0x0000000000001000: 97 02 00 00 auipc t0,0x0 //t0=PC+0x0<<12,即t0=0x1000
0x0000000000001004: 13 86 82 02 addi a2,t0,40//a2=t0+40,即0x1028
0x0000000000001008: 73 25 40 f1 csrr a0,mhartid//a0=0
0x000000000000100c: 83 b5 02 02 ld a1,32(t0) //a1=t0+0x20地址的值即0x1020地址处
ld是加载8字节,由于是小端,所以a1=0x87e00000
0x0000000000001010: 83 b2 82 01 ld t0,24(t0) //t0=t0+0x18地址的值即0x1018地址处
同理ld是加载8字节且小端,所有t0=80000000
0x0000000000001014: 67 80 02 00 jr t0 //跳转到0x80000000执行。