diff --git a/qemu/target/arm/cpu.h b/qemu/target/arm/cpu.h index 0ea053c1..6a77123e 100644 --- a/qemu/target/arm/cpu.h +++ b/qemu/target/arm/cpu.h @@ -3110,6 +3110,8 @@ FIELD(TBFLAG_A32, NS, 6, 1) FIELD(TBFLAG_A32, VFPEN, 7, 1) FIELD(TBFLAG_A32, CONDEXEC, 8, 8) FIELD(TBFLAG_A32, SCTLR_B, 16, 1) +/* For M profile only, set if we must create a new FP context */ +FIELD(TBFLAG_A32, NEW_FP_CTXT_NEEDED, 19, 1) /* For M profile only, set if FPCCR.S does not match current security state */ FIELD(TBFLAG_A32, FPCCR_S_WRONG, 20, 1) /* For M profile only, Handler (ie not Thread) mode */ diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index 0d450215..f697ce2d 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -13218,6 +13218,19 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, flags = FIELD_DP32(flags, TBFLAG_A32, FPCCR_S_WRONG, 1); } + if (arm_feature(env, ARM_FEATURE_M) && + (env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && + (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) || + (env->v7m.secure && + !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) { + /* + * ASPEN is set, but FPCA/SFPA indicate that there is no active + * FP context; we must create a new FP context before executing + * any FP insn. + */ + flags = FIELD_DP32(flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED, 1); + } + *pflags = flags; *cs_base = 0; } diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index f9fca30e..481f29fe 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -3545,6 +3545,33 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) /* Don't need to do this for any further FP insns in this TB */ s->v8m_fpccr_s_wrong = false; } + + if (s->v7m_new_fp_ctxt_needed) { + /* + * Create new FP context by updating CONTROL.FPCA, CONTROL.SFPA + * and the FPSCR. + */ + TCGv_i32 control, fpscr; + uint32_t bits = R_V7M_CONTROL_FPCA_MASK; + + fpscr = load_cpu_field(s->uc, v7m.fpdscr[s->v8m_secure]); + gen_helper_vfp_set_fpscr(tcg_ctx, tcg_ctx->cpu_env, fpscr); + tcg_temp_free_i32(tcg_ctx, fpscr); + /* + * We don't need to arrange to end the TB, because the only + * parts of FPSCR which we cache in the TB flags are the VECLEN + * and VECSTRIDE, and those don't exist for M-profile. + */ + + if (s->v8m_secure) { + bits |= R_V7M_CONTROL_SFPA_MASK; + } + control = load_cpu_field(s->uc, v7m.control[M_REG_S]); + tcg_gen_ori_i32(tcg_ctx, control, control, bits); + store_cpu_field(s, control, v7m.control[M_REG_S]); + /* Don't need to do this for any further FP insns in this TB */ + s->v7m_new_fp_ctxt_needed = false; + } } if (extract32(insn, 28, 4) == 0xf) { diff --git a/qemu/target/arm/translate.h b/qemu/target/arm/translate.h index 58707b8b..f9529766 100644 --- a/qemu/target/arm/translate.h +++ b/qemu/target/arm/translate.h @@ -40,6 +40,7 @@ typedef struct DisasContext { bool v8m_secure; /* true if v8M and we're in Secure mode */ bool v8m_stackcheck; /* true if we need to perform v8M stack limit checks */ bool v8m_fpccr_s_wrong; /* true if v8M FPCCR.S != v8m_secure */ + bool v7m_new_fp_ctxt_needed; /* ASPEN set but no active FP context */ /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI * so that top level loop can generate correct syndrome information. */