From 457934855b0e1ba7ecb12df19a074999e08eea3e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Jan 2020 18:07:15 -0500 Subject: [PATCH] target/arm: Handle AArch32 CP15 trapping via HSTR_EL2 HSTR_EL2 offers a way to trap ranges of CP15 system register accesses to EL2, and it looks like this register is completely ignored by QEMU. To avoid adding extra .accessfn filters all over the place (which would have a direct performance impact), let's add a new TB flag that gets set whenever HSTR_EL2 is non-zero and that QEMU translates a context where this trap has a chance to apply, and only generate the extra access check if the hypervisor is actively using this feature. Tested with a hand-crafted KVM guest accessing CBAR. Backports commit 5bb0a20b74ad17dee5dae38e3b8b70b383ee7c2d from qemu --- qemu/target/arm/cpu.h | 2 ++ qemu/target/arm/helper.c | 6 ++++++ qemu/target/arm/op_helper.c | 22 ++++++++++++++++++++++ qemu/target/arm/translate.c | 3 ++- qemu/target/arm/translate.h | 2 ++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/qemu/target/arm/cpu.h b/qemu/target/arm/cpu.h index 71bc9490..c9b51ae4 100644 --- a/qemu/target/arm/cpu.h +++ b/qemu/target/arm/cpu.h @@ -3132,6 +3132,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) +FIELD(TBFLAG_A32, HSTR_ACTIVE, 17, 1) + /* For M profile only, set if FPCCR.LSPACT is set */ FIELD(TBFLAG_A32, LSPACT, 18, 1) /* For M profile only, set if we must create a new FP context */ diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index 383cc7d4..60839b36 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -11166,6 +11166,12 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, || arm_el_is_aa64(env, 1) || arm_feature(env, ARM_FEATURE_M)) { flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); } + + if (arm_current_el(env) < 2 && env->cp15.hstr_el2 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + flags = FIELD_DP32(flags, TBFLAG_A32, HSTR_ACTIVE, 1); + } + /* Note that XSCALE_CPAR shares bits with VECSTRIDE */ if (arm_feature(env, ARM_FEATURE_XSCALE)) { flags = FIELD_DP32(flags, TBFLAG_A32, diff --git a/qemu/target/arm/op_helper.c b/qemu/target/arm/op_helper.c index 47c32411..07d88b49 100644 --- a/qemu/target/arm/op_helper.c +++ b/qemu/target/arm/op_helper.c @@ -614,6 +614,27 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env)); } + /* + * Check for an EL2 trap due to HSTR_EL2. We expect EL0 accesses + * to sysregs non accessible at EL0 to have UNDEF-ed already. + */ + if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + uint32_t mask = 1 << ri->crn; + + if (ri->type & ARM_CP_64BIT) { + mask = 1 << ri->crm; + } + + /* T4 and T14 are RES0 */ + mask &= ~((1 << 4) | (1 << 14)); + + if (env->cp15.hstr_el2 & mask) { + target_el = 2; + goto exept; + } + } + if (!ri->accessfn) { return; } @@ -665,6 +686,7 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, g_assert_not_reached(); } +exept: raise_exception(env, EXCP_UDEF, syndrome, target_el); } diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index 03a38964..e8b599b9 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -7031,7 +7031,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) return 1; } - if (ri->accessfn || + if (s->hstr_active || ri->accessfn || (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. @@ -11158,6 +11158,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) !arm_el_is_aa64(env, 3); dc->thumb = FIELD_EX32(tb_flags, TBFLAG_A32, THUMB); dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B); + dc->hstr_active = FIELD_EX32(tb_flags, TBFLAG_A32, HSTR_ACTIVE); dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; condexec = FIELD_EX32(tb_flags, TBFLAG_A32, CONDEXEC); dc->condexec_mask = (condexec & 0xf) << 1; diff --git a/qemu/target/arm/translate.h b/qemu/target/arm/translate.h index 0d5902d9..de29881c 100644 --- a/qemu/target/arm/translate.h +++ b/qemu/target/arm/translate.h @@ -78,6 +78,8 @@ typedef struct DisasContext { int c15_cpar; /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ bool bt; + /* True if any CP15 access is trapped by HSTR_EL2 */ + bool hstr_active; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction.