aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-02-11 18:41:40 +0000
committerPeter Maydell <peter.maydell@linaro.org>2014-02-14 16:49:44 +0000
commit4c7e50f9a60d4b882da3a9f5522095b4b35db1ba (patch)
treeff68d5b471ee93588be211458003ebf9a876d7f4
parent90c6b83596157e8c4d1c72047389ef91a286e201 (diff)
downloadqemu-arm-4c7e50f9a60d4b882da3a9f5522095b4b35db1ba.tar.gz
target-arm: Fix VFP enables for AArch32 EL0 under AArch64 EL1
The current A32/T32 decoder bases its "is VFP/Neon enabled?" check on the FPSCR.EN bit. This is correct if EL1 is AArch32, but for an AArch64 EL1 the logic is different: it must act as if FPSCR.EN is always set. Instead, trapping must happen according to CPACR bits for cp10/cp11; these cover all of FP/Neon, including the FPSCR/FPSID/MVFR register accesses which FPSCR.EN does not affect. Add support for CPACR checks (which are also required for ARMv7, but were unimplemented because Linux happens not to use them) and make sure they generate exceptions with the correct syndrome. We actually return incorrect syndrome information for cases where FP is disabled but the specific instruction bit pattern is unallocated: strictly these should be the Uncategorized exception, not a "SIMD disabled" exception. This should be mostly harmless, and the structure of the A32/T32 VFP/Neon decoder makes it painful to put the 'FP disabled?' checks in the right places. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--target-arm/cpu.h10
-rw-r--r--target-arm/translate.c19
2 files changed, 28 insertions, 1 deletions
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 6b7b1d0861..085529fe3c 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -1269,6 +1269,8 @@ static inline int cpu_mmu_index (CPUARMState *env)
#define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT)
#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16
#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
+#define ARM_TBFLAG_CPACR_FPEN_SHIFT 17
+#define ARM_TBFLAG_CPACR_FPEN_MASK (1 << ARM_TBFLAG_CPACR_FPEN_SHIFT)
/* Bit usage when in AArch64 state */
#define ARM_TBFLAG_AA64_EL_SHIFT 0
@@ -1293,6 +1295,8 @@ static inline int cpu_mmu_index (CPUARMState *env)
(((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT)
#define ARM_TBFLAG_BSWAP_CODE(F) \
(((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT)
+#define ARM_TBFLAG_CPACR_FPEN(F) \
+ (((F) & ARM_TBFLAG_CPACR_FPEN_MASK) >> ARM_TBFLAG_CPACR_FPEN_SHIFT)
#define ARM_TBFLAG_AA64_EL(F) \
(((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT)
#define ARM_TBFLAG_AA64_FPEN(F) \
@@ -1326,9 +1330,13 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
if (privmode) {
*flags |= ARM_TBFLAG_PRIV_MASK;
}
- if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) {
+ if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)
+ || arm_el_is_aa64(env, 1)) {
*flags |= ARM_TBFLAG_VFPEN_MASK;
}
+ if (fpen == 3 || (fpen == 1 && arm_current_pl(env) != 0)) {
+ *flags |= ARM_TBFLAG_CPACR_FPEN_MASK;
+ }
}
*cs_base = 0;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 30eafc3b18..a15c23712e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2951,6 +2951,12 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
if (!arm_feature(env, ARM_FEATURE_VFP))
return 1;
+ if (!s->cpacr_fpen) {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, s->thumb));
+ return 0;
+ }
+
if (!s->vfp_enabled) {
/* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */
if ((insn & 0x0fe00fff) != 0x0ee00a10)
@@ -4228,6 +4234,12 @@ static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
TCGv_i32 tmp2;
TCGv_i64 tmp64;
+ if (!s->cpacr_fpen) {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, s->thumb));
+ return 0;
+ }
+
if (!s->vfp_enabled)
return 1;
VFP_DREG_D(rd, insn);
@@ -4950,6 +4962,12 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
TCGv_i32 tmp, tmp2, tmp3, tmp4, tmp5;
TCGv_i64 tmp64;
+ if (!s->cpacr_fpen) {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, s->thumb));
+ return 0;
+ }
+
if (!s->vfp_enabled)
return 1;
q = (insn & (1 << 6)) != 0;
@@ -10660,6 +10678,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
#if !defined(CONFIG_USER_ONLY)
dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
#endif
+ dc->cpacr_fpen = ARM_TBFLAG_CPACR_FPEN(tb->flags);
dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);