为什么TF-A的代码仍然能够在它自己把某个地址空间设为“不可执行”后继续运行?
环境:
- 异常等级: EL3 (AArch64)
- 组件: ARM Trusted Firmware(TF-A) - BL2/BL31
- 平台: Arm FVP / Base_Revc_2xAEMvA / Bare Metal Debug /ARMAEM-A_MP_0 [ Arm Development Studio ]
观察:
我在ARMv8-A架构的内存寻址/转换规则上观察到一些奇怪的现象。尽管翻译表明确将该内存区域标记为Execute-Never,CPU仍然从该区域获取指令并执行,且没有触发异常。我是一个硬件背景有限的学生,在实习期间学习TF-A并进行移植。非常想知道其中的原因。
使用的代码: ARM Trusted Firmware v2.13
Github: github.com/.../arm-trusted-firmware
以Development Studio中运行BL31为例(在bl31_setup的起点设置断点)
技术证据(通过调试器验证):
SCTLR_EL3: M=1(MMU已启用),WXN=1(写入即禁止执行),I=1(指令缓存已启用)。
BL31的代码加载地址:0x04003000
翻译表项(L3描述符):0x00400000_04003743
物理地址:通过TTBR0_EL3走查验证。(0x04034600 -> 0x04035003 -> 0x04037003 -> 0x00400000_04003743)
属性:AP[2:1]=0x1(读/写),XN=1(执行禁止),AF=1,SH=0x3(内部可共享),NS=0,AttrIndx = 0x0(见MAIR_EL3)
MAIR_EL3:0x4404FF(Attr0 = 0xFF,Normal Memory)
同步:DSB SY + ISB
PC(程序计数器)确认从地址0x04003000的 BL31第一条指令处执行。
问题:
上述证据应指向一个结论:它不应执行BL31代码并会报告错误。然而,实际执行流程似乎并未中断。
在我看来,应该会导致 “ESR_EL3 = 0x8600000F”,意思是:
“在异常等级不变的情况下发生指令中止。用于由指令访问引发的MMU故障,以及同步外部中止,包括同步的奇偶或ECC错误。不用于调试相关异常。”
+
“权限错误,等级3”。
当我在真实的FPGA上使用类似代码进行测试,在BL1末尾做一些修改,使其在EL3级别执行BL2(但执行的不是bl2_el3_entrypoint.S,而是bl2_entrypoint.S)时,会在跳转到BL2的第一条指令时抛出错误,ESR_EL3寄存器显示“权限错误,等级3”。
如果在BL1末尾添加禁用MMU的指令(将SCTLR_EL3.M_BIT设为0),并将官方代码arm_bl2_plat_arch_setup改为使用enable_mmu_el3(0) 来启用MMU,那么在FPGA上就能正常运行并启动UEFI。(在这个真实测试中,我使用DDR而非SRAM,因此BL2和 BL31也在解析后被放置在这里。)
求助请求:
上述内容超出我的理解范围;甚至我的实习导师也不清楚背后的原因。因此,我需要本论坛的专家提供帮助。
参考资料:
DDI0487M_a_a-profile_architecture_reference_manual.pdf
ARM Development Studio@Docs(如Docs/ARM_A/xhtml/AArch64-esr_el3.html)
armv8_a_address_translation version1.1
Development Studio中 BL31代码地址对应的页表截图
解决方案
以Development Studio中运行BL31为例(在bl31_setup的起点设置断点)
技术证据(通过调试器验证):
SCTLR_EL3: M=1(MMU已启用),WXN=1(写入即禁止执行),I=1(指令缓存已启用)。
上面的SCTLR_EL3.M对我来说看起来有点怪,因为bl31_setup() -> bl31_plat_arch_setup() 是为BL31固件在EL3启用MMU,而不是在bl31_setup() 调用的起始处。XN=1的体系结构规则在指令执行时应该被遵守。
不确定原始关切是否得到解决。有 FVP_Base_RevC-2xAEMvA Reference Stacks 可供下载并运行Arm Base FVP的整套软件栈。