diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-01-05 15:49:06 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-01-05 15:49:06 +0000 |
commit | 37064a8b6f9075e18b05bfc6d5264b138a224713 (patch) | |
tree | 797c55e04c3ae3edb29daa89b8559327f1d32852 /target-arm | |
parent | 1b9e01c1109189951a5f28b90ca037e12d61511c (diff) | |
download | qemu-37064a8b6f9075e18b05bfc6d5264b138a224713.zip qemu-37064a8b6f9075e18b05bfc6d5264b138a224713.tar.gz qemu-37064a8b6f9075e18b05bfc6d5264b138a224713.tar.bz2 |
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 <peter.maydell@linaro.org>
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/helper.c | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index 5b994d5..261d547 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); |