Skip to content

# arm: cortex_m: TrustZone-M secure-only builds never clear AIRCR.BFHFNMINS — real BusFaults misreport as an undebuggable "bus fault on vector table read" at PC=0x0 #117172

Description

@martinrbowman

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

  • This is a regression.

Steps to reproduce

Reproduction

  1. Any TrustZone-M-capable NXP MCX N-series board, single secure-only image
    (CONFIG_ARM_SECURE_FIRMWARE=y, no NS build).
  2. 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.
  3. 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).
  4. 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
    0xE000ED080xE000ED3C) 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

Metadata

Metadata

Labels

area: TF-MARM Trusted Firmware-M (TF-M)

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions