Describe the bug
On an ARMv8-M TrustZone-M SoC (Cortex-M33) built as a secure-only single image
(CONFIG_ARM_SECURE_FIRMWARE=y, CONFIG_ARM_TRUSTZONE_M=y, no Non-secure partition, VTOR_NS
never configured), any real BusFault — an ordinary NULL-pointer dereference, an unaligned access,
whatever — gets mis-vectored and reported as an inscrutable, undebuggable fault instead of the real one:
***** HARD FAULT *****
Bus fault on vector table read
r0/a1: 0xfefa125b r1/a2: 0x00000000 r2/a3: 0x30000000
r3/a4: 0x1000f8fb r12/ip: 0x30002ed8 r14/lr: 0x00000000
xpsr: 0x00000008
Faulting instruction address (r15/pc): 0x00000000
Fault during interrupt handling
r14/lr and r15/pc are both 0x0 in the stacked exception frame — the CPU never even determined
a valid PC, because the vector fetch itself failed. r0 (0xfefa125b) is not corrupted-pointer
garbage; it's ARMv8-M's architecturally-defined EXC_INTEGRITY_SIGNATURE (see core_cm33.h's
EXC_INTEGRITY_SIGNATURE / arch/arm/core/cortex_m/fault.c's INTEGRITY_SIGNATURE), which per ARM's
own documentation only appears when returning from Non-secure state back to Secure. On a secure-only
build with no NS image, that should never happen — but it does, because AIRCR.BFHFNMINS is left at
its hardware reset default instead of being explicitly cleared.
Regression
Steps to reproduce
Reproduction
- Any TrustZone-M-capable NXP MCX N-series board, single secure-only image
(CONFIG_ARM_SECURE_FIRMWARE=y, no NS build).
- Trigger any real BusFault at runtime — in our case, a genuine NULL-pointer dereference inside a
driver ISR (see the companion report on video_mcux_smartdma.c's nxp_video_sdma_callback()), but
any BusFault reproduces the same masking.
- Observe: instead of a decodable fault (correct PC/LR, readable CFSR/HFSR/BFAR), Zephyr reports
"Bus fault on vector table read" with PC=0x0, LR=0x0, and r0 holding
EXC_INTEGRITY_SIGNATURE (0xFEFA125B).
- Confirmed via GDB (LinkServer/CMSIS-DAP probe, both an onboard MCU-Link and a separate standalone
unit — same firmware, same result): SCB registers (CFSR/HFSR/VTOR/etc, addresses
0xE000ED08–0xE000ED3C) are inaccessible from the debugger once halted in this state, which is
itself part of what makes this so hard to diagnose — there is no way to inspect the real fault
registers after the fact without first fixing this masking issue.
Fix
Explicitly clear AIRCR.BFHFNMINS early in the TrustZone-M boot path (e.g. alongside wherever SAU
setup already happens for CONFIG_ARM_TRUSTZONE_M, or as a documented step apps must take themselves).
Minimal example, done from application code as a workaround pending an upstream fix:
#include <cmsis_core.h>
uint32_t aircr = SCB->AIRCR;
aircr &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_BFHFNMINS_Msk);
aircr |= (0x5FAUL << SCB_AIRCR_VECTKEY_Pos);
SCB->AIRCR = aircr;
This should arguably live in Zephyr's own TrustZone-M init path (conditionally, since a real
Secure+Non-secure split image does want BFHFNMINS following whatever policy the app defines), at
minimum for the single-secure-image case where there's no NS vector table to route to regardless.
Testing done
Applied the workaround in application code (main(), before any driver touches an interrupt), rebuilt,
reflashed real hardware — both frdm_mcxn947 and frdm_mcxn236. Result: the previously-inscrutable
"Bus fault on vector table read" became a normal, fully decodable BUS FAULT (Precise data bus error, correct BFAR, correct PC/LR resolving to real source lines via addr2line) pointing
directly at the actual bug (a NULL-pointer dereference, see companion report). Without the fix: same
underlying NULL dereference, but reported as an undecodable PC=0 fault every time, 100% reproducible,
on both chips.
Relevant log output
Impact
Major – Severely degrades functionality; workaround is difficult or unavailable.
Environment
- Zephyr: v4.4.0
- Boards:
frdm_mcxn947/mcxn947/cpu0 and frdm_mcxn236 (both Cortex-M33/TrustZone-M, same
nxp,mcx SoC family) — confirmed identical symptom on both, different dies
- Toolchain: Zephyr SDK 1.0.1 (arm-zephyr-eabi)
Additional Context
This isn't specific to USB or SmartDMA or this app — it's a general TrustZone-M boot-path gap that
makes any real BusFault undebuggable on secure-only single-image builds for this SoC family (and
likely any Cortex-M33/M55 part where BFHFNMINS resets Non-secure). Worth checking whether this should
be a general Zephyr TrustZone-M fix (clear BFHFNMINS unconditionally when no NS image is configured)
or at least documented prominently, since the failure mode actively misleads debugging effort
Describe the bug
On an ARMv8-M TrustZone-M SoC (Cortex-M33) built as a secure-only single image
(
CONFIG_ARM_SECURE_FIRMWARE=y,CONFIG_ARM_TRUSTZONE_M=y, no Non-secure partition,VTOR_NSnever configured), any real BusFault — an ordinary NULL-pointer dereference, an unaligned access,
whatever — gets mis-vectored and reported as an inscrutable, undebuggable fault instead of the real one:
r14/lrandr15/pcare both0x0in the stacked exception frame — the CPU never even determineda valid PC, because the vector fetch itself failed.
r0(0xfefa125b) is not corrupted-pointergarbage; it's ARMv8-M's architecturally-defined
EXC_INTEGRITY_SIGNATURE(seecore_cm33.h'sEXC_INTEGRITY_SIGNATURE/arch/arm/core/cortex_m/fault.c'sINTEGRITY_SIGNATURE), which per ARM'sown documentation only appears when returning from Non-secure state back to Secure. On a secure-only
build with no NS image, that should never happen — but it does, because
AIRCR.BFHFNMINSis left atits hardware reset default instead of being explicitly cleared.
Regression
Steps to reproduce
Reproduction
(
CONFIG_ARM_SECURE_FIRMWARE=y, no NS build).driver ISR (see the companion report on
video_mcux_smartdma.c'snxp_video_sdma_callback()), butany BusFault reproduces the same masking.
"Bus fault on vector table read"withPC=0x0,LR=0x0, andr0holdingEXC_INTEGRITY_SIGNATURE(0xFEFA125B).unit — same firmware, same result):
SCBregisters (CFSR/HFSR/VTOR/etc, addresses0xE000ED08–0xE000ED3C) are inaccessible from the debugger once halted in this state, which isitself part of what makes this so hard to diagnose — there is no way to inspect the real fault
registers after the fact without first fixing this masking issue.
Fix
Explicitly clear
AIRCR.BFHFNMINSearly in the TrustZone-M boot path (e.g. alongside whereverSAUsetup already happens for
CONFIG_ARM_TRUSTZONE_M, or as a documented step apps must take themselves).Minimal example, done from application code as a workaround pending an upstream fix:
This should arguably live in Zephyr's own TrustZone-M init path (conditionally, since a real
Secure+Non-secure split image does want
BFHFNMINSfollowing whatever policy the app defines), atminimum for the single-secure-image case where there's no NS vector table to route to regardless.
Testing done
Applied the workaround in application code (
main(), before any driver touches an interrupt), rebuilt,reflashed real hardware — both
frdm_mcxn947andfrdm_mcxn236. Result: the previously-inscrutable"Bus fault on vector table read"became a normal, fully decodableBUS FAULT(Precise data bus error, correctBFAR, correctPC/LRresolving to real source lines viaaddr2line) pointingdirectly at the actual bug (a NULL-pointer dereference, see companion report). Without the fix: same
underlying NULL dereference, but reported as an undecodable PC=0 fault every time, 100% reproducible,
on both chips.
Relevant log output
Impact
Major – Severely degrades functionality; workaround is difficult or unavailable.
Environment
frdm_mcxn947/mcxn947/cpu0andfrdm_mcxn236(both Cortex-M33/TrustZone-M, samenxp,mcxSoC family) — confirmed identical symptom on both, different diesAdditional Context
This isn't specific to USB or SmartDMA or this app — it's a general TrustZone-M boot-path gap that
makes any real BusFault undebuggable on secure-only single-image builds for this SoC family (and
likely any Cortex-M33/M55 part where
BFHFNMINSresets Non-secure). Worth checking whether this shouldbe a general Zephyr TrustZone-M fix (clear
BFHFNMINSunconditionally when no NS image is configured)or at least documented prominently, since the failure mode actively misleads debugging effort