diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 660dae696d..7797a137af 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -658,7 +658,11 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, goto fail; } - if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && + /* + * HSTR_EL2 traps from EL1 are checked earlier, in generated code; + * we only need to check here for traps from EL0. + */ + if (!is_a64(env) && arm_current_el(env) == 0 && ri->cp == 15 && (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { uint32_t mask = 1 << ri->crn; diff --git a/target/arm/translate.c b/target/arm/translate.c index 9252a464a1..f4bfe55158 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -4760,6 +4760,32 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, break; } + if (s->hstr_active && cpnum == 15 && s->current_el == 1) { + /* + * At EL1, check for a HSTR_EL2 trap, which must take precedence + * over the UNDEF for "no such register" or the UNDEF for "access + * permissions forbid this EL1 access". HSTR_EL2 traps from EL0 + * only happen if the cpreg doesn't UNDEF at EL0, so we do those in + * access_check_cp_reg(), after the checks for whether the access + * configurably trapped to EL1. + */ + uint32_t maskbit = is64 ? crm : crn; + + if (maskbit != 4 && maskbit != 14) { + /* T4 and T14 are RES0 so never cause traps */ + TCGv_i32 t; + DisasLabel over = gen_disas_label(s); + + t = load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); + tcg_gen_andi_i32(t, t, 1u << maskbit); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); + tcg_temp_free_i32(t); + + gen_exception_insn(s, 0, EXCP_UDEF, syndrome); + set_disas_label(s, over); + } + } + if (!ri) { /* * Unknown register; this might be a guest error or a QEMU @@ -4788,7 +4814,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, return; } - if (s->hstr_active || ri->accessfn || + if ((s->hstr_active && s->current_el == 0) || ri->accessfn || (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { /* * Emit code to perform further access permissions checks at