ARM64从EL2切换至EL1时.data段被清零的原因排查
RK3588(Radxa RockPi 5b)EL1模式下.data段被清零问题解决
问题背景
在Radxa RockPi 5b(RK3588)上通过Uboot启动裸机应用,启动命令为:
fatload mmc 1 0x8300000 rk3588.bin go 0x8300000
自定义printf函数在EL2下完全正常,但切换到EL1后出现异常:
- .rodata段数据(字符串字面量、立即数)可正常打印
- 局部变量可正常使用
- 全局变量(.data段)读取值全为0,直接访问.data段物理地址也返回0
切换EL1的汇编代码如下:
prepare_el1: ldr x0, =((1 << 31) | (1 << 41) | (1 << 40)) msr hcr_el2, x0 isb msr sctlr_el1, xzr isb ldr x0, =((7<<6) | 5) msr spsr_el2, x0 isb adr x0, entry_el1 msr elr_el2, x0 isb eret
临时重定位程序到其他地址后,.data段可正常访问,但未明确根本原因。
核心原因分析
问题出在HCR_EL2的TGE位配置以及EL1的内存访问模式:
- 你设置了HCR_EL2的
TGE位(bit41),这会让EL1进入Guest虚拟化模式 - 此时即使EL1关闭MMU(SCTLR_EL1.M=0),CPU访问的是中间物理地址(IPA),而非直接物理地址(PA)
- Uboot在EL2下的阶段2页表(用于Guest地址转换)未正确映射
0x8300000附近的.data段区域:要么该区域未被映射,要么属性被设为只读/清零,导致EL1访问时返回0 - 重定位到其他地址后,该地址不在阶段2页表的限制范围内,因此能正常访问.data段
解决步骤
方案1:关闭Guest模式(推荐)
修改HCR_EL2的配置,移除TGE位(bit41),让EL1运行在Non-Guest模式,此时关闭MMU时直接访问物理地址,和EL2的访问逻辑一致:
prepare_el1: ; 移除(1 << 41),保留RW位(bit31)和TWED位(bit40) ldr x0, =((1 << 31) | (1 << 40)) msr hcr_el2, x0 isb msr sctlr_el1, xzr isb ldr x0, =((7<<6) | 5) msr spsr_el2, x0 isb adr x0, entry_el1 msr elr_el2, x0 isb eret
方案2:配置EL2阶段2页表
如果你需要保留Guest模式,需在EL2中为.data段所在的物理地址范围配置阶段2页表,将其标记为:
- Normal Memory类型
- 读写权限
- 非执行属性
确保EL1的IPA能正确映射到对应的PA,且属性允许读写。
补充优化
建议在EL1中正确配置SCTLR_EL1,开启缓存以提升性能:
; 在EL1入口处配置SCTLR_EL1 mov x0, #(1 << 0) ; M位:开启MMU(如果需要) orr x0, x0, #(1 << 2) ; C位:开启数据缓存 orr x0, x0, #(1 << 12) ; I位:开启指令缓存 msr sctlr_el1, x0 isb
内容的提问来源于stack exchange,提问作者SONGYJ
相关产品推荐
相关产品推荐

