summaryrefslogtreecommitdiff
path: root/target-arm/translate-a64.c
diff options
context:
space:
mode:
authorEdgar E. Iglesias <edgar.iglesias@xilinx.com>2016-06-06 16:59:28 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-06-06 16:59:28 +0100
commitaaa1f954d4cab243e3d5337a72bc6d104e1c4808 (patch)
tree809f34f601f6dc8bdfc98c7484717a90c71877f4 /target-arm/translate-a64.c
parent2a5a9abd4bc45e2f4c62c77e07aebe53608c6915 (diff)
downloadqemu-aaa1f954d4cab243e3d5337a72bc6d104e1c4808.tar.gz
target-arm: A64: Create Instruction Syndromes for Data Aborts
Add support for generating the ISS (Instruction Specific Syndrome) for Data Abort exceptions taken from AArch64. These syndromes are used by hypervisors for example to trap and emulate memory accesses. We save the decoded data out-of-band with the TBs at translation time. When exceptions hit, the extra data attached to the TB is used to recreate the state needed to encode instruction syndromes. This avoids the need to emit moves with every load/store. Based on a suggestion from Peter Maydell. Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1462464601-10888-2-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target-arm/translate-a64.c')
-rw-r--r--target-arm/translate-a64.c140
1 files changed, 118 insertions, 22 deletions
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index ce8141a442..f5e29d20a1 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -308,6 +308,20 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
}
}
+static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn)
+{
+ /* We don't need to save all of the syndrome so we mask and shift
+ * out uneeded bits to help the sleb128 encoder do a better job.
+ */
+ syn &= ARM_INSN_START_WORD2_MASK;
+ syn >>= ARM_INSN_START_WORD2_SHIFT;
+
+ /* We check and clear insn_start_idx to catch multiple updates. */
+ assert(s->insn_start_idx != 0);
+ tcg_set_insn_param(s->insn_start_idx, 2, syn);
+ s->insn_start_idx = 0;
+}
+
static void unallocated_encoding(DisasContext *s)
{
/* Unallocated and reserved encodings are uncategorized */
@@ -723,23 +737,47 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
* Store from GPR register to memory.
*/
static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source,
- TCGv_i64 tcg_addr, int size, int memidx)
+ TCGv_i64 tcg_addr, int size, int memidx,
+ bool iss_valid,
+ unsigned int iss_srt,
+ bool iss_sf, bool iss_ar)
{
g_assert(size <= 3);
tcg_gen_qemu_st_i64(source, tcg_addr, memidx, s->be_data + size);
+
+ if (iss_valid) {
+ uint32_t syn;
+
+ syn = syn_data_abort_with_iss(0,
+ size,
+ false,
+ iss_srt,
+ iss_sf,
+ iss_ar,
+ 0, 0, 0, 0, 0, false);
+ disas_set_insn_syndrome(s, syn);
+ }
}
static void do_gpr_st(DisasContext *s, TCGv_i64 source,
- TCGv_i64 tcg_addr, int size)
+ TCGv_i64 tcg_addr, int size,
+ bool iss_valid,
+ unsigned int iss_srt,
+ bool iss_sf, bool iss_ar)
{
- do_gpr_st_memidx(s, source, tcg_addr, size, get_mem_index(s));
+ do_gpr_st_memidx(s, source, tcg_addr, size, get_mem_index(s),
+ iss_valid, iss_srt, iss_sf, iss_ar);
}
/*
* Load from memory to GPR register
*/
-static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
- int size, bool is_signed, bool extend, int memidx)
+static void do_gpr_ld_memidx(DisasContext *s,
+ TCGv_i64 dest, TCGv_i64 tcg_addr,
+ int size, bool is_signed,
+ bool extend, int memidx,
+ bool iss_valid, unsigned int iss_srt,
+ bool iss_sf, bool iss_ar)
{
TCGMemOp memop = s->be_data + size;
@@ -755,13 +793,30 @@ static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
g_assert(size < 3);
tcg_gen_ext32u_i64(dest, dest);
}
+
+ if (iss_valid) {
+ uint32_t syn;
+
+ syn = syn_data_abort_with_iss(0,
+ size,
+ is_signed,
+ iss_srt,
+ iss_sf,
+ iss_ar,
+ 0, 0, 0, 0, 0, false);
+ disas_set_insn_syndrome(s, syn);
+ }
}
-static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
- int size, bool is_signed, bool extend)
+static void do_gpr_ld(DisasContext *s,
+ TCGv_i64 dest, TCGv_i64 tcg_addr,
+ int size, bool is_signed, bool extend,
+ bool iss_valid, unsigned int iss_srt,
+ bool iss_sf, bool iss_ar)
{
do_gpr_ld_memidx(s, dest, tcg_addr, size, is_signed, extend,
- get_mem_index(s));
+ get_mem_index(s),
+ iss_valid, iss_srt, iss_sf, iss_ar);
}
/*
@@ -1817,6 +1872,22 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
}
#endif
+/* Update the Sixty-Four bit (SF) registersize. This logic is derived
+ * from the ARMv8 specs for LDR (Shared decode for all encodings).
+ */
+static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc)
+{
+ int opc0 = extract32(opc, 0, 1);
+ int regsize;
+
+ if (is_signed) {
+ regsize = opc0 ? 32 : 64;
+ } else {
+ regsize = size == 3 ? 64 : 32;
+ }
+ return regsize == 64;
+}
+
/* C3.3.6 Load/store exclusive
*
* 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0
@@ -1868,10 +1939,15 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn)
}
} else {
TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ bool iss_sf = disas_ldst_compute_iss_sf(size, false, 0);
+
+ /* Generate ISS for non-exclusive accesses including LASR. */
if (is_store) {
- do_gpr_st(s, tcg_rt, tcg_addr, size);
+ do_gpr_st(s, tcg_rt, tcg_addr, size,
+ true, rt, iss_sf, is_lasr);
} else {
- do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false);
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false,
+ true, rt, iss_sf, is_lasr);
}
}
}
@@ -1923,7 +1999,11 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn)
if (is_vector) {
do_fp_ld(s, rt, tcg_addr, size);
} else {
- do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+ /* Only unsigned 32bit loads target 32bit registers. */
+ bool iss_sf = opc == 0 ? 32 : 64;
+
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false,
+ true, rt, iss_sf, false);
}
tcg_temp_free_i64(tcg_addr);
}
@@ -2042,9 +2122,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
} else {
TCGv_i64 tcg_rt = cpu_reg(s, rt);
if (is_load) {
- do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false,
+ false, 0, false, false);
} else {
- do_gpr_st(s, tcg_rt, tcg_addr, size);
+ do_gpr_st(s, tcg_rt, tcg_addr, size,
+ false, 0, false, false);
}
}
tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size);
@@ -2057,9 +2139,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
} else {
TCGv_i64 tcg_rt2 = cpu_reg(s, rt2);
if (is_load) {
- do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false);
+ do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false,
+ false, 0, false, false);
} else {
- do_gpr_st(s, tcg_rt2, tcg_addr, size);
+ do_gpr_st(s, tcg_rt2, tcg_addr, size,
+ false, 0, false, false);
}
}
@@ -2102,6 +2186,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
bool is_store = false;
bool is_extended = false;
bool is_unpriv = (idx == 2);
+ bool iss_valid = !is_vector;
bool post_index;
bool writeback;
@@ -2169,12 +2254,15 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
} else {
TCGv_i64 tcg_rt = cpu_reg(s, rt);
int memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s);
+ bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
if (is_store) {
- do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx);
+ do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx,
+ iss_valid, rt, iss_sf, false);
} else {
do_gpr_ld_memidx(s, tcg_rt, tcg_addr, size,
- is_signed, is_extended, memidx);
+ is_signed, is_extended, memidx,
+ iss_valid, rt, iss_sf, false);
}
}
@@ -2272,10 +2360,14 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn,
}
} else {
TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
if (is_store) {
- do_gpr_st(s, tcg_rt, tcg_addr, size);
+ do_gpr_st(s, tcg_rt, tcg_addr, size,
+ true, rt, iss_sf, false);
} else {
- do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+ do_gpr_ld(s, tcg_rt, tcg_addr, size,
+ is_signed, is_extended,
+ true, rt, iss_sf, false);
}
}
}
@@ -2352,10 +2444,13 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn,
}
} else {
TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
if (is_store) {
- do_gpr_st(s, tcg_rt, tcg_addr, size);
+ do_gpr_st(s, tcg_rt, tcg_addr, size,
+ true, rt, iss_sf, false);
} else {
- do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended,
+ true, rt, iss_sf, false);
}
}
}
@@ -11102,7 +11197,8 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb)
tcg_clear_temp_count();
do {
- tcg_gen_insn_start(dc->pc, 0);
+ dc->insn_start_idx = tcg_op_buf_count();
+ tcg_gen_insn_start(dc->pc, 0, 0);
num_insns++;
if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {