Commit 37064a8b authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 1b9e01c1
Loading
Loading
Loading
Loading
+29 −1
Original line number Diff line number Diff line
@@ -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,8 +519,16 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
    }

    if ((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.
             */
            mask &= ~CPSR_M;
        } else {
            switch_mode(env, val & CPSR_M);
        }
    }
    mask &= ~CACHED_CPSR_BITS;
    env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
}