Commit 58117c9b authored by Michael Davidsaver's avatar Michael Davidsaver Committed by Peter Maydell
Browse files

armv7m: MRS/MSR: handle unprivileged access



The MRS and MSR instruction handling has a number of flaws:
 * unprivileged accesses should only be able to read
   CONTROL and the xPSR subfields, and only write APSR
   (others RAZ/WI)
 * privileged access should not be able to write xPSR
   subfields other than APSR
 * accesses to unimplemented registers should log as
   guest errors, not abort QEMU

Signed-off-by: default avatarMichael Davidsaver <mdavidsaver@gmail.com>
Reviewed-by: default avatarPeter Maydell <peter.maydell@linaro.org>
Message-id: 1484937883-1068-2-git-send-email-peter.maydell@linaro.org
[PMM: rewrote commit message]
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent ac2810de
Loading
Loading
Loading
Loading
+37 −42
Original line number Diff line number Diff line
@@ -8243,23 +8243,32 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,

uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
{
    ARMCPU *cpu = arm_env_get_cpu(env);
    uint32_t mask;
    unsigned el = arm_current_el(env);

    /* First handle registers which unprivileged can read */

    switch (reg) {
    case 0 ... 7: /* xPSR sub-fields */
        mask = 0;
        if ((reg & 1) && el) {
            mask |= 0x000001ff; /* IPSR (unpriv. reads as zero) */
        }
        if (!(reg & 4)) {
            mask |= 0xf8000000; /* APSR */
        }
        /* EPSR reads as zero */
        return xpsr_read(env) & mask;
        break;
    case 20: /* CONTROL */
        return env->v7m.control;
    }

    if (el == 0) {
        return 0; /* unprivileged reads others as zero */
    }

    switch (reg) {
    case 0: /* APSR */
        return xpsr_read(env) & 0xf8000000;
    case 1: /* IAPSR */
        return xpsr_read(env) & 0xf80001ff;
    case 2: /* EAPSR */
        return xpsr_read(env) & 0xff00fc00;
    case 3: /* xPSR */
        return xpsr_read(env) & 0xff00fdff;
    case 5: /* IPSR */
        return xpsr_read(env) & 0x000001ff;
    case 6: /* EPSR */
        return xpsr_read(env) & 0x0700fc00;
    case 7: /* IEPSR */
        return xpsr_read(env) & 0x0700edff;
    case 8: /* MSP */
        return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13];
    case 9: /* PSP */
@@ -8271,40 +8280,26 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
        return env->v7m.basepri;
    case 19: /* FAULTMASK */
        return (env->daif & PSTATE_F) != 0;
    case 20: /* CONTROL */
        return env->v7m.control;
    default:
        /* ??? For debugging only.  */
        cpu_abort(CPU(cpu), "Unimplemented system register read (%d)\n", reg);
        qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special"
                                       " register %d\n", reg);
        return 0;
    }
}

void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
{
    ARMCPU *cpu = arm_env_get_cpu(env);
    if (arm_current_el(env) == 0 && reg > 7) {
        /* only xPSR sub-fields may be written by unprivileged */
        return;
    }

    switch (reg) {
    case 0: /* APSR */
        xpsr_write(env, val, 0xf8000000);
        break;
    case 1: /* IAPSR */
        xpsr_write(env, val, 0xf8000000);
        break;
    case 2: /* EAPSR */
        xpsr_write(env, val, 0xfe00fc00);
        break;
    case 3: /* xPSR */
        xpsr_write(env, val, 0xfe00fc00);
        break;
    case 5: /* IPSR */
        /* IPSR bits are readonly.  */
        break;
    case 6: /* EPSR */
        xpsr_write(env, val, 0x0600fc00);
        break;
    case 7: /* IEPSR */
        xpsr_write(env, val, 0x0600fc00);
    case 0 ... 7: /* xPSR sub-fields */
        /* only APSR is actually writable */
        if (reg & 4) {
            xpsr_write(env, val, 0xf8000000); /* APSR */
        }
        break;
    case 8: /* MSP */
        if (env->v7m.current_sp)
@@ -8345,8 +8340,8 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
        switch_v7m_sp(env, (val & 2) != 0);
        break;
    default:
        /* ??? For debugging only.  */
        cpu_abort(CPU(cpu), "Unimplemented system register write (%d)\n", reg);
        qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special"
                                       " register %d\n", reg);
        return;
    }
}