diff mbox

[v2,09/35] target-arm: A64: Implement MSR (immediate) instructions

Message ID 1391183143-30724-10-git-send-email-peter.maydell@linaro.org
State Superseded
Headers show

Commit Message

Peter Maydell Jan. 31, 2014, 3:45 p.m. UTC
Implement the MSR (immediate) instructions, which can update the
PSTATE SP and DAIF fields.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/cpu.h           |  1 +
 target-arm/helper.h        |  2 ++
 target-arm/op_helper.c     | 25 +++++++++++++++++++++++++
 target-arm/translate-a64.c | 24 +++++++++++++++++++++++-
 4 files changed, 51 insertions(+), 1 deletion(-)

Comments

Peter Maydell Feb. 5, 2014, 10:55 a.m. UTC | #1
On 5 February 2014 06:23, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> Implement the MSR (immediate) instructions, which can update the
>> PSTATE SP and DAIF fields.
>>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> ---
>>  target-arm/cpu.h           |  1 +
>>  target-arm/helper.h        |  2 ++
>>  target-arm/op_helper.c     | 25 +++++++++++++++++++++++++
>>  target-arm/translate-a64.c | 24 +++++++++++++++++++++++-
>>  4 files changed, 51 insertions(+), 1 deletion(-)
>>
>> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
>> index 385cfcd..e66d464 100644
>> --- a/target-arm/cpu.h
>> +++ b/target-arm/cpu.h
>> @@ -426,6 +426,7 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
>>  #define PSTATE_Z (1U << 30)
>>  #define PSTATE_N (1U << 31)
>>  #define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
>> +#define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F)
>>  #define CACHED_PSTATE_BITS (PSTATE_NZCV)
>>  /* Mode values for AArch64 */
>>  #define PSTATE_MODE_EL3h 13
>> diff --git a/target-arm/helper.h b/target-arm/helper.h
>> index 71b8411..93a27ce 100644
>> --- a/target-arm/helper.h
>> +++ b/target-arm/helper.h
>> @@ -62,6 +62,8 @@ DEF_HELPER_2(get_cp_reg, i32, env, ptr)
>>  DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
>>  DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
>>
>> +DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
>> +
>>  DEF_HELPER_2(get_r13_banked, i32, env, i32)
>>  DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
>>
>> diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
>> index a918e5b..c812a9f 100644
>> --- a/target-arm/op_helper.c
>> +++ b/target-arm/op_helper.c
>> @@ -313,6 +313,31 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
>>      return value;
>>  }
>>
>> +void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
>> +{
>> +    /* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set.
>> +     * Note that SPSel is never OK from EL0; we rely on handle_msr_i()
>> +     * to catch that case at translate time.
>> +     */
>> +    if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) {
>> +        raise_exception(env, EXCP_UDEF);
>> +    }
>> +
>> +    switch (op) {
>> +    case 0x05: /* SPSel */
>> +        env->pstate = deposit32(env->pstate, 0, 1, imm);
>
> "0","1" hardcoded constants are a bit unfriendly. I guess the current
> macro set doesnt define _SHIFT and _WIDTH definitions, should they be
> added?
>
> FWIW, I have this macro in my tree which makes short work of defining
> mask, shift and width constants as a one liner:
>
> /* Define SHIFT, LENGTH and MASK constants for a field within a register */
>
> #define FIELD(reg, field, length, shift) \
> enum { reg ## _ ## field ## _SHIFT = (shift)}; \
> enum { reg ## _ ## field ## _LENGTH = (length)}; \
> enum { reg ## _ ## field ## _MASK = (((1ULL << (length)) - 1) \
>                                           << (shift)) };
>
> Usage would be something like FIELD(PSTATE, SPSEL, 1, 0)

Hmm. I guess we could use some more consistent structure in
defining bit macros. (reg, field, start, len) would be a better
argument order though, to match the extract and deposit fns.

>> +        break;
>> +    case 0x1e: /* DAIFSet */
>> +        env->pstate |= (imm << 6) & PSTATE_DAIF;
>> +        break;
>> +    case 0x1f: /* DAIFClear */
>> +        env->pstate &= ~((imm << 6) & PSTATE_DAIF);
>
> I wonder whether deposit should be extended with and/or (with
> existing) versions to allow for consistency in places like this. In
> SPSel we get the nice deposit based implementation but with the logic
> function change here were are stuck with open codedness. Set and
> clearing and fields should be common enough tree wide to warrant it
> perhaps.

I dunno. Deposit is a function mostly because "clear old
field to zeroes then insert new value" is complicated enough
that it's easy to miscode it. Plain force-set and force-clear
I think is simple enough (and not all that common) that I'm
happy to opencode it.

One point I need to think about a little more with the DAIF
bits is whether we should be keeping them in one place for
AArch32 and AArch64 -- at the moment an AArch32 core's
IF bits are in cpsr. Having them in one location would make
the cpu-exec.c code a bit simpler.

thanks
-- PMM
Peter Maydell Feb. 14, 2014, 4:41 p.m. UTC | #2
On 5 February 2014 06:23, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> +    switch (op) {
>> +    case 0x05: /* SPSel */
>> +        env->pstate = deposit32(env->pstate, 0, 1, imm);
>
> "0","1" hardcoded constants are a bit unfriendly. I guess the current
> macro set doesnt define _SHIFT and _WIDTH definitions, should they be
> added?
>
> FWIW, I have this macro in my tree which makes short work of defining
> mask, shift and width constants as a one liner:
>
> /* Define SHIFT, LENGTH and MASK constants for a field within a register */
>
> #define FIELD(reg, field, length, shift) \
> enum { reg ## _ ## field ## _SHIFT = (shift)}; \
> enum { reg ## _ ## field ## _LENGTH = (length)}; \
> enum { reg ## _ ## field ## _MASK = (((1ULL << (length)) - 1) \
>                                           << (shift)) };
>
> Usage would be something like FIELD(PSTATE, SPSEL, 1, 0)

So, I kind of like this, but I'm a bit reluctant to tie up
this patchset in "add a new generic facility to bitops.h",
and current handling for pstate/cpsr has a lot of hardcoded
constants for bit positions. Is it OK if we do this as a
separate cleanup, or do you feel strongly we should bite
the bullet and do it as part of this series?

thanks
-- PMM
diff mbox

Patch

diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 385cfcd..e66d464 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -426,6 +426,7 @@  int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
 #define PSTATE_Z (1U << 30)
 #define PSTATE_N (1U << 31)
 #define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
+#define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F)
 #define CACHED_PSTATE_BITS (PSTATE_NZCV)
 /* Mode values for AArch64 */
 #define PSTATE_MODE_EL3h 13
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 71b8411..93a27ce 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -62,6 +62,8 @@  DEF_HELPER_2(get_cp_reg, i32, env, ptr)
 DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
 DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
 
+DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
+
 DEF_HELPER_2(get_r13_banked, i32, env, i32)
 DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
 
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index a918e5b..c812a9f 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -313,6 +313,31 @@  uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
     return value;
 }
 
+void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
+{
+    /* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set.
+     * Note that SPSel is never OK from EL0; we rely on handle_msr_i()
+     * to catch that case at translate time.
+     */
+    if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) {
+        raise_exception(env, EXCP_UDEF);
+    }
+
+    switch (op) {
+    case 0x05: /* SPSel */
+        env->pstate = deposit32(env->pstate, 0, 1, imm);
+        break;
+    case 0x1e: /* DAIFSet */
+        env->pstate |= (imm << 6) & PSTATE_DAIF;
+        break;
+    case 0x1f: /* DAIFClear */
+        env->pstate &= ~((imm << 6) & PSTATE_DAIF);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 /* ??? Flag setting arithmetic is awkward because we need to do comparisons.
    The only way to do that in TCG is a conditional branch, which clobbers
    all our temporaries.  For now implement these as helper functions.  */
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index d938e5e..a942609 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1106,7 +1106,29 @@  static void handle_sync(DisasContext *s, uint32_t insn,
 static void handle_msr_i(DisasContext *s, uint32_t insn,
                          unsigned int op1, unsigned int op2, unsigned int crm)
 {
-    unsupported_encoding(s, insn);
+    int op = op1 << 3 | op2;
+    switch (op) {
+    case 0x05: /* SPSel */
+        if (s->current_pl == 0) {
+            unallocated_encoding(s);
+            return;
+        }
+        /* fall through */
+    case 0x1e: /* DAIFSet */
+    case 0x1f: /* DAIFClear */
+    {
+        TCGv_i32 tcg_imm = tcg_const_i32(crm);
+        TCGv_i32 tcg_op = tcg_const_i32(op);
+        gen_helper_msr_i_pstate(cpu_env, tcg_op, tcg_imm);
+        tcg_temp_free_i32(tcg_imm);
+        tcg_temp_free_i32(tcg_op);
+        s->is_jmp = DISAS_UPDATE;
+        break;
+    }
+    default:
+        unallocated_encoding(s);
+        return;
+    }
 }
 
 static void gen_get_nzcv(TCGv_i64 tcg_rt)