diff options
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/helper.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index 5926b15506..ff5f8955a1 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5325,11 +5325,20 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, (env->uncached_cpsr & CPSR_M) != CPSR_USER && ((env->uncached_cpsr ^ val) & mask & 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. + /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in + * v7, and has defined behaviour in v8: + * + leave CPSR.M untouched + * + allow changes to the other CPSR fields + * + set PSTATE.IL + * For user changes via the GDB stub, we don't set PSTATE.IL, + * as this would be unnecessarily harsh for a user error. */ mask &= ~CPSR_M; + if (write_type != CPSRWriteByGDBStub && + arm_feature(env, ARM_FEATURE_V8)) { + mask |= CPSR_IL; + val |= CPSR_IL; + } } else { switch_mode(env, val & CPSR_M); } |