带OpenSBI的Linux内核跳转至0x80100000后无法正常启动排查
问题:跳转加载OpenSBI Payload后内核无法执行的原因分析
问题背景
我通过以下命令构建OpenSBI payload与Linux内核镜像:
make PLATFORM=generic FW_FDT_PATH=$WORKLOAD_BUILD_ENV_HOME/dts/build/xiangshan.dtb FW_PAYLOAD_PATH=$RISCV_LINUX_HOME/arch/riscv/boot/Image
获取fw_payload.bin后,我编写了简单的跳转逻辑,将该payload加载到新地址0x80100000并跳转执行,预期内核能正常启动。但在模拟器运行时,仅能看到OpenSBI的输出,无法看到内核输出;而直接运行fw_payload.bin时,内核可正常运行。
我的链接脚本(linker.lds)
OUTPUT_ARCH( "riscv" ) ENTRY( jump_payload ) SECTIONS { . = ALIGN(4); begin = .; .header.code : { *(.header.code) } end = .; . = begin + 0x100000; .payload : { PROVIDE(_payload_start = .); *(.payload) . = ALIGN(8); PROVIDE(_payload_end = .); } }
跳转逻辑汇编代码
.section ".header.code", "ax", %progbits .globl jump_payload jump_payload: li t0, 0x80100000 jr t0 add t0, x0,x0 .section ".payload", "ax", %progbits .align 4 .global payload_bin payload_bin: .incbin PAYLOAD_PATH
原因分析与解决建议
1. OpenSBI Payload是位置相关代码
fw_payload.bin默认是位置相关代码(PDC),编译时基于RISCV通用平台的默认起始地址0x80000000生成。当你把它加载到0x80100000后,代码中的绝对地址引用(比如全局变量、函数跳转地址)依然指向0x80000000附近的原始位置,执行时会访问错误地址,导致内核初始化失败。
2. 缺少正确的启动参数传递
直接运行fw_payload.bin时,模拟器会按照RISCV启动规范设置初始环境:
a0寄存器:当前hart的ID(通常为0)a1寄存器:设备树(DTB)的物理地址a2寄存器:预留参数(通常为0)
而你的跳转逻辑只是单纯跳转到0x80100000,没有设置这些寄存器参数,OpenSBI无法完成正确的初始化流程,自然无法启动内核。
3. 链接脚本与实际加载地址不匹配
你的链接脚本将.payload段放在begin + 0x100000的位置,但如果整个镜像的加载起始地址不是0x80000000,那么.payload的实际加载地址会和预期的0x80100000不符;再加上fw_payload.bin内部地址已固定,双重地址偏移会彻底打乱执行流程。
解决建议
- 编译位置无关的OpenSBI Payload:编译时添加
FW_PAYLOAD_ALIGN=0x100000参数,或启用位置无关编译选项,让fw_payload.bin支持任意地址加载执行。 - 传递正确的启动寄存器参数:在跳转至
0x80100000前,设置好a0、a1、a2寄存器的值,匹配模拟器的初始启动环境。 - 保持加载地址与编译地址一致:如果必须使用位置相关版本,要么将
fw_payload.bin加载到默认的0x80000000地址,要么重新编译OpenSBI时指定平台加载地址为0x80100000。
内容的提问来源于stack exchange,提问作者XM Zg
相关产品推荐
相关产品推荐

