*Environment:*
- *Exception Level:* EL3 (AArch64) - *Component:* [image: 微信图片_20260304154227_69_553.png] ARM Trusted Firmware (TF-A) - BL2/BL31 - *Platform:* Arm FVP / Base_Revc_2xAEMvA / Bare Metal Debug /ARMAEM-A_MP_0 [ Arm Development Studio ]
*Observation:* I am observing a strange thing of the ARMv8-A architecture's memory translation rules. Despite the translation table explicitly marking the memory region as Execute-Never, the CPU continues to fetch and execute instructions from this region without triggering an exception. I'm a student with limited hardware background, and I'm learning TF-A and doing porting during my internship. I'd really like to know the reason behind this.
*Code I use:* ARM Trusted Firmware v2.13
*Github:* github.com/.../arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware
*Take the case of running BL31 in Development Studio as an example. (Breakpoint at the beginning of bl31_setup)*
*Technical Evidence (Verified via Debugger):* SCTLR_EL3: M=1 (MMU enabled), WXN=1 (Write implies execute-never), I=1 (Instruction Cache enabled).
BL31’s code is loaded at: 0x04003000
Translation Table Entry (L3 Descriptor): 0x00400000_04003743
Physical Address: Verified via TTBR0_EL3 walk. (0x04034600 -> 0x04035003 -> 0x04037003 -> 0x00400000_04003743)
Attributes: AP[2:1]=0x1 (Read/Write), XN=1 (Execute-Never), AF=1, SH=0x3 (Inner Shareable), NS=0, AttrIndx = 0x0 (See the MAIR_EL3)
MAIR_EL3: 0x4404FF (Attr0 = 0xFF, Normal Memory).
Synchronization Performed: DSB SY + ISB
The PC (Program Counter) is confirmed to executing from the first instruction of BL31 code at address 0x04003000.
*The Problem:* This evidence should point to one conclusion: it cannot execute the BL31 code and will report an error. However, the execution flow remains uninterrupted.
From my point of view, it should cause "ESR_EL3 = 0x8600000F", which means: "Instruction Abort taken without a change in Exception level. Used for MMU faults generated by instruction accesses and synchronous External aborts, including synchronous parity or ECC errors. Not used for debug-related exceptions." + "Permission Fault, level 3".
As I test on a real fpga by using similar code by making some changes at the end of BL1 so that it would execute BL2 at level EL3 (but instead of bl2_el3_entrypoint.S, it would execute bl2_entrypoint.S). In this case, it throws an error when it jumps to the first instruction of BL2, and the ESR_EL3 register displays "Permission Fault, level 3".
If I add the instruction to disable the MMU (setting SCTLR_EL3.M_BIT to 0) at the end of BL1, and change the function to enable the MMU in the official code "arm_bl2_plat_arch_setup" to use "enable_mmu_el3(0)", it can run normally on the FPGA and bring up the UEFI. (In this real-world test, I used DDR instead of SRAM, so BL2 and BL31 were also placed here after being parsed.)
*Request for Help:* The above content is beyond my comprehension; even my internship supervisor doesn't understand the reasoning behind it. Therefore, I need help from the experts on this forum.
*Reference:* DDI0487M_a_a-profile_architecture_reference_manual.pdf ARM Development Studio@Docs (such as Docs/ARM_A/xhtml/AArch64-esr_el3.html) armv8_a_address_translation version1.1
Hi Hughard,
I'm not entirely sure I understand where the problem is so I'd like to ask a few follow ups:
1. What code and platform are you running? Is it the unmodified upstream for PLAT=fvp and PLAT=arm_fpga? Do you have any local changes? 2. Where are you expecting the exception to happen? All BL images should be jumped to with MMU off so the WXN bit should have no effect.
Thanks, Boyan ________________________________ From: Hughard via TF-A tf-a@lists.trustedfirmware.org Sent: 06 March 2026 12:28 PM To: tf-a@lists.trustedfirmware.org tf-a@lists.trustedfirmware.org Subject: [TF-A] Why can TF-A code still run in an address space that it itself has set to be "Execute-Never"?
Environment:
* Exception Level: EL3 (AArch64) * Component: [微信图片_20260304154227_69_553.png] ARM Trusted Firmware (TF-A) - BL2/BL31 * Platform: Arm FVP / Base_Revc_2xAEMvA / Bare Metal Debug /ARMAEM-A_MP_0 [ Arm Development Studio ]
Observation: I am observing a strange thing of the ARMv8-A architecture's memory translation rules. Despite the translation table explicitly marking the memory region as Execute-Never, the CPU continues to fetch and execute instructions from this region without triggering an exception. I'm a student with limited hardware background, and I'm learning TF-A and doing porting during my internship. I'd really like to know the reason behind this.
Code I use: ARM Trusted Firmware v2.13
Github: github.com/.../arm-trusted-firmwarehttps://github.com/ARM-software/arm-trusted-firmware
Take the case of running BL31 in Development Studio as an example. (Breakpoint at the beginning of bl31_setup)
Technical Evidence (Verified via Debugger): SCTLR_EL3: M=1 (MMU enabled), WXN=1 (Write implies execute-never), I=1 (Instruction Cache enabled).
BL31’s code is loaded at: 0x04003000
Translation Table Entry (L3 Descriptor): 0x00400000_04003743
Physical Address: Verified via TTBR0_EL3 walk. (0x04034600 -> 0x04035003 -> 0x04037003 -> 0x00400000_04003743)
Attributes: AP[2:1]=0x1 (Read/Write), XN=1 (Execute-Never), AF=1, SH=0x3 (Inner Shareable), NS=0, AttrIndx = 0x0 (See the MAIR_EL3)
MAIR_EL3: 0x4404FF (Attr0 = 0xFF, Normal Memory).
Synchronization Performed: DSB SY + ISB
The PC (Program Counter) is confirmed to executing from the first instruction of BL31 code at address 0x04003000.
The Problem: This evidence should point to one conclusion: it cannot execute the BL31 code and will report an error. However, the execution flow remains uninterrupted.
From my point of view, it should cause "ESR_EL3 = 0x8600000F", which means: "Instruction Abort taken without a change in Exception level. Used for MMU faults generated by instruction accesses and synchronous External aborts, including synchronous parity or ECC errors. Not used for debug-related exceptions." + "Permission Fault, level 3".
As I test on a real fpga by using similar code by making some changes at the end of BL1 so that it would execute BL2 at level EL3 (but instead of bl2_el3_entrypoint.S, it would execute bl2_entrypoint.S). In this case, it throws an error when it jumps to the first instruction of BL2, and the ESR_EL3 register displays "Permission Fault, level 3".
If I add the instruction to disable the MMU (setting SCTLR_EL3.M_BIT to 0) at the end of BL1, and change the function to enable the MMU in the official code "arm_bl2_plat_arch_setup" to use "enable_mmu_el3(0)", it can run normally on the FPGA and bring up the UEFI. (In this real-world test, I used DDR instead of SRAM, so BL2 and BL31 were also placed here after being parsed.)
Request for Help: The above content is beyond my comprehension; even my internship supervisor doesn't understand the reasoning behind it. Therefore, I need help from the experts on this forum.
Reference: DDI0487M_a_a-profile_architecture_reference_manual.pdf ARM Development Studio@Docs (such as Docs/ARM_A/xhtml/AArch64-esr_el3.html) armv8_a_address_translation version1.1
tf-a@lists.trustedfirmware.org