From 23cafae344f4f663879e38903c78b20a3e1602d6 Mon Sep 17 00:00:00 2001 From: Chapoly1305 Date: Mon, 13 Jul 2026 16:22:48 -0400 Subject: [PATCH] arm/translate: allow MOV.W Rd,SP (Thumb2 T3) on non-MVE M-profile cores The data-processing shifted-register wide-shift space (op==2) excluded rm==0xd (SP) because those encodings are the ARMv8.1-M MVE long-shift instructions (ASRL/LSLL/...). But MVE only exists on cores that implement it (e.g. Cortex-M55/ M85), not Cortex-M33. On a non-MVE M-profile core, 'MOV.W Rd, SP' (rn==0xf, rm==0xd) is a legal plain MOV; rejecting it as UNDEFINSTR makes real firmware fault. Observed on real EFR32MG21 (Cortex-M33) firmware: a single such encoding caused an endless fault->SYSRESETREQ reset loop, preventing radio bring-up. Gate the rm==0xd exclusion on ENABLE_ARCH_MVE so non-MVE cores decode it as MOV. --- arch/arm/translate.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/translate.c b/arch/arm/translate.c index e4d117ca2..9fcd5211c 100644 --- a/arch/arm/translate.c +++ b/arch/arm/translate.c @@ -9572,7 +9572,13 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) uint8_t op0 = (insn >> 15) & 0x1; uint8_t op1 = (insn >> 12) & 0x3; - if(op0 == 0 && rm != 0xd && rm != 0xf) { + // The rm==0xd (SP) encodings in this space are the ARMv8.1-M MVE + // long-shift instructions (ASRL/LSLL/...), so they are only + // reserved on cores that implement MVE. On a non-MVE M-profile + // core (e.g. Cortex-M33) `MOV.W Rd, SP` (rn==0xf, rm==0xd) is a + // legal plain MOV; without allowing it here the decoder rejects + // it as UNDEFINSTR and real firmware faults/reset-loops. + if(op0 == 0 && rm != 0xf && (rm != 0xd || !ENABLE_ARCH_MVE)) { tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); gen_arm_shift_im(tmp2, shiftop, shift, logic_cc);