QEMU中DTB加载位置咨询——AArch64 Linux Bootloader开发
问题分析与解决
核心问题1:QEMU启动命令错误
你当前使用-kernel pflash.bin的方式是错误的——-kernel参数用于让QEMU直接将指定内核镜像加载到RAM,而非从pflash分区读取内核。正确的启动命令应通过-drive指定pflash设备:
qemu-system-aarch64 -nographic -machine virt -cpu cortex-a72 -drive if=pflash,file=pflash.bin,format=raw -initrd initramfs.cpio.gz -serial mon:stdio -m 2G -smp 4
核心问题2:内核未从pflash复制到RAM
你的pflash布局中,内核被写入到50MB偏移处(seek=50),对应物理地址为FLASH_START + 50*1024*1024 = 0x000100000 + 0x3200000 = 0x003300000。但Bootloader直接跳转到RAM地址0x43280000,此时内核仍在只读的pflash MMIO区域,无法直接执行——必须先将内核镜像复制到RAM的正确加载地址。
AArch64 Linux内核的加载规则:
- 未压缩的
Image镜像必须加载到物理地址0x80080000,入口点即为该地址。 - 压缩的
zImage/Image.gz可加载到RAM中任意空闲区域(需避开Bootloader、栈、DTB等),入口点为其加载地址。
关于DTB的位置
使用自定义Bootloader从pflash启动时,QEMU不会自动加载DTB到RAM,需手动处理:
- 获取QEMU virt平台DTB:可从Linux内核源码
arch/arm64/boot/dts/qemu/virt.dtb路径获取,或通过qemu-system-aarch64 -machine virt,dumpdtb=virt.dtb命令导出。 - 将DTB写入pflash空闲区域(比如
seek=100,即100MB偏移处),对应物理地址0x000100000 + 0x6400000 = 0x006500000。 - 在Bootloader中,将DTB从pflash复制到RAM空闲区域(比如
RAM_START + 0x100000即0x40100000,需避开栈和Bootloader代码),再将该地址存入x0寄存器传递给内核。
Bootloader需补充的关键操作
当前Bootloader缺少核心步骤,以下是修改思路(示例汇编代码):
.section ".text.startup" .global _start STACK_TOP = 0x40800000 // RAM_START + 8MB,避开前8MB的DTB/Bootloader区域 KERNEL_PFLASH_ADDR = 0x003300000 // FLASH_START + 50MB KERNEL_RAM_ADDR = 0x80080000 KERNEL_SIZE = 0x800000 // 假设内核大小不超过8MB DTB_PFLASH_ADDR = 0x006500000 // FLASH_START + 100MB DTB_RAM_ADDR = 0x40100000 DTB_SIZE = 0x10000 // 假设DTB大小不超过64KB _start: // 设置栈 mov sp, #STACK_TOP // 复制内核从pflash到RAM ldr x0, =KERNEL_PFLASH_ADDR ldr x1, =KERNEL_RAM_ADDR ldr x2, =KERNEL_SIZE bl memcpy // 复制DTB从pflash到RAM ldr x0, =DTB_PFLASH_ADDR ldr x1, =DTB_RAM_ADDR ldr x2, =DTB_SIZE bl memcpy // 准备内核启动参数:x0=DTB地址,x1/x2保留为0 ldr x0, =DTB_RAM_ADDR mov x1, #0 mov x2, #0 // 跳转到内核入口 ldr x3, =KERNEL_RAM_ADDR br x3 // 简单内存复制函数:源地址x0,目标地址x1,长度x2 memcpy: cbz x2, memcpy_done ldrb w3, [x0], #1 strb w3, [x1], #1 sub x2, x2, #1 b memcpy memcpy_done: ret
额外注意事项
- 确保链接脚本
link.ld将Bootloader代码加载到TEXT_START(0x40080000),且栈的位置不会覆盖Bootloader、内核和DTB。 - 若使用
initrd,需将initramfs复制到RAM后,在DTB的chosen节点添加initrd-start和initrd-end属性,以此告知内核initramfs的位置。
内容的提问来源于stack exchange,提问作者Vladouch
相关产品推荐
相关产品推荐

