@@ -94,7 +94,19 @@
struct arm_boot_info;
#define NB_MMU_MODES 7
-#define TARGET_INSN_START_EXTRA_WORDS 1
+/* ARM-specific extra insn start words:
+ * 1: Conditional execution bits
+ * 2: Partial exception syndrome for data aborts
+ */
+#define TARGET_INSN_START_EXTRA_WORDS 2
+
+/* The 2nd extra word holding syndrome info for data aborts does not use
+ * the upper 6 bits nor the lower 14 bits. We mask and shift it down to
+ * help the sleb128 encoder do a better job.
+ * When restoring the CPU state, we shift it back up.
+ */
+#define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1)
+#define ARM_INSN_START_WORD2_SHIFT 14
/* We currently assume float and double are IEEE single and double
precision respectively.
@@ -75,6 +75,43 @@ uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def,
#if !defined(CONFIG_USER_ONLY)
+static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
+ unsigned int target_el,
+ bool same_el,
+ bool s1ptw, int is_write,
+ int fsc)
+{
+ uint32_t syn;
+
+ /* ISV is only set for data aborts routed to EL2 and
+ * never for stage-1 page table walks faulting on stage 2.
+ *
+ * Furthermore, ISV is only set for certain kinds of load/stores.
+ * If the template syndrome does not have ISV set, we should leave
+ * it cleared.
+ *
+ * See ARMv8 specs, D7-1974:
+ * ISS encoding for an exception from a Data Abort, the
+ * ISV field.
+ */
+ if (!(template_syn & ARM_EL_ISV) || target_el != 2 || s1ptw) {
+ syn = syn_data_abort_no_iss(same_el,
+ 0, 0, s1ptw, is_write == 1, fsc);
+ } else {
+ /* Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template
+ * syndrome created at translation time.
+ * Now we create the runtime syndrome with the remaining fields.
+ */
+ syn = syn_data_abort_with_iss(same_el,
+ 0, 0, 0, 0, 0,
+ 0, 0, s1ptw, is_write == 1, fsc,
+ false);
+ /* Merge the runtime syndrome with the template syndrome. */
+ syn |= template_syn;
+ }
+ return syn;
+}
+
/* try to fill the TLB and return an exception if error. If retaddr is
* NULL, it means that the function was called in C code (i.e. not
* from generated code or from helper.c)
@@ -115,8 +152,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
syn = syn_insn_abort(same_el, 0, fi.s1ptw, syn);
exc = EXCP_PREFETCH_ABORT;
} else {
- syn = syn_data_abort_no_iss(same_el,
- 0, 0, fi.s1ptw, is_write == 1, syn);
+ syn = merge_syn_data_abort(env->exception.syndrome, target_el,
+ same_el, fi.s1ptw, is_write, syn);
if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) {
fsr |= (1 << 11);
}
@@ -137,6 +174,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
CPUARMState *env = &cpu->env;
int target_el;
bool same_el;
+ uint32_t syn;
if (retaddr) {
/* now we have a real cpu fault */
@@ -161,10 +199,9 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write,
env->exception.fsr |= (1 << 11);
}
- raise_exception(env, EXCP_DATA_ABORT,
- syn_data_abort_no_iss(same_el,
- 0, 0, 0, is_write == 1, 0x21),
- target_el);
+ syn = merge_syn_data_abort(env->exception.syndrome, target_el,
+ same_el, 0, is_write, 0x21);
+ raise_exception(env, EXCP_DATA_ABORT, syn, target_el);
}
#endif /* !defined(CONFIG_USER_ONLY) */
@@ -305,6 +305,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 */
@@ -720,23 +734,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;
@@ -752,13 +790,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);
}
/*
@@ -1814,6 +1869,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
@@ -1865,10 +1936,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);
}
}
}
@@ -1920,7 +1996,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);
}
@@ -2039,9 +2119,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);
@@ -2054,9 +2136,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);
}
}
@@ -2099,6 +2183,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;
@@ -2166,12 +2251,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);
}
}
@@ -2269,10 +2357,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);
}
}
}
@@ -2349,10 +2441,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);
}
}
}
@@ -11099,7 +11194,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))) {
@@ -11724,7 +11724,8 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
}
do {
tcg_gen_insn_start(dc->pc,
- (dc->condexec_cond << 4) | (dc->condexec_mask >> 1));
+ (dc->condexec_cond << 4) | (dc->condexec_mask >> 1),
+ 0);
num_insns++;
#ifdef CONFIG_USER_ONLY
@@ -12041,8 +12042,10 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb,
if (is_a64(env)) {
env->pc = data[0];
env->condexec_bits = 0;
+ env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
} else {
env->regs[15] = data[0];
env->condexec_bits = data[1];
+ env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT;
}
}
@@ -59,6 +59,8 @@ typedef struct DisasContext {
bool ss_same_el;
/* Bottom two bits of XScale c15_cpar coprocessor access control reg */
int c15_cpar;
+ /* TCG op index of the current insn_start. */
+ int insn_start_idx;
#define TMP_A64_MAX 16
int tmp_a64_count;
TCGv_i64 tmp_a64[TMP_A64_MAX];