From 37064a8b6f9075e18b05bfc6d5264b138a224713 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 5 Jan 2012 15:49:06 +0000 Subject: [PATCH] target-arm: Ignore attempts to set invalid modes in CPSR Ignore attempts to set the CPSR mode field to an invalid value. This is UNPREDICTABLE, but we should not cpu_abort() for things a malicious guest (or a confused user on the gdbstub interface) can provoke. Signed-off-by: Peter Maydell --- target-arm/helper.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 5b994d57b..261d547d6 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -463,6 +463,26 @@ void cpu_arm_close(CPUARMState *env) g_free(env); } +static int bad_mode_switch(CPUState *env, int mode) +{ + /* Return true if it is not valid for us to switch to + * this CPU mode (ie all the UNPREDICTABLE cases in + * the ARM ARM CPSRWriteByInstr pseudocode). + */ + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_SYS: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_FIQ: + return 0; + default: + return 1; + } +} + uint32_t cpsr_read(CPUARMState *env) { int ZF; @@ -499,7 +519,15 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) } if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { - switch_mode(env, val & CPSR_M); + if (bad_mode_switch(env, val & CPSR_M)) { + /* Attempt to switch to an invalid mode: this is UNPREDICTABLE. + * We choose to ignore the attempt and leave the CPSR M field + * untouched. + */ + mask &= ~CPSR_M; + } else { + switch_mode(env, val & CPSR_M); + } } mask &= ~CACHED_CPSR_BITS; env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);