[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (ba
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (banked) instructions |
Date: |
Wed, 16 Mar 2016 17:18:16 +0000 |
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 <address@hidden>
Acked-by: Edgar E. Iglesias <address@hidden>
Message-id: address@hidden
---
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 e3d09d9..84aa637 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -77,6 +77,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 92fde0a..d626ff1 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -494,6 +494,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 025c7a5..2827519 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -4160,6 +4160,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)
{
@@ -8022,7 +8211,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);
@@ -10133,6 +10341,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),
@@ -10205,7 +10425,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);
@@ -10216,7 +10446,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
- [Qemu-devel] [PULL 14/21] hw/arm: Add ASPEED AST2400 SoC model, (continued)
- [Qemu-devel] [PULL 14/21] hw/arm: Add ASPEED AST2400 SoC model, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 03/21] target-arm: Fix translation level on early translation faults, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 09/21] i.MX: Add the CLK_IPG_HIGH clock, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 07/21] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency., Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 05/21] arm: virt: Move machine class init code to the abstract machine type, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 08/21] i.MX: Remove CCM useless clock computation handling., Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 11/21] i.MX: Add missing descriptions in devices., Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 21/21] sd: Fix "info qtree" on boards with SD cards, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 17/21] bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 19/21] bcm2835_property: implement framebuffer control/configuration properties, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (banked) instructions,
Peter Maydell <=
- [Qemu-devel] [PULL 12/21] hw/timer: Add ASPEED timer device model, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 04/21] arm: virt: Add an abstract ARM virt machine type, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 06/21] i.MX: Allow GPT timer to rollover., Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 01/21] loader: Fix incorrect parameter name in load_image_mr() macro, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 20/21] bcm2835_dma: add emulation of Raspberry Pi DMA controller, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 10/21] i.MX: Add i.MX6 CCM and ANALOG device., Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 13/21] hw/intc: Add (new) ASPEED VIC device model, Peter Maydell, 2016/03/16
- [Qemu-devel] [PULL 18/21] bcm2835_fb: add framebuffer device for Raspberry Pi, Peter Maydell, 2016/03/16
- Re: [Qemu-devel] [PULL 00/21] target-arm queue, Peter Maydell, 2016/03/16
- Re: [Qemu-devel] [PULL 00/21] target-arm queue, Peter Maydell, 2016/03/16