diff mbox

target-arm: Implement MRS (banked) and MSR (banked) instructions

Message ID 1456762734-23939-1-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell Feb. 29, 2016, 4:18 p.m. UTC
Starting with the ARMv7 Virtualization Extensions, the A32 and T32
instruction sets provide instructions "MSR (banked)" and "MRS
(banked)" which can be used to access registers for a mode other
than the current one:
 * R<m>_<mode>
 * ELR_hyp
 * SPSR_<mode>

Implement the missing instructions.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
We don't support EL2 yet, but you can get at these on a v8 CPU in
32-bit EL1 if EL3 is enabled. Obviously there's not going to be much
32-bit EL1 code out there that uses the insns though, as it wouldn't
work on v7 if it did...

 target-arm/helper.h    |   3 +
 target-arm/op_helper.c | 120 ++++++++++++++++++++++++
 target-arm/translate.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 366 insertions(+), 3 deletions(-)

Comments

Sergey Fedorov Feb. 29, 2016, 4:24 p.m. UTC | #1
On 29.02.2016 19:18, Peter Maydell wrote:
> Starting with the ARMv7 Virtualization Extensions, the A32 and T32
> instruction sets provide instructions "MSR (banked)" and "MRS
> (banked)" which can be used to access registers for a mode other
> than the current one:
>  * R<m>_<mode>
>  * ELR_hyp
>  * SPSR_<mode>
>
> Implement the missing instructions.

Likely, there is no disassembling support in QEMU for these instructions
as well. Are you going to add it?

Best regards,
Sergey

>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> We don't support EL2 yet, but you can get at these on a v8 CPU in
> 32-bit EL1 if EL3 is enabled. Obviously there's not going to be much
> 32-bit EL1 code out there that uses the insns though, as it wouldn't
> work on v7 if it did...
Peter Maydell Feb. 29, 2016, 4:25 p.m. UTC | #2
On 29 February 2016 at 16:24, Sergey Fedorov <serge.fdrv@gmail.com> wrote:
> On 29.02.2016 19:18, Peter Maydell wrote:
>> Starting with the ARMv7 Virtualization Extensions, the A32 and T32
>> instruction sets provide instructions "MSR (banked)" and "MRS
>> (banked)" which can be used to access registers for a mode other
>> than the current one:
>>  * R<m>_<mode>
>>  * ELR_hyp
>>  * SPSR_<mode>
>>
>> Implement the missing instructions.
>
> Likely, there is no disassembling support in QEMU for these instructions
> as well. Are you going to add it?

No, I don't plan to.

thanks
-- PMM
Edgar E. Iglesias Feb. 29, 2016, 9:22 p.m. UTC | #3
On Mon, Feb 29, 2016 at 04:18:54PM +0000, Peter Maydell wrote:
> Starting with the ARMv7 Virtualization Extensions, the A32 and T32
> instruction sets provide instructions "MSR (banked)" and "MRS
> (banked)" which can be used to access registers for a mode other
> than the current one:
>  * R<m>_<mode>
>  * ELR_hyp
>  * SPSR_<mode>
> 
> Implement the missing instructions.

Wow, these insns were a bit messy to follow... Anyway, AFAIK this looks OK:

Acked-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>


> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> We don't support EL2 yet, but you can get at these on a v8 CPU in
> 32-bit EL1 if EL3 is enabled. Obviously there's not going to be much
> 32-bit EL1 code out there that uses the insns though, as it wouldn't
> work on v7 if it did...
> 
>  target-arm/helper.h    |   3 +
>  target-arm/op_helper.c | 120 ++++++++++++++++++++++++
>  target-arm/translate.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 366 insertions(+), 3 deletions(-)
> 
> diff --git a/target-arm/helper.h b/target-arm/helper.h
> index ea13202..de3c644 100644
> --- a/target-arm/helper.h
> +++ b/target-arm/helper.h
> @@ -76,6 +76,9 @@ DEF_HELPER_1(exception_return, void, env)
>  DEF_HELPER_2(get_r13_banked, i32, env, i32)
>  DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
>  
> +DEF_HELPER_3(mrs_banked, i32, env, i32, i32)
> +DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
> +
>  DEF_HELPER_2(get_user_reg, i32, env, i32)
>  DEF_HELPER_3(set_user_reg, void, env, i32, i32)
>  
> diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
> index 4881e34..a2afdd8 100644
> --- a/target-arm/op_helper.c
> +++ b/target-arm/op_helper.c
> @@ -489,6 +489,126 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
>      }
>  }
>  
> +static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
> +                                      uint32_t regno)
> +{
> +    /* Raise an exception if the requested access is one of the UNPREDICTABLE
> +     * cases; otherwise return. This broadly corresponds to the pseudocode
> +     * BankedRegisterAccessValid() and SPSRAccessValid(),
> +     * except that we have already handled some cases at translate time.
> +     */
> +    int curmode = env->uncached_cpsr & CPSR_M;
> +
> +    if (curmode == tgtmode) {
> +        goto undef;
> +    }
> +
> +    if (tgtmode == ARM_CPU_MODE_USR) {
> +        switch (regno) {
> +        case 8 ... 12:
> +            if (curmode != ARM_CPU_MODE_FIQ) {
> +                goto undef;
> +            }
> +            break;
> +        case 13:
> +            if (curmode == ARM_CPU_MODE_SYS) {
> +                goto undef;
> +            }
> +            break;
> +        case 14:
> +            if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) {
> +                goto undef;
> +            }
> +            break;
> +        default:
> +            break;
> +        }
> +    }
> +
> +    if (tgtmode == ARM_CPU_MODE_HYP) {
> +        switch (regno) {
> +        case 17: /* ELR_Hyp */
> +            if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
> +                goto undef;
> +            }
> +            break;
> +        default:
> +            if (curmode != ARM_CPU_MODE_MON) {
> +                goto undef;
> +            }
> +            break;
> +        }
> +    }
> +
> +    return;
> +
> +undef:
> +    raise_exception(env, EXCP_UDEF, syn_uncategorized(),
> +                    exception_target_el(env));
> +}
> +
> +void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode,
> +                        uint32_t regno)
> +{
> +    msr_mrs_banked_exc_checks(env, tgtmode, regno);
> +
> +    switch (regno) {
> +    case 16: /* SPSRs */
> +        env->banked_spsr[bank_number(tgtmode)] = value;
> +        break;
> +    case 17: /* ELR_Hyp */
> +        env->elr_el[2] = value;
> +        break;
> +    case 13:
> +        env->banked_r13[bank_number(tgtmode)] = value;
> +        break;
> +    case 14:
> +        env->banked_r14[bank_number(tgtmode)] = value;
> +        break;
> +    case 8 ... 12:
> +        switch (tgtmode) {
> +        case ARM_CPU_MODE_USR:
> +            env->usr_regs[regno - 8] = value;
> +            break;
> +        case ARM_CPU_MODE_FIQ:
> +            env->fiq_regs[regno - 8] = value;
> +            break;
> +        default:
> +            g_assert_not_reached();
> +        }
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
> +uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno)
> +{
> +    msr_mrs_banked_exc_checks(env, tgtmode, regno);
> +
> +    switch (regno) {
> +    case 16: /* SPSRs */
> +        return env->banked_spsr[bank_number(tgtmode)];
> +    case 17: /* ELR_Hyp */
> +        return env->elr_el[2];
> +    case 13:
> +        return env->banked_r13[bank_number(tgtmode)];
> +    case 14:
> +        return env->banked_r14[bank_number(tgtmode)];
> +    case 8 ... 12:
> +        switch (tgtmode) {
> +        case ARM_CPU_MODE_USR:
> +            return env->usr_regs[regno - 8];
> +        case ARM_CPU_MODE_FIQ:
> +            return env->fiq_regs[regno - 8];
> +        default:
> +            g_assert_not_reached();
> +        }
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
>  void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
>                                   uint32_t isread)
>  {
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 413f7de..a8dd490 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -4088,6 +4088,195 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val
>      return gen_set_psr(s, mask, spsr, tmp);
>  }
>  
> +static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn,
> +                                     int *tgtmode, int *regno)
> +{
> +    /* Decode the r and sysm fields of MSR/MRS banked accesses into
> +     * the target mode and register number, and identify the various
> +     * unpredictable cases.
> +     * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if:
> +     *  + executed in user mode
> +     *  + using R15 as the src/dest register
> +     *  + accessing an unimplemented register
> +     *  + accessing a register that's inaccessible at current PL/security state*
> +     *  + accessing a register that you could access with a different insn
> +     * We choose to UNDEF in all these cases.
> +     * Since we don't know which of the various AArch32 modes we are in
> +     * we have to defer some checks to runtime.
> +     * Accesses to Monitor mode registers from Secure EL1 (which implies
> +     * that EL3 is AArch64) must trap to EL3.
> +     *
> +     * If the access checks fail this function will emit code to take
> +     * an exception and return false. Otherwise it will return true,
> +     * and set *tgtmode and *regno appropriately.
> +     */
> +    int exc_target = default_exception_el(s);
> +
> +    /* These instructions are present only in ARMv8, or in ARMv7 with the
> +     * Virtualization Extensions.
> +     */
> +    if (!arm_dc_feature(s, ARM_FEATURE_V8) &&
> +        !arm_dc_feature(s, ARM_FEATURE_EL2)) {
> +        goto undef;
> +    }
> +
> +    if (IS_USER(s) || rn == 15) {
> +        goto undef;
> +    }
> +
> +    /* The table in the v8 ARM ARM section F5.2.3 describes the encoding
> +     * of registers into (r, sysm).
> +     */
> +    if (r) {
> +        /* SPSRs for other modes */
> +        switch (sysm) {
> +        case 0xe: /* SPSR_fiq */
> +            *tgtmode = ARM_CPU_MODE_FIQ;
> +            break;
> +        case 0x10: /* SPSR_irq */
> +            *tgtmode = ARM_CPU_MODE_IRQ;
> +            break;
> +        case 0x12: /* SPSR_svc */
> +            *tgtmode = ARM_CPU_MODE_SVC;
> +            break;
> +        case 0x14: /* SPSR_abt */
> +            *tgtmode = ARM_CPU_MODE_ABT;
> +            break;
> +        case 0x16: /* SPSR_und */
> +            *tgtmode = ARM_CPU_MODE_UND;
> +            break;
> +        case 0x1c: /* SPSR_mon */
> +            *tgtmode = ARM_CPU_MODE_MON;
> +            break;
> +        case 0x1e: /* SPSR_hyp */
> +            *tgtmode = ARM_CPU_MODE_HYP;
> +            break;
> +        default: /* unallocated */
> +            goto undef;
> +        }
> +        /* We arbitrarily assign SPSR a register number of 16. */
> +        *regno = 16;
> +    } else {
> +        /* general purpose registers for other modes */
> +        switch (sysm) {
> +        case 0x0 ... 0x6:   /* 0b00xxx : r8_usr ... r14_usr */
> +            *tgtmode = ARM_CPU_MODE_USR;
> +            *regno = sysm + 8;
> +            break;
> +        case 0x8 ... 0xe:   /* 0b01xxx : r8_fiq ... r14_fiq */
> +            *tgtmode = ARM_CPU_MODE_FIQ;
> +            *regno = sysm;
> +            break;
> +        case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */
> +            *tgtmode = ARM_CPU_MODE_IRQ;
> +            *regno = sysm & 1 ? 13 : 14;
> +            break;
> +        case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */
> +            *tgtmode = ARM_CPU_MODE_SVC;
> +            *regno = sysm & 1 ? 13 : 14;
> +            break;
> +        case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */
> +            *tgtmode = ARM_CPU_MODE_ABT;
> +            *regno = sysm & 1 ? 13 : 14;
> +            break;
> +        case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */
> +            *tgtmode = ARM_CPU_MODE_UND;
> +            *regno = sysm & 1 ? 13 : 14;
> +            break;
> +        case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */
> +            *tgtmode = ARM_CPU_MODE_MON;
> +            *regno = sysm & 1 ? 13 : 14;
> +            break;
> +        case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */
> +            *tgtmode = ARM_CPU_MODE_HYP;
> +            /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */
> +            *regno = sysm & 1 ? 13 : 17;
> +            break;
> +        default: /* unallocated */
> +            goto undef;
> +        }
> +    }
> +
> +    /* Catch the 'accessing inaccessible register' cases we can detect
> +     * at translate time.
> +     */
> +    switch (*tgtmode) {
> +    case ARM_CPU_MODE_MON:
> +        if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) {
> +            goto undef;
> +        }
> +        if (s->current_el == 1) {
> +            /* If we're in Secure EL1 (which implies that EL3 is AArch64)
> +             * then accesses to Mon registers trap to EL3
> +             */
> +            exc_target = 3;
> +            goto undef;
> +        }
> +        break;
> +    case ARM_CPU_MODE_HYP:
> +        /* Note that we can forbid accesses from EL2 here because they
> +         * must be from Hyp mode itself
> +         */
> +        if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) {
> +            goto undef;
> +        }
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return true;
> +
> +undef:
> +    /* If we get here then some access check did not pass */
> +    gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target);
> +    return false;
> +}
> +
> +static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn)
> +{
> +    TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
> +    int tgtmode, regno;
> +
> +    if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
> +        return;
> +    }
> +
> +    /* Sync state because msr_banked() can raise exceptions */
> +    gen_set_condexec(s);
> +    gen_set_pc_im(s, s->pc - 4);
> +    tcg_reg = load_reg(s, rn);
> +    tcg_tgtmode = tcg_const_i32(tgtmode);
> +    tcg_regno = tcg_const_i32(regno);
> +    gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno);
> +    tcg_temp_free_i32(tcg_tgtmode);
> +    tcg_temp_free_i32(tcg_regno);
> +    tcg_temp_free_i32(tcg_reg);
> +    s->is_jmp = DISAS_UPDATE;
> +}
> +
> +static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
> +{
> +    TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
> +    int tgtmode, regno;
> +
> +    if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
> +        return;
> +    }
> +
> +    /* Sync state because mrs_banked() can raise exceptions */
> +    gen_set_condexec(s);
> +    gen_set_pc_im(s, s->pc - 4);
> +    tcg_reg = tcg_temp_new_i32();
> +    tcg_tgtmode = tcg_const_i32(tgtmode);
> +    tcg_regno = tcg_const_i32(regno);
> +    gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno);
> +    tcg_temp_free_i32(tcg_tgtmode);
> +    tcg_temp_free_i32(tcg_regno);
> +    store_reg(s, rn, tcg_reg);
> +    s->is_jmp = DISAS_UPDATE;
> +}
> +
>  /* Generate an old-style exception return. Marks pc as dead. */
>  static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
>  {
> @@ -7950,7 +8139,26 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
>          sh = (insn >> 4) & 0xf;
>          rm = insn & 0xf;
>          switch (sh) {
> -        case 0x0: /* move program status register */
> +        case 0x0: /* MSR, MRS */
> +            if (insn & (1 << 9)) {
> +                /* MSR (banked) and MRS (banked) */
> +                int sysm = extract32(insn, 16, 4) |
> +                    (extract32(insn, 8, 1) << 4);
> +                int r = extract32(insn, 22, 1);
> +
> +                if (op1 & 1) {
> +                    /* MSR (banked) */
> +                    gen_msr_banked(s, r, sysm, rm);
> +                } else {
> +                    /* MRS (banked) */
> +                    int rd = extract32(insn, 12, 4);
> +
> +                    gen_mrs_banked(s, r, sysm, rd);
> +                }
> +                break;
> +            }
> +
> +            /* MSR, MRS (for PSRs) */
>              if (op1 & 1) {
>                  /* PSR = reg */
>                  tmp = load_reg(s, rm);
> @@ -10055,6 +10263,18 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
>                          if (arm_dc_feature(s, ARM_FEATURE_M)) {
>                              goto illegal_op;
>                          }
> +
> +                        if (extract32(insn, 5, 1)) {
> +                            /* MSR (banked) */
> +                            int sysm = extract32(insn, 8, 4) |
> +                                (extract32(insn, 4, 1) << 4);
> +                            int r = op & 1;
> +
> +                            gen_msr_banked(s, r, sysm, rm);
> +                            break;
> +                        }
> +
> +                        /* MSR (for PSRs) */
>                          tmp = load_reg(s, rn);
>                          if (gen_set_psr(s,
>                                msr_mask(s, (insn >> 8) & 0xf, op == 1),
> @@ -10127,7 +10347,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
>                          tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
>                          gen_exception_return(s, tmp);
>                          break;
> -                    case 6: /* mrs cpsr.  */
> +                    case 6: /* MRS */
> +                        if (extract32(insn, 5, 1)) {
> +                            /* MRS (banked) */
> +                            int sysm = extract32(insn, 16, 4) |
> +                                (extract32(insn, 4, 1) << 4);
> +
> +                            gen_mrs_banked(s, 0, sysm, rd);
> +                            break;
> +                        }
> +
> +                        /* mrs cpsr */
>                          tmp = tcg_temp_new_i32();
>                          if (arm_dc_feature(s, ARM_FEATURE_M)) {
>                              addr = tcg_const_i32(insn & 0xff);
> @@ -10138,7 +10368,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
>                          }
>                          store_reg(s, rd, tmp);
>                          break;
> -                    case 7: /* mrs spsr.  */
> +                    case 7: /* MRS */
> +                        if (extract32(insn, 5, 1)) {
> +                            /* MRS (banked) */
> +                            int sysm = extract32(insn, 16, 4) |
> +                                (extract32(insn, 4, 1) << 4);
> +
> +                            gen_mrs_banked(s, 1, sysm, rd);
> +                            break;
> +                        }
> +
> +                        /* mrs spsr.  */
>                          /* Not accessible in user mode.  */
>                          if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
>                              goto illegal_op;
> -- 
> 1.9.1
>
diff mbox

Patch

diff --git a/target-arm/helper.h b/target-arm/helper.h
index ea13202..de3c644 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -76,6 +76,9 @@  DEF_HELPER_1(exception_return, void, env)
 DEF_HELPER_2(get_r13_banked, i32, env, i32)
 DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
 
+DEF_HELPER_3(mrs_banked, i32, env, i32, i32)
+DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
+
 DEF_HELPER_2(get_user_reg, i32, env, i32)
 DEF_HELPER_3(set_user_reg, void, env, i32, i32)
 
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 4881e34..a2afdd8 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -489,6 +489,126 @@  uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
     }
 }
 
+static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
+                                      uint32_t regno)
+{
+    /* Raise an exception if the requested access is one of the UNPREDICTABLE
+     * cases; otherwise return. This broadly corresponds to the pseudocode
+     * BankedRegisterAccessValid() and SPSRAccessValid(),
+     * except that we have already handled some cases at translate time.
+     */
+    int curmode = env->uncached_cpsr & CPSR_M;
+
+    if (curmode == tgtmode) {
+        goto undef;
+    }
+
+    if (tgtmode == ARM_CPU_MODE_USR) {
+        switch (regno) {
+        case 8 ... 12:
+            if (curmode != ARM_CPU_MODE_FIQ) {
+                goto undef;
+            }
+            break;
+        case 13:
+            if (curmode == ARM_CPU_MODE_SYS) {
+                goto undef;
+            }
+            break;
+        case 14:
+            if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) {
+                goto undef;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (tgtmode == ARM_CPU_MODE_HYP) {
+        switch (regno) {
+        case 17: /* ELR_Hyp */
+            if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
+                goto undef;
+            }
+            break;
+        default:
+            if (curmode != ARM_CPU_MODE_MON) {
+                goto undef;
+            }
+            break;
+        }
+    }
+
+    return;
+
+undef:
+    raise_exception(env, EXCP_UDEF, syn_uncategorized(),
+                    exception_target_el(env));
+}
+
+void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode,
+                        uint32_t regno)
+{
+    msr_mrs_banked_exc_checks(env, tgtmode, regno);
+
+    switch (regno) {
+    case 16: /* SPSRs */
+        env->banked_spsr[bank_number(tgtmode)] = value;
+        break;
+    case 17: /* ELR_Hyp */
+        env->elr_el[2] = value;
+        break;
+    case 13:
+        env->banked_r13[bank_number(tgtmode)] = value;
+        break;
+    case 14:
+        env->banked_r14[bank_number(tgtmode)] = value;
+        break;
+    case 8 ... 12:
+        switch (tgtmode) {
+        case ARM_CPU_MODE_USR:
+            env->usr_regs[regno - 8] = value;
+            break;
+        case ARM_CPU_MODE_FIQ:
+            env->fiq_regs[regno - 8] = value;
+            break;
+        default:
+            g_assert_not_reached();
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno)
+{
+    msr_mrs_banked_exc_checks(env, tgtmode, regno);
+
+    switch (regno) {
+    case 16: /* SPSRs */
+        return env->banked_spsr[bank_number(tgtmode)];
+    case 17: /* ELR_Hyp */
+        return env->elr_el[2];
+    case 13:
+        return env->banked_r13[bank_number(tgtmode)];
+    case 14:
+        return env->banked_r14[bank_number(tgtmode)];
+    case 8 ... 12:
+        switch (tgtmode) {
+        case ARM_CPU_MODE_USR:
+            return env->usr_regs[regno - 8];
+        case ARM_CPU_MODE_FIQ:
+            return env->fiq_regs[regno - 8];
+        default:
+            g_assert_not_reached();
+        }
+    default:
+        g_assert_not_reached();
+    }
+}
+
 void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
                                  uint32_t isread)
 {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 413f7de..a8dd490 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -4088,6 +4088,195 @@  static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val
     return gen_set_psr(s, mask, spsr, tmp);
 }
 
+static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn,
+                                     int *tgtmode, int *regno)
+{
+    /* Decode the r and sysm fields of MSR/MRS banked accesses into
+     * the target mode and register number, and identify the various
+     * unpredictable cases.
+     * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if:
+     *  + executed in user mode
+     *  + using R15 as the src/dest register
+     *  + accessing an unimplemented register
+     *  + accessing a register that's inaccessible at current PL/security state*
+     *  + accessing a register that you could access with a different insn
+     * We choose to UNDEF in all these cases.
+     * Since we don't know which of the various AArch32 modes we are in
+     * we have to defer some checks to runtime.
+     * Accesses to Monitor mode registers from Secure EL1 (which implies
+     * that EL3 is AArch64) must trap to EL3.
+     *
+     * If the access checks fail this function will emit code to take
+     * an exception and return false. Otherwise it will return true,
+     * and set *tgtmode and *regno appropriately.
+     */
+    int exc_target = default_exception_el(s);
+
+    /* These instructions are present only in ARMv8, or in ARMv7 with the
+     * Virtualization Extensions.
+     */
+    if (!arm_dc_feature(s, ARM_FEATURE_V8) &&
+        !arm_dc_feature(s, ARM_FEATURE_EL2)) {
+        goto undef;
+    }
+
+    if (IS_USER(s) || rn == 15) {
+        goto undef;
+    }
+
+    /* The table in the v8 ARM ARM section F5.2.3 describes the encoding
+     * of registers into (r, sysm).
+     */
+    if (r) {
+        /* SPSRs for other modes */
+        switch (sysm) {
+        case 0xe: /* SPSR_fiq */
+            *tgtmode = ARM_CPU_MODE_FIQ;
+            break;
+        case 0x10: /* SPSR_irq */
+            *tgtmode = ARM_CPU_MODE_IRQ;
+            break;
+        case 0x12: /* SPSR_svc */
+            *tgtmode = ARM_CPU_MODE_SVC;
+            break;
+        case 0x14: /* SPSR_abt */
+            *tgtmode = ARM_CPU_MODE_ABT;
+            break;
+        case 0x16: /* SPSR_und */
+            *tgtmode = ARM_CPU_MODE_UND;
+            break;
+        case 0x1c: /* SPSR_mon */
+            *tgtmode = ARM_CPU_MODE_MON;
+            break;
+        case 0x1e: /* SPSR_hyp */
+            *tgtmode = ARM_CPU_MODE_HYP;
+            break;
+        default: /* unallocated */
+            goto undef;
+        }
+        /* We arbitrarily assign SPSR a register number of 16. */
+        *regno = 16;
+    } else {
+        /* general purpose registers for other modes */
+        switch (sysm) {
+        case 0x0 ... 0x6:   /* 0b00xxx : r8_usr ... r14_usr */
+            *tgtmode = ARM_CPU_MODE_USR;
+            *regno = sysm + 8;
+            break;
+        case 0x8 ... 0xe:   /* 0b01xxx : r8_fiq ... r14_fiq */
+            *tgtmode = ARM_CPU_MODE_FIQ;
+            *regno = sysm;
+            break;
+        case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */
+            *tgtmode = ARM_CPU_MODE_IRQ;
+            *regno = sysm & 1 ? 13 : 14;
+            break;
+        case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */
+            *tgtmode = ARM_CPU_MODE_SVC;
+            *regno = sysm & 1 ? 13 : 14;
+            break;
+        case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */
+            *tgtmode = ARM_CPU_MODE_ABT;
+            *regno = sysm & 1 ? 13 : 14;
+            break;
+        case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */
+            *tgtmode = ARM_CPU_MODE_UND;
+            *regno = sysm & 1 ? 13 : 14;
+            break;
+        case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */
+            *tgtmode = ARM_CPU_MODE_MON;
+            *regno = sysm & 1 ? 13 : 14;
+            break;
+        case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */
+            *tgtmode = ARM_CPU_MODE_HYP;
+            /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */
+            *regno = sysm & 1 ? 13 : 17;
+            break;
+        default: /* unallocated */
+            goto undef;
+        }
+    }
+
+    /* Catch the 'accessing inaccessible register' cases we can detect
+     * at translate time.
+     */
+    switch (*tgtmode) {
+    case ARM_CPU_MODE_MON:
+        if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) {
+            goto undef;
+        }
+        if (s->current_el == 1) {
+            /* If we're in Secure EL1 (which implies that EL3 is AArch64)
+             * then accesses to Mon registers trap to EL3
+             */
+            exc_target = 3;
+            goto undef;
+        }
+        break;
+    case ARM_CPU_MODE_HYP:
+        /* Note that we can forbid accesses from EL2 here because they
+         * must be from Hyp mode itself
+         */
+        if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) {
+            goto undef;
+        }
+        break;
+    default:
+        break;
+    }
+
+    return true;
+
+undef:
+    /* If we get here then some access check did not pass */
+    gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target);
+    return false;
+}
+
+static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn)
+{
+    TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
+    int tgtmode, regno;
+
+    if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
+        return;
+    }
+
+    /* Sync state because msr_banked() can raise exceptions */
+    gen_set_condexec(s);
+    gen_set_pc_im(s, s->pc - 4);
+    tcg_reg = load_reg(s, rn);
+    tcg_tgtmode = tcg_const_i32(tgtmode);
+    tcg_regno = tcg_const_i32(regno);
+    gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno);
+    tcg_temp_free_i32(tcg_tgtmode);
+    tcg_temp_free_i32(tcg_regno);
+    tcg_temp_free_i32(tcg_reg);
+    s->is_jmp = DISAS_UPDATE;
+}
+
+static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
+{
+    TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
+    int tgtmode, regno;
+
+    if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
+        return;
+    }
+
+    /* Sync state because mrs_banked() can raise exceptions */
+    gen_set_condexec(s);
+    gen_set_pc_im(s, s->pc - 4);
+    tcg_reg = tcg_temp_new_i32();
+    tcg_tgtmode = tcg_const_i32(tgtmode);
+    tcg_regno = tcg_const_i32(regno);
+    gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno);
+    tcg_temp_free_i32(tcg_tgtmode);
+    tcg_temp_free_i32(tcg_regno);
+    store_reg(s, rn, tcg_reg);
+    s->is_jmp = DISAS_UPDATE;
+}
+
 /* Generate an old-style exception return. Marks pc as dead. */
 static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
 {
@@ -7950,7 +8139,26 @@  static void disas_arm_insn(DisasContext *s, unsigned int insn)
         sh = (insn >> 4) & 0xf;
         rm = insn & 0xf;
         switch (sh) {
-        case 0x0: /* move program status register */
+        case 0x0: /* MSR, MRS */
+            if (insn & (1 << 9)) {
+                /* MSR (banked) and MRS (banked) */
+                int sysm = extract32(insn, 16, 4) |
+                    (extract32(insn, 8, 1) << 4);
+                int r = extract32(insn, 22, 1);
+
+                if (op1 & 1) {
+                    /* MSR (banked) */
+                    gen_msr_banked(s, r, sysm, rm);
+                } else {
+                    /* MRS (banked) */
+                    int rd = extract32(insn, 12, 4);
+
+                    gen_mrs_banked(s, r, sysm, rd);
+                }
+                break;
+            }
+
+            /* MSR, MRS (for PSRs) */
             if (op1 & 1) {
                 /* PSR = reg */
                 tmp = load_reg(s, rm);
@@ -10055,6 +10263,18 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         if (arm_dc_feature(s, ARM_FEATURE_M)) {
                             goto illegal_op;
                         }
+
+                        if (extract32(insn, 5, 1)) {
+                            /* MSR (banked) */
+                            int sysm = extract32(insn, 8, 4) |
+                                (extract32(insn, 4, 1) << 4);
+                            int r = op & 1;
+
+                            gen_msr_banked(s, r, sysm, rm);
+                            break;
+                        }
+
+                        /* MSR (for PSRs) */
                         tmp = load_reg(s, rn);
                         if (gen_set_psr(s,
                               msr_mask(s, (insn >> 8) & 0xf, op == 1),
@@ -10127,7 +10347,17 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
                         gen_exception_return(s, tmp);
                         break;
-                    case 6: /* mrs cpsr.  */
+                    case 6: /* MRS */
+                        if (extract32(insn, 5, 1)) {
+                            /* MRS (banked) */
+                            int sysm = extract32(insn, 16, 4) |
+                                (extract32(insn, 4, 1) << 4);
+
+                            gen_mrs_banked(s, 0, sysm, rd);
+                            break;
+                        }
+
+                        /* mrs cpsr */
                         tmp = tcg_temp_new_i32();
                         if (arm_dc_feature(s, ARM_FEATURE_M)) {
                             addr = tcg_const_i32(insn & 0xff);
@@ -10138,7 +10368,17 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         }
                         store_reg(s, rd, tmp);
                         break;
-                    case 7: /* mrs spsr.  */
+                    case 7: /* MRS */
+                        if (extract32(insn, 5, 1)) {
+                            /* MRS (banked) */
+                            int sysm = extract32(insn, 16, 4) |
+                                (extract32(insn, 4, 1) << 4);
+
+                            gen_mrs_banked(s, 1, sysm, rd);
+                            break;
+                        }
+
+                        /* mrs spsr.  */
                         /* Not accessible in user mode.  */
                         if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
                             goto illegal_op;