M-profile CCR.BFHFNMIGN lets software executing at a negative execution priority (in HardFault/NMI, or with FAULTMASK set) suppress precise data BusFaults caused by load/store instructions: the access completes returning UNKNOWN data, the fault status is recorded in BFSR/BFAR, but no BusFault exception is taken. Software uses this to probe for the presence of a device.
QEMU stored CCR.BFHFNMIGN but never consumed it: arm_cpu_do_transaction_ failed() always raised the external abort, which arm_v7m_cpu_do_interrupt() pended as a BusFault and then escalated to a HardFault it could not take at priority -1, aborting the VM with "Lockup: can't escalate 3 to HardFault". Honour the bit in arm_cpu_do_transaction_failed(): when the access is a data access from M-profile code at negative priority with BFHFNMIGN set, record PRECISERR/BFARVALID and BFAR and return without raising, so the faulting instruction completes instead of re-faulting forever. Instruction fetches are unaffected, since BFHFNMIGN applies only to data accesses. The SG instruction's stack-word load is also an AccType_NORMAL data access that must honour BFHFNMIGN, but QEMU performs it manually in v7m_read_sg_stack_word() (outside the TCG TLB, so it never reaches arm_cpu_do_transaction_failed()). Apply the same suppression there: on a BusFault, record the status and, when BFHFNMIGN is set at negative priority, return the UNKNOWN data instead of pending ARMV7M_EXCP_BUS. The remaining manual EXCP_BUS sites (vector-table loads, stacking, unstacking) are AccType_VECTABLE/STACK/UNSTACK and are not required to honour the bit, so they are left unchanged. This surfaced running the real NXP i.MX 95 System Manager firmware on the emulated Cortex-M33: its SystemMemoryProbe() (set BFHFNMIGN + FAULTMASK, do the access, test CFSR.BFARVALID) locked up the VM. With this change the SM's debug-monitor memory-probe commands run and recover correctly. Signed-off-by: Kyle Fox <[email protected]> --- v2: - Also honour BFHFNMIGN for the SG instruction's stack-word load in v7m_read_sg_stack_word() (an AccType_NORMAL access performed manually, outside the TCG TLB), per review. The vector-table/stacking/unstacking EXCP_BUS sites are left unchanged (AccType_VECTABLE/STACK/UNSTACK). target/arm/tcg/m_helper.c | 12 ++++++++++++ target/arm/tcg/tlb_helper.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index f2059ed8b03..ba101ecb953 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -2086,6 +2086,18 @@ static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx, env->v7m.cfsr[M_REG_NS] |= (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); env->v7m.bfar = addr; + /* + * The SG instruction's stack-word load is an AccType_NORMAL data + * access, so CCR.BFHFNMIGN applies: at negative execution priority + * with BFHFNMIGN set, the BusFault is suppressed -- the access + * completes returning UNKNOWN data (status recorded above), with no + * BusFault exception pended. + */ + if ((env->v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK) && + armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) { + *spdata = value; + return true; + } armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); return false; } diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index bbe1e70bc43..452688010f5 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -10,6 +10,7 @@ #include "helper.h" #include "internals.h" #include "cpu-features.h" +#include "hw/intc/armv7m_nvic.h" /* * Returns true if the stage 1 translation regime is using LPAE format page @@ -318,8 +319,31 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; ARMMMUFaultInfo fi = {}; + /* + * For M-profile, CCR.BFHFNMIGN lets software executing at a negative + * priority (in HardFault/NMI, or with FAULTMASK set) suppress precise + * data BusFaults from load/store instructions: the access completes + * returning UNKNOWN data (the store is dropped), the fault status is + * recorded in BFSR/BFAR, but no BusFault exception is taken. This is + * the mechanism software uses to probe for the presence of a device + * (e.g. the NXP System Manager's SystemMemoryProbe). Honour it by + * recording the status and returning without raising, so the faulting + * instruction completes rather than re-faulting forever. BFHFNMIGN + * applies only to data accesses, so instruction fetches are unaffected. + */ + if (arm_feature(env, ARM_FEATURE_M) && + access_type != MMU_INST_FETCH && + (env->v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK) && + armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) { + env->v7m.cfsr[M_REG_NS] |= + (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); + env->v7m.bfar = addr; + return; + } + /* now we have a real cpu fault */ cpu_restore_state(cs, retaddr); -- 2.34.1
