summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2019-02-05 16:52:37 +0000
committerPeter Maydell <peter.maydell@linaro.org>2019-02-05 16:52:37 +0000
commit51bf0d7aa91a9d4e2563240a42e6cb705cef84aa (patch)
tree4072b59c5ac2c290bd577f62a77bb62e32461a44
parent1bafc2ba7e6bfe89fff3503fdac8db39c973de48 (diff)
downloadqemu-51bf0d7aa91a9d4e2563240a42e6cb705cef84aa.zip
target/arm: Default handling of BTYPE during translation
The branch target exception for guarded pages has high priority, and only 8 instructions are valid for that case. Perform this check before doing any other decode. Clear BTYPE after all insns that neither set BTYPE nor exit via exception (DISAS_NORETURN). Not yet handled are insns that exit via DISAS_NORETURN for some other reason, like direct branches. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20190128223118.5255-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--target/arm/internals.h6
-rw-r--r--target/arm/translate-a64.c139
-rw-r--r--target/arm/translate.h9
3 files changed, 152 insertions, 2 deletions
diff --git a/target/arm/internals.h b/target/arm/internals.h
index a6fd4582b2..d01a3f9f44 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -268,6 +268,7 @@ enum arm_exception_class {
EC_FPIDTRAP = 0x08,
EC_PACTRAP = 0x09,
EC_CP14RRTTRAP = 0x0c,
+ EC_BTITRAP = 0x0d,
EC_ILLEGALSTATE = 0x0e,
EC_AA32_SVC = 0x11,
EC_AA32_HVC = 0x12,
@@ -439,6 +440,11 @@ static inline uint32_t syn_pactrap(void)
return EC_PACTRAP << ARM_EL_EC_SHIFT;
}
+static inline uint32_t syn_btitrap(int btype)
+{
+ return (EC_BTITRAP << ARM_EL_EC_SHIFT) | btype;
+}
+
static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
{
return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index a92fd43378..7034fb3d12 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -128,6 +128,16 @@ static inline int get_a64_user_mem_index(DisasContext *s)
return arm_to_core_mmu_idx(useridx);
}
+static void reset_btype(DisasContext *s)
+{
+ if (s->btype != 0) {
+ TCGv_i32 zero = tcg_const_i32(0);
+ tcg_gen_st_i32(zero, cpu_env, offsetof(CPUARMState, btype));
+ tcg_temp_free_i32(zero);
+ s->btype = 0;
+ }
+}
+
void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
fprintf_function cpu_fprintf, int flags)
{
@@ -13756,6 +13766,90 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn)
}
}
+/**
+ * is_guarded_page:
+ * @env: The cpu environment
+ * @s: The DisasContext
+ *
+ * Return true if the page is guarded.
+ */
+static bool is_guarded_page(CPUARMState *env, DisasContext *s)
+{
+#ifdef CONFIG_USER_ONLY
+ return false; /* FIXME */
+#else
+ uint64_t addr = s->base.pc_first;
+ int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx);
+ unsigned int index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
+
+ /*
+ * We test this immediately after reading an insn, which means
+ * that any normal page must be in the TLB. The only exception
+ * would be for executing from flash or device memory, which
+ * does not retain the TLB entry.
+ *
+ * FIXME: Assume false for those, for now. We could use
+ * arm_cpu_get_phys_page_attrs_debug to re-read the page
+ * table entry even for that case.
+ */
+ return (tlb_hit(entry->addr_code, addr) &&
+ env->iotlb[mmu_idx][index].attrs.target_tlb_bit0);
+#endif
+}
+
+/**
+ * btype_destination_ok:
+ * @insn: The instruction at the branch destination
+ * @bt: SCTLR_ELx.BT
+ * @btype: PSTATE.BTYPE, and is non-zero
+ *
+ * On a guarded page, there are a limited number of insns
+ * that may be present at the branch target:
+ * - branch target identifiers,
+ * - paciasp, pacibsp,
+ * - BRK insn
+ * - HLT insn
+ * Anything else causes a Branch Target Exception.
+ *
+ * Return true if the branch is compatible, false to raise BTITRAP.
+ */
+static bool btype_destination_ok(uint32_t insn, bool bt, int btype)
+{
+ if ((insn & 0xfffff01fu) == 0xd503201fu) {
+ /* HINT space */
+ switch (extract32(insn, 5, 7)) {
+ case 0b011001: /* PACIASP */
+ case 0b011011: /* PACIBSP */
+ /*
+ * If SCTLR_ELx.BT, then PACI*SP are not compatible
+ * with btype == 3. Otherwise all btype are ok.
+ */
+ return !bt || btype != 3;
+ case 0b100000: /* BTI */
+ /* Not compatible with any btype. */
+ return false;
+ case 0b100010: /* BTI c */
+ /* Not compatible with btype == 3 */
+ return btype != 3;
+ case 0b100100: /* BTI j */
+ /* Not compatible with btype == 2 */
+ return btype != 2;
+ case 0b100110: /* BTI jc */
+ /* Compatible with any btype. */
+ return true;
+ }
+ } else {
+ switch (insn & 0xffe0001fu) {
+ case 0xd4200000u: /* BRK */
+ case 0xd4400000u: /* HLT */
+ /* Give priority to the breakpoint exception. */
+ return true;
+ }
+ }
+ return false;
+}
+
/* C3.1 A64 instruction index by encoding */
static void disas_a64_insn(CPUARMState *env, DisasContext *s)
{
@@ -13767,6 +13861,43 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
s->fp_access_checked = false;
+ if (dc_isar_feature(aa64_bti, s)) {
+ if (s->base.num_insns == 1) {
+ /*
+ * At the first insn of the TB, compute s->guarded_page.
+ * We delayed computing this until successfully reading
+ * the first insn of the TB, above. This (mostly) ensures
+ * that the softmmu tlb entry has been populated, and the
+ * page table GP bit is available.
+ *
+ * Note that we need to compute this even if btype == 0,
+ * because this value is used for BR instructions later
+ * where ENV is not available.
+ */
+ s->guarded_page = is_guarded_page(env, s);
+
+ /* First insn can have btype set to non-zero. */
+ tcg_debug_assert(s->btype >= 0);
+
+ /*
+ * Note that the Branch Target Exception has fairly high
+ * priority -- below debugging exceptions but above most
+ * everything else. This allows us to handle this now
+ * instead of waiting until the insn is otherwise decoded.
+ */
+ if (s->btype != 0
+ && s->guarded_page
+ && !btype_destination_ok(insn, s->bt, s->btype)) {
+ gen_exception_insn(s, 4, EXCP_UDEF, syn_btitrap(s->btype),
+ default_exception_el(s));
+ return;
+ }
+ } else {
+ /* Not the first insn: btype must be 0. */
+ tcg_debug_assert(s->btype == 0);
+ }
+ }
+
switch (extract32(insn, 25, 4)) {
case 0x0: case 0x1: case 0x3: /* UNALLOCATED */
unallocated_encoding(s);
@@ -13803,6 +13934,14 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
/* if we allocated any temporaries, free them here */
free_tmp_a64(s);
+
+ /*
+ * After execution of most insns, btype is reset to 0.
+ * Note that we set btype == -1 when the insn sets btype.
+ */
+ if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) {
+ reset_btype(s);
+ }
}
static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 3d5e8bacac..f73939d7b4 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -71,8 +71,13 @@ typedef struct DisasContext {
bool pauth_active;
/* True with v8.5-BTI and SCTLR_ELx.BT* set. */
bool bt;
- /* A copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. */
- uint8_t btype;
+ /*
+ * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
+ * < 0, set by the current instruction.
+ */
+ int8_t btype;
+ /* True if this page is guarded. */
+ bool guarded_page;
/* Bottom two bits of XScale c15_cpar coprocessor access control reg */
int c15_cpar;
/* TCG op of the current insn_start. */