2026-05-30
The asker is running ARM Trusted Firmware (BL2/BL31) at EL3 on an FVP Base_Revc_2xAEMvA model. They observe that even after the translation tables explicitly mark a memory region as UXN/PXN (Execute-Never), the CPU keeps fetching and executing instructions from that region. From a pure "MMU enforces permissions on every access" mental model, this looks impossible — so what gives?
Why it's interesting: this sits at the intersection of three subtle ARMv8-A behaviours that are easy to overlook when you only read the descriptor format tables:
TLBI + DSB ISH + ISB sequence for the right regime (TLBI ALLE3 at EL3, not VAE1).TTBR0_EL3 and SCTLR_EL3. If TF-A is editing the EL1/EL2 tables (or only one of MAIR/TCR/TTBR at the wrong EL), the XN bit applies to a regime the CPU isn't currently translating through.ISB after the TLBI, the core can legitimately retire instructions whose translations no longer authorize execution.Approach to debug:
CurrentEL at the moment of execution.TTBR0_EL3 manually for the faulting VA — don't trust the C struct you think you wrote. The FVP's "Memory" view lies if you read the source table instead of following the walk.DSB ISHST; TLBI ALLE3; DSB ISH; ISB. Skipping the trailing ISB is the classic bug.SCTLR_EL3.M = 1 and SCTLR_EL3.WXN. If M=0, the MMU is off and every region is effectively RWX flat-mapped — XN bits are dead text.HCR_EL2.{TGE,E2H} if a transition occurred; the effective regime can shift mid-flow.Gotcha: on FVP specifically, the model is very forgiving about caches but very strict about TLB semantics. A bug that "works" on silicon because of timing can be exposed on FVP — or vice versa. Also, WXN only promotes writable pages to XN; it doesn't retroactively invalidate cached translations.
