ARMv8架构下BL1内部跳转至0x8000等地址系统挂起问题咨询
问题描述
我是ARM架构初学者,想要从BL1内部跳转到其他地址(例如0x8000)执行,首先在bl1_entrypoint中调用bl_main之后添加了两行代码:
bl bl1_main mov x0, #0x8000 br x0
但运行时br指令会导致系统挂起。
之后我尝试在bl1_main.c中新增jump()函数,在进入bl1_main()后调用该函数:
void jump(void) { NOTICE("BL1: entering jump()...\n"); console_flush(); asm volatile ("mov x20, #0x8000\n"); asm volatile ("br x20\n"); }
经JTAG测试,代码同样在br指令处停滞。
如果我将跳转地址从0x8000改为0x0,BL1会循环重复启动,符合预期;如果改为jump()函数自身的地址(通过bl1.dump查得为0xb00),运行也符合预期。
我不太理解为什么无法跳转到更远的地址,除了0x8000外我还测试了0x5000,同样无法正常跳转。
我放在0x8000(或0x5000)处的代码是从rom_trampoline.o提取的类似跳转代码,仅将跳转目标改为0x0,我的目标是测试BL1跳转到0x8000后再跳回0x0、循环往复的效果。
通过JTAG调试器,我在jump()地址0xb00处设置断点,程序会停在该地址的movz x20, #0x8000指令处,下一条就是br x20指令。当我单步跳过这条br指令时,JTAG调试器会与FPGA目标断开连接,状态栏报错Error - Failed asm stepin ! - Unknown Error (0x00000002)。这也不符合预期,因为启动初始在地址0x0处我可以正常单步执行,且mov指令本身无风险。
我还测试了无BL1参与的两个trampoline跳转代码:在0x0处放跳转到0x8000的代码,在0x8000处放跳回0x0的代码,测试结果符合预期,两个地址的断点会交替触发、无限循环。
目前我对BL1跳转到BL2的机制还不太熟悉,希望了解上述场景无法正常运行的原因。
根因说明
- ARMv8架构下MMU启用后,所有内存地址访问都会先经过页表属性校验,可执行的代码段必须配置为可执行(XN位未置1)、特权等级匹配、内存类型为正常内存的属性,也就是TF-A中定义的
MT_CODE类型对应的配置。 - BL1在启动流程中会初始化MMU并配置默认的静态内存映射,默认映射仅覆盖BL1本身所在的地址区间、外设寄存器地址区间等必要区域,
0x5000、0x8000这类地址不在默认映射范围内,或者被配置为不可执行的MT_DEVICE/MT_MEMORY_RW属性。 - 当CPU执行
br指令跳转到未正确配置可执行属性的地址时,会触发指令预取中止异常,如果BL1未注册该异常的处理函数,就会直接挂死,表现为JTAG连接断开、系统停滞。 - 测试用的
0x0、0xb00地址都属于BL1默认的代码段映射范围,已经配置了MT_CODE可执行属性,所以跳转可以正常运行。 - 无BL1参与的场景下MMU未启用,CPU访问地址采用物理地址直接寻址,不需要经过页表属性校验,所以跳转可以正常执行。
内容的提问来源于stack exchange,提问作者bruin

