summaryrefslogtreecommitdiffstats
path: root/target-arm
diff options
context:
space:
mode:
authorPeter Maydell2014-08-19 19:56:26 +0200
committerPeter Maydell2014-08-19 20:02:03 +0200
commit4051e12c5df1c46b542b28ed43f1614a42245ecf (patch)
tree06aee211a240ff8caa2712de853f62e6c1462c1e /target-arm
parenttarget-arm: Adjust debug ID registers per-CPU (diff)
downloadqemu-4051e12c5df1c46b542b28ed43f1614a42245ecf.tar.gz
qemu-4051e12c5df1c46b542b28ed43f1614a42245ecf.tar.xz
qemu-4051e12c5df1c46b542b28ed43f1614a42245ecf.zip
target-arm: Don't allow AArch32 to access RES0 CPSR bits
The CPSR has a new-in-v8 execution state bit (IL), and also some state which has effects in AArch32 but appears only in the SPSR format (SS) but is RES0 in the CPSR. Add the IL bit to CPSR_EXEC, and enforce that guest direct reads and writes to CPSR can't read or write the RES0 bits, so the guest can't get at the SS bit which we store in uncached_cpsr. This includes not permitting exception returns to copy reserved bits from an SPSR into CPSR. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Diffstat (limited to 'target-arm')
-rw-r--r--target-arm/cpu.h12
-rw-r--r--target-arm/op_helper.c2
-rw-r--r--target-arm/translate.c13
3 files changed, 18 insertions, 9 deletions
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 79205ba335..8380c136e2 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -411,7 +411,13 @@ int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
#define CPSR_E (1U << 9)
#define CPSR_IT_2_7 (0xfc00U)
#define CPSR_GE (0xfU << 16)
-#define CPSR_RESERVED (0xfU << 20)
+#define CPSR_IL (1U << 20)
+/* Note that the RESERVED bits include bit 21, which is PSTATE_SS in
+ * an AArch64 SPSR but RES0 in AArch32 SPSR and CPSR. In QEMU we use
+ * env->uncached_cpsr bit 21 to store PSTATE.SS when executing in AArch32,
+ * where it is live state but not accessible to the AArch32 code.
+ */
+#define CPSR_RESERVED (0x7U << 21)
#define CPSR_J (1U << 24)
#define CPSR_IT_0_1 (3U << 25)
#define CPSR_Q (1U << 27)
@@ -428,7 +434,9 @@ int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
/* Bits writable in user mode. */
#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
/* Execution state bits. MRS read as zero, MSR writes ignored. */
-#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
+#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J | CPSR_IL)
+/* Mask of bits which may be set by exception return copying them from SPSR */
+#define CPSR_ERET_MASK (~CPSR_RESERVED)
#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */
#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 25ad902e04..180a4a096b 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -258,7 +258,7 @@ void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp,
uint32_t HELPER(cpsr_read)(CPUARMState *env)
{
- return cpsr_read(env) & ~CPSR_EXEC;
+ return cpsr_read(env) & ~(CPSR_EXEC | CPSR_RESERVED);
}
void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask)
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 40121858d6..4cde3096d4 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -3908,9 +3908,10 @@ static uint32_t msr_mask(CPUARMState *env, DisasContext *s, int flags, int spsr)
mask &= ~(CPSR_E | CPSR_GE);
if (!arm_feature(env, ARM_FEATURE_THUMB2))
mask &= ~CPSR_IT;
- /* Mask out execution state bits. */
- if (!spsr)
- mask &= ~CPSR_EXEC;
+ /* Mask out execution state and reserved bits. */
+ if (!spsr) {
+ mask &= ~(CPSR_EXEC | CPSR_RESERVED);
+ }
/* Mask out privileged bits. */
if (IS_USER(s))
mask &= CPSR_USER;
@@ -3954,7 +3955,7 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
TCGv_i32 tmp;
store_reg(s, 15, pc);
tmp = load_cpu_field(spsr);
- gen_set_cpsr(tmp, 0xffffffff);
+ gen_set_cpsr(tmp, CPSR_ERET_MASK);
tcg_temp_free_i32(tmp);
s->is_jmp = DISAS_UPDATE;
}
@@ -3962,7 +3963,7 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
/* Generate a v6 exception return. Marks both values as dead. */
static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr)
{
- gen_set_cpsr(cpsr, 0xffffffff);
+ gen_set_cpsr(cpsr, CPSR_ERET_MASK);
tcg_temp_free_i32(cpsr);
store_reg(s, 15, pc);
s->is_jmp = DISAS_UPDATE;
@@ -8836,7 +8837,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
if ((insn & (1 << 22)) && !user) {
/* Restore CPSR from SPSR. */
tmp = load_cpu_field(spsr);
- gen_set_cpsr(tmp, 0xffffffff);
+ gen_set_cpsr(tmp, CPSR_ERET_MASK);
tcg_temp_free_i32(tmp);
s->is_jmp = DISAS_UPDATE;
}