QEMU模拟树莓派4(aarch64)UART异常排查求助
问题描述
按照某树莓派AArch64内核教程构建了kernel8.img,放入2024-07-04版本Raspios镜像后,用以下QEMU命令模拟Raspi4b时仅显示黑屏:
qemu-system-aarch64 \ -machine raspi4b \ -cpu cortex-a72 \ -smp 4 -m 2G \ -dtb bcm2711-rpi-4-b.dtb \ -drive format=raw,file=2024-07-04-raspios-bookworm-arm64-full.img
尝试添加-semihosting --semihosting-config enable=on,target=native或-serial mon:stdio参数无效;改用以下命令仍无输出:
qemu-system-aarch64 -M raspi4b \ -display none \ -serial null \ -serial stdio \ -kernel kernel8.img
通过GDB调试发现:加载raw二进制时未执行kernel_main,加载ELF则卡在uart_send发送首个字符环节。确认教程代码针对Raspi3b,其辅助及GPIO寄存器基址与Raspi4b不同,且代码在Raspi3b模拟下可正常运行。
求助:上述命令存在哪些问题?如何验证QEMU是否模拟UART及查看其输出?(要求模拟Raspi4b,不使用-machine virt)
解答
一、现有QEMU命令的问题
1. 第一个启动命令的核心问题
- 直接启动Raspios镜像时,镜像自带的bootloader会优先加载自身的
kernel8.img,而非你替换的版本。必须手动修改镜像中boot/config.txt文件,添加kernel=kernel8.img指定加载自定义内核,否则QEMU不会加载你的编译产物。 - 未指定
-kernel参数时,QEMU完全依赖镜像内的原生引导流程,Raspios的bootloader不会自动识别替换的内核文件。
2. 第二个启动命令的问题
- 代码针对Raspi3b编写,而Raspi4b的外设基址完全不同:Raspi3b外设基址为
0x3F000000,Raspi4b为0xFE000000。UART、GPIO的寄存器地址均基于该基址,硬编码的旧地址会导致UART初始化失败,进而卡在发送字符环节。 - 缺少指定Raspi4b的设备树文件(
-dtb bcm2711-rpi-4-b.dtb),虽QEMU模拟Raspi4b时部分兼容,但缺少DTB会影响外设初始化的正确性。
二、验证QEMU的UART模拟及查看输出的方法
1. 修正命令与代码,获取UART输出
- 先修改代码中的外设基址:将所有
0x3F000000开头的寄存器地址替换为0xFE000000(例如UART0基址从0x3F201000改为0xFE201000,GPIO基址从0x3F200000改为0xFE200000)。 - 使用修正后的启动命令(直接加载自定义内核):
该命令将UART输出直接重定向到终端,若代码修改正确,即可看到内核输出内容。qemu-system-aarch64 -M raspi4b \ -cpu cortex-a72 \ -smp 4 -m 2G \ -dtb bcm2711-rpi-4-b.dtb \ -kernel kernel8.img \ -display none \ -serial stdio
2. 验证QEMU的UART模拟功能
- 用官方Raspi4b的正常Raspios镜像启动,添加
-serial stdio参数,若能看到完整的系统启动日志,说明QEMU的UART模拟功能正常。 - 借助GDB断点调试:在
uart_init函数处设置断点,检查寄存器地址的写入值是否正确,确认GPIO引脚是否被配置为UART功能(Raspi4b的UART0引脚定义与Raspi3b一致,但寄存器地址不同)。
3. 绕过UART的快速调试方法
- 添加半宿主参数,修改代码使用半宿主API(如
sys_write0)输出内容,绕过硬件UART初始化问题,先验证内核是否能正常执行:
若半宿主输出正常,说明内核可正常运行,问题确实出在UART硬件地址不匹配。qemu-system-aarch64 -M raspi4b \ -cpu cortex-a72 \ -smp 4 -m 2G \ -kernel kernel8.img \ -display none \ -semihosting --semihosting-config enable=on,target=native
内容的提问来源于stack exchange,提问作者k1r1t0
相关产品推荐
相关产品推荐

