[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH] target-arm: Implement MRS (banked) and MSR (ban
From: |
Edgar E. Iglesias |
Subject: |
Re: [Qemu-devel] [PATCH] target-arm: Implement MRS (banked) and MSR (banked) instructions |
Date: |
Mon, 29 Feb 2016 22:22:39 +0100 |
User-agent: |
Mutt/1.5.23 (2014-03-12) |
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 <address@hidden>
>
> Signed-off-by: Peter Maydell <address@hidden>
> ---
> 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, ®no)) {
> + 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, ®no)) {
> + 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
>