aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-09-04 13:59:01 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-09-04 13:59:01 +0100
commit3c8153d3f50b7a94d06a1b34138a9fe6fd49f538 (patch)
tree0ef74d10b9f8c01bfec46cb36863fd3810788ecb /target
parent6b422e5f58f50b34b20c50ebe496fcb822f419b5 (diff)
parent5e5584c89f36b302c666bc6db535fd3f7ff35ad2 (diff)
downloadqemu-3c8153d3f50b7a94d06a1b34138a9fe6fd49f538.zip
qemu-3c8153d3f50b7a94d06a1b34138a9fe6fd49f538.tar.gz
qemu-3c8153d3f50b7a94d06a1b34138a9fe6fd49f538.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190903' into staging
target-arm queue: * Revert and correctly fix refactoring of unallocated_encoding() * Take exceptions on ATS instructions when needed * aspeed/timer: Provide back-pressure information for short periods * memory: Remove unused memory_region_iommu_replay_all() * hw/arm/smmuv3: Log a guest error when decoding an invalid STE * hw/arm/smmuv3: Remove spurious error messages on IOVA invalidations * target/arm: Fix SMMLS argument order * hw/arm: Use ARM_CPU_TYPE_NAME() macro when appropriate * hw/arm: Correct reference counting for creation of various objects * includes: remove stale [smp|max]_cpus externs * tcg/README: fix typo * atomic_template: fix indentation in GEN_ATOMIC_HELPER * include/exec/cpu-defs.h: fix typo * target/arm: Free TCG temps in trans_VMOV_64_sp() * target/arm: Don't abort on M-profile exception return in linux-user mode # gpg: Signature made Tue 03 Sep 2019 16:35:19 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190903: (21 commits) target/arm: Don't abort on M-profile exception return in linux-user mode target/arm: Free TCG temps in trans_VMOV_64_sp() include/exec/cpu-defs.h: fix typo atomic_template: fix indentation in GEN_ATOMIC_HELPER tcg/README: fix typo s/afterwise/afterwards/ includes: remove stale [smp|max]_cpus externs hw/net/xilinx_axi: Use object_initialize_child for correct ref. counting hw/dma/xilinx_axi: Use object_initialize_child for correct ref. counting hw/arm/fsl-imx: Add the cpu as child of the SoC object hw/arm: Use sysbus_init_child_obj for correct reference counting hw/arm: Use object_initialize_child for correct reference counting hw/arm: Use ARM_CPU_TYPE_NAME() macro when appropriate target/arm: Fix SMMLS argument order hw/arm/smmuv3: Remove spurious error messages on IOVA invalidations hw/arm/smmuv3: Log a guest error when decoding an invalid STE memory: Remove unused memory_region_iommu_replay_all() aspeed/timer: Provide back-pressure information for short periods target/arm: Take exceptions on ATS instructions when needed target/arm: Allow ARMCPRegInfo read/write functions to throw exceptions target/arm: Factor out unallocated_encoding for aarch32 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target')
-rw-r--r--target/arm/cpu.h6
-rw-r--r--target/arm/helper.c107
-rw-r--r--target/arm/translate-a64.c13
-rw-r--r--target/arm/translate-a64.h2
-rw-r--r--target/arm/translate-vfp.inc.c2
-rw-r--r--target/arm/translate.c50
-rw-r--r--target/arm/translate.h2
7 files changed, 160 insertions, 22 deletions
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 0981303..297ad5e 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -2212,6 +2212,9 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
* IO indicates that this register does I/O and therefore its accesses
* need to be surrounded by gen_io_start()/gen_io_end(). In particular,
* registers which implement clocks or timers require this.
+ * RAISES_EXC is for when the read or write hook might raise an exception;
+ * the generated code will synchronize the CPU state before calling the hook
+ * so that it is safe for the hook to call raise_exception().
*/
#define ARM_CP_SPECIAL 0x0001
#define ARM_CP_CONST 0x0002
@@ -2230,10 +2233,11 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
#define ARM_CP_FPU 0x1000
#define ARM_CP_SVE 0x2000
#define ARM_CP_NO_GDB 0x4000
+#define ARM_CP_RAISES_EXC 0x8000
/* Used only as a terminator for ARMCPRegInfo lists */
#define ARM_CP_SENTINEL 0xffff
/* Mask of only the flag bits in a type field */
-#define ARM_CP_FLAG_MASK 0x70ff
+#define ARM_CP_FLAG_MASK 0xf0ff
/* Valid values for ARMCPRegInfo state field, indicating which of
* the AArch32 and AArch64 execution states this register is visible in.
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 7e0d539..507026c 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -2946,6 +2946,73 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &attrs,
&prot, &page_size, &fi, &cacheattrs);
+ if (ret) {
+ /*
+ * Some kinds of translation fault must cause exceptions rather
+ * than being reported in the PAR.
+ */
+ int current_el = arm_current_el(env);
+ int target_el;
+ uint32_t syn, fsr, fsc;
+ bool take_exc = false;
+
+ if (fi.s1ptw && current_el == 1 && !arm_is_secure(env)
+ && (mmu_idx == ARMMMUIdx_S1NSE1 || mmu_idx == ARMMMUIdx_S1NSE0)) {
+ /*
+ * Synchronous stage 2 fault on an access made as part of the
+ * translation table walk for AT S1E0* or AT S1E1* insn
+ * executed from NS EL1. If this is a synchronous external abort
+ * and SCR_EL3.EA == 1, then we take a synchronous external abort
+ * to EL3. Otherwise the fault is taken as an exception to EL2,
+ * and HPFAR_EL2 holds the faulting IPA.
+ */
+ if (fi.type == ARMFault_SyncExternalOnWalk &&
+ (env->cp15.scr_el3 & SCR_EA)) {
+ target_el = 3;
+ } else {
+ env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4;
+ target_el = 2;
+ }
+ take_exc = true;
+ } else if (fi.type == ARMFault_SyncExternalOnWalk) {
+ /*
+ * Synchronous external aborts during a translation table walk
+ * are taken as Data Abort exceptions.
+ */
+ if (fi.stage2) {
+ if (current_el == 3) {
+ target_el = 3;
+ } else {
+ target_el = 2;
+ }
+ } else {
+ target_el = exception_target_el(env);
+ }
+ take_exc = true;
+ }
+
+ if (take_exc) {
+ /* Construct FSR and FSC using same logic as arm_deliver_fault() */
+ if (target_el == 2 || arm_el_is_aa64(env, target_el) ||
+ arm_s1_regime_using_lpae_format(env, mmu_idx)) {
+ fsr = arm_fi_to_lfsc(&fi);
+ fsc = extract32(fsr, 0, 6);
+ } else {
+ fsr = arm_fi_to_sfsc(&fi);
+ fsc = 0x3f;
+ }
+ /*
+ * Report exception with ESR indicating a fault due to a
+ * translation table walk for a cache maintenance instruction.
+ */
+ syn = syn_data_abort_no_iss(current_el == target_el,
+ fi.ea, 1, fi.s1ptw, 1, fsc);
+ env->exception.vaddress = value;
+ env->exception.fsr = fsr;
+ raise_exception(env, EXCP_DATA_ABORT, syn, target_el);
+ }
+ }
+
if (is_a64(env)) {
format64 = true;
} else if (arm_feature(env, ARM_FEATURE_LPAE)) {
@@ -3150,7 +3217,7 @@ static const ARMCPRegInfo vapa_cp_reginfo[] = {
/* This underdecoding is safe because the reginfo is NO_RAW. */
{ .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY,
.access = PL1_W, .accessfn = ats_access,
- .writefn = ats_write, .type = ARM_CP_NO_RAW },
+ .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
#endif
REGINFO_SENTINEL
};
@@ -4283,35 +4350,45 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
/* 64 bit address translation operations */
{ .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0,
- .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1,
- .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2,
- .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3,
- .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4,
- .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5,
- .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6,
- .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7,
- .access = PL2_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
/* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */
{ .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0,
- .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1,
- .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
+ .writefn = ats_write64 },
{ .name = "PAR_EL1", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0,
@@ -4893,11 +4970,11 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0,
.access = PL2_W, .accessfn = at_s1e2_access,
- .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .writefn = ats_write64 },
{ .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1,
.access = PL2_W, .accessfn = at_s1e2_access,
- .type = ARM_CP_NO_RAW, .writefn = ats_write64 },
+ .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .writefn = ats_write64 },
/* The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE
* if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3
* with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose
@@ -4905,10 +4982,10 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
*/
{ .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0,
.access = PL2_W,
- .writefn = ats1h_write, .type = ARM_CP_NO_RAW },
+ .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
{ .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1,
.access = PL2_W,
- .writefn = ats1h_write, .type = ARM_CP_NO_RAW },
+ .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
{ .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0,
/* ARMv7 requires bit 0 and 1 to reset to 1. ARMv8 defines the
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 6fd0b77..4d09ae6 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -338,6 +338,13 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
}
}
+void unallocated_encoding(DisasContext *s)
+{
+ /* Unallocated and reserved encodings are uncategorized */
+ gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn_uncategorized(),
+ default_exception_el(s));
+}
+
static void init_tmp_a64_array(DisasContext *s)
{
#ifdef CONFIG_DEBUG_TCG
@@ -1707,6 +1714,12 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
tcg_temp_free_ptr(tmpptr);
tcg_temp_free_i32(tcg_syn);
tcg_temp_free_i32(tcg_isread);
+ } else if (ri->type & ARM_CP_RAISES_EXC) {
+ /*
+ * The readfn or writefn might raise an exception;
+ * synchronize the CPU state in case it does.
+ */
+ gen_a64_set_pc_im(s->pc_curr);
}
/* Handle special cases first */
diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h
index 12ad8ac..9cd2b3d 100644
--- a/target/arm/translate-a64.h
+++ b/target/arm/translate-a64.h
@@ -18,6 +18,8 @@
#ifndef TARGET_ARM_TRANSLATE_A64_H
#define TARGET_ARM_TRANSLATE_A64_H
+void unallocated_encoding(DisasContext *s);
+
#define unsupported_encoding(s, insn) \
do { \
qemu_log_mask(LOG_UNIMP, \
diff --git a/target/arm/translate-vfp.inc.c b/target/arm/translate-vfp.inc.c
index 3e8ea80..9ae980b 100644
--- a/target/arm/translate-vfp.inc.c
+++ b/target/arm/translate-vfp.inc.c
@@ -880,8 +880,10 @@ static bool trans_VMOV_64_sp(DisasContext *s, arg_VMOV_64_sp *a)
/* gpreg to fpreg */
tmp = load_reg(s, a->rt);
neon_store_reg32(tmp, a->vm);
+ tcg_temp_free_i32(tmp);
tmp = load_reg(s, a->rt2);
neon_store_reg32(tmp, a->vm + 1);
+ tcg_temp_free_i32(tmp);
}
return true;
diff --git a/target/arm/translate.c b/target/arm/translate.c
index cbe19b7..615859e 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -915,10 +915,27 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var)
store_cpu_field(var, thumb);
}
-/* Set PC and Thumb state from var. var is marked as dead.
+/*
+ * Set PC and Thumb state from var. var is marked as dead.
* For M-profile CPUs, include logic to detect exception-return
* branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC,
* and BX reg, and no others, and happens only for code in Handler mode.
+ * The Security Extension also requires us to check for the FNC_RETURN
+ * which signals a function return from non-secure state; this can happen
+ * in both Handler and Thread mode.
+ * To avoid having to do multiple comparisons in inline generated code,
+ * we make the check we do here loose, so it will match for EXC_RETURN
+ * in Thread mode. For system emulation do_v7m_exception_exit() checks
+ * for these spurious cases and returns without doing anything (giving
+ * the same behaviour as for a branch to a non-magic address).
+ *
+ * In linux-user mode it is unclear what the right behaviour for an
+ * attempted FNC_RETURN should be, because in real hardware this will go
+ * directly to Secure code (ie not the Linux kernel) which will then treat
+ * the error in any way it chooses. For QEMU we opt to make the FNC_RETURN
+ * attempt behave the way it would on a CPU without the security extension,
+ * which is to say "like a normal branch". That means we can simply treat
+ * all branches as normal with no magic address behaviour.
*/
static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var)
{
@@ -926,10 +943,12 @@ static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var)
* s->base.is_jmp that we need to do the rest of the work later.
*/
gen_bx(s, var);
+#ifndef CONFIG_USER_ONLY
if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY) ||
(s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M))) {
s->base.is_jmp = DISAS_BX_EXCRET;
}
+#endif
}
static inline void gen_bx_excret_final_code(DisasContext *s)
@@ -1231,7 +1250,7 @@ static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn)
s->base.is_jmp = DISAS_NORETURN;
}
-void unallocated_encoding(DisasContext *s)
+static void unallocated_encoding(DisasContext *s)
{
/* Unallocated and reserved encodings are uncategorized */
gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn_uncategorized(),
@@ -7191,6 +7210,13 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)
tcg_temp_free_ptr(tmpptr);
tcg_temp_free_i32(tcg_syn);
tcg_temp_free_i32(tcg_isread);
+ } else if (ri->type & ARM_CP_RAISES_EXC) {
+ /*
+ * The readfn or writefn might raise an exception;
+ * synchronize the CPU state in case it does.
+ */
+ gen_set_condexec(s);
+ gen_set_pc_im(s, s->pc_curr);
}
/* Handle special cases first */
@@ -8824,7 +8850,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
if (rd != 15) {
tmp3 = load_reg(s, rd);
if (insn & (1 << 6)) {
- tcg_gen_sub_i32(tmp, tmp, tmp3);
+ /*
+ * For SMMLS, we need a 64-bit subtract.
+ * Borrow caused by a non-zero multiplicand
+ * lowpart, and the correct result lowpart
+ * for rounding.
+ */
+ TCGv_i32 zero = tcg_const_i32(0);
+ tcg_gen_sub2_i32(tmp2, tmp, zero, tmp3,
+ tmp2, tmp);
+ tcg_temp_free_i32(zero);
} else {
tcg_gen_add_i32(tmp, tmp, tmp3);
}
@@ -10068,7 +10103,14 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
if (insn & (1 << 20)) {
tcg_gen_add_i32(tmp, tmp, tmp3);
} else {
- tcg_gen_sub_i32(tmp, tmp, tmp3);
+ /*
+ * For SMMLS, we need a 64-bit subtract.
+ * Borrow caused by a non-zero multiplicand lowpart,
+ * and the correct result lowpart for rounding.
+ */
+ TCGv_i32 zero = tcg_const_i32(0);
+ tcg_gen_sub2_i32(tmp2, tmp, zero, tmp3, tmp2, tmp);
+ tcg_temp_free_i32(zero);
}
tcg_temp_free_i32(tmp3);
}
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 92ef790..64304c9 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -99,8 +99,6 @@ typedef struct DisasCompare {
bool value_global;
} DisasCompare;
-void unallocated_encoding(DisasContext *s);
-
/* Share the TCG temporaries common between 32 and 64 bit modes. */
extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
extern TCGv_i64 cpu_exclusive_addr;