[09/11] target/arm: Set PAN bit as required on exception entry

Message ID 20191203225333.17055-10-richard.henderson@linaro.org
State New
Headers show
Series
  • target/arm: Implement ARMv8.1-PAN + ARMv8.2-ATS1E1
Related show

Commit Message

Richard Henderson Dec. 3, 2019, 10:53 p.m.
The PAN bit is preserved, or set as per SCTLR_ELx.SPAN,
plus several other conditions listed in the ARM ARM.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 target/arm/helper.c | 42 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 39 insertions(+), 3 deletions(-)

-- 
2.17.1

Comments

Peter Maydell Dec. 9, 2019, 11:55 a.m. | #1
On Tue, 3 Dec 2019 at 22:53, Richard Henderson
<richard.henderson@linaro.org> wrote:
>

> The PAN bit is preserved, or set as per SCTLR_ELx.SPAN,

> plus several other conditions listed in the ARM ARM.

>

> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> ---

>  target/arm/helper.c | 42 +++++++++++++++++++++++++++++++++++++++---

>  1 file changed, 39 insertions(+), 3 deletions(-)

>

> diff --git a/target/arm/helper.c b/target/arm/helper.c

> index a1dbafb9b2..043e44d73d 100644

> --- a/target/arm/helper.c

> +++ b/target/arm/helper.c

> @@ -8634,8 +8634,12 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,

>                                     uint32_t mask, uint32_t offset,

>                                     uint32_t newpc)

>  {

> +    int new_el;

> +

>      /* Change the CPU state so as to actually take the exception. */

>      switch_mode(env, new_mode);

> +    new_el = arm_current_el(env);

> +

>      /*

>       * For exceptions taken to AArch32 we must clear the SS bit in both

>       * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now.

> @@ -8648,7 +8652,7 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,

>      env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;

>      /* Set new mode endianness */

>      env->uncached_cpsr &= ~CPSR_E;

> -    if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) {

> +    if (env->cp15.sctlr_el[new_el] & SCTLR_EE) {

>          env->uncached_cpsr |= CPSR_E;

>      }

>      /* J and IL must always be cleared for exception entry */

> @@ -8659,6 +8663,14 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,

>          env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0;

>          env->elr_el[2] = env->regs[15];

>      } else {

> +        /* CPSR.PAN is preserved unless target is EL1 and SCTLR.SPAN == 0. */

> +        if (cpu_isar_feature(aa64_pan, env_archcpu(env))) {

> +            env->uncached_cpsr |=

> +                (new_el == 1 &&

> +                 (env->cp15.sctlr_el[1] & SCTLR_SPAN) == 0

> +                 ? CPSR_PAN

> +                 : env->spsr & CPSR_PAN);


env->uncached_cpsr isn't wiped by this function, so the default
behaviour is "same as it was previously" without needing to fish
the bit out of env->spsr again, I think.

> +        }

>          /*

>           * this is a lie, as there was no c1_sys on V4T/V5, but who cares

>           * and we should just guard the thumb mode on V4


thanks
-- PMM

Patch

diff --git a/target/arm/helper.c b/target/arm/helper.c
index a1dbafb9b2..043e44d73d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -8634,8 +8634,12 @@  static void take_aarch32_exception(CPUARMState *env, int new_mode,
                                    uint32_t mask, uint32_t offset,
                                    uint32_t newpc)
 {
+    int new_el;
+
     /* Change the CPU state so as to actually take the exception. */
     switch_mode(env, new_mode);
+    new_el = arm_current_el(env);
+
     /*
      * For exceptions taken to AArch32 we must clear the SS bit in both
      * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now.
@@ -8648,7 +8652,7 @@  static void take_aarch32_exception(CPUARMState *env, int new_mode,
     env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
     /* Set new mode endianness */
     env->uncached_cpsr &= ~CPSR_E;
-    if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) {
+    if (env->cp15.sctlr_el[new_el] & SCTLR_EE) {
         env->uncached_cpsr |= CPSR_E;
     }
     /* J and IL must always be cleared for exception entry */
@@ -8659,6 +8663,14 @@  static void take_aarch32_exception(CPUARMState *env, int new_mode,
         env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0;
         env->elr_el[2] = env->regs[15];
     } else {
+        /* CPSR.PAN is preserved unless target is EL1 and SCTLR.SPAN == 0. */
+        if (cpu_isar_feature(aa64_pan, env_archcpu(env))) {
+            env->uncached_cpsr |=
+                (new_el == 1 &&
+                 (env->cp15.sctlr_el[1] & SCTLR_SPAN) == 0
+                 ? CPSR_PAN
+                 : env->spsr & CPSR_PAN);
+        }
         /*
          * this is a lie, as there was no c1_sys on V4T/V5, but who cares
          * and we should just guard the thumb mode on V4
@@ -8921,6 +8933,7 @@  static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
     unsigned int new_el = env->exception.target_el;
     target_ulong addr = env->cp15.vbar_el[new_el];
     unsigned int new_mode = aarch64_pstate_mode(new_el, true);
+    unsigned int old_mode;
     unsigned int cur_el = arm_current_el(env);
 
     /*
@@ -9006,20 +9019,43 @@  static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
     }
 
     if (is_a64(env)) {
-        env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env);
+        old_mode = pstate_read(env);
         aarch64_save_sp(env, arm_current_el(env));
         env->elr_el[new_el] = env->pc;
     } else {
-        env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env);
+        old_mode = cpsr_read(env);
         env->elr_el[new_el] = env->regs[15];
 
         aarch64_sync_32_to_64(env);
 
         env->condexec_bits = 0;
     }
+    env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode;
+
     qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n",
                   env->elr_el[new_el]);
 
+    if (cpu_isar_feature(aa64_pan, cpu)) {
+        /* The value of PSTATE.PAN is normally preserved, except when ... */
+        new_mode |= old_mode & PSTATE_PAN;
+        switch (new_el) {
+        case 2:
+            /* ... the target is EL2 with HCR_EL2.{E2H,TGE} == '11' ...  */
+            if ((arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE))
+                != (HCR_E2H | HCR_TGE)) {
+                break;
+            }
+            /* fall through */
+        case 1:
+            /* ... the target is EL1 ... */
+            /* ... and SCTLR_ELx.SPAN == 0, then set to 1.  */
+            if ((env->cp15.sctlr_el[new_el] & SCTLR_SPAN) == 0) {
+                new_mode |= PSTATE_PAN;
+            }
+            break;
+        }
+    }
+
     pstate_write(env, PSTATE_DAIF | new_mode);
     env->aarch64 = 1;
     aarch64_restore_sp(env, new_el);