summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-arm/helper.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 18c82967d3..935f13b7ed 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -5490,9 +5490,16 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask,
env->daif |= val & CPSR_AIF & mask;
if (write_type != CPSRWriteRaw &&
- (env->uncached_cpsr & CPSR_M) != CPSR_USER &&
((env->uncached_cpsr ^ val) & mask & CPSR_M)) {
- if (bad_mode_switch(env, val & CPSR_M, write_type)) {
+ if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
+ /* Note that we can only get here in USR mode if this is a
+ * gdb stub write; for this case we follow the architectural
+ * behaviour for guest writes in USR mode of ignoring an attempt
+ * to switch mode. (Those are caught by translate.c for writes
+ * triggered by guest instructions.)
+ */
+ mask &= ~CPSR_M;
+ } else if (bad_mode_switch(env, val & CPSR_M, write_type)) {
/* Attempt to switch to an invalid mode: this is UNPREDICTABLE in
* v7, and has defined behaviour in v8:
* + leave CPSR.M untouched