[PATCH 14/22] target/arm: Handle no-execute for Realm and Root regimes

Richard Henderson posted 22 patches 2 years, 5 months ago
There is a newer version of this series
[PATCH 14/22] target/arm: Handle no-execute for Realm and Root regimes
Posted by Richard Henderson 2 years, 5 months ago
While Root and Realm may read and write data from other spaces,
neither may execute from other pa spaces.

This happens for Stage1 EL3, EL2, EL2&0, but stage2 EL1&0.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/arm/ptw.c | 66 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 58 insertions(+), 8 deletions(-)

diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 849f5e89ca..6b6f8195eb 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -909,7 +909,7 @@ do_fault:
  * @xn:      XN (execute-never) bits
  * @s1_is_el0: true if this is S2 of an S1+2 walk for EL0
  */
-static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
+static int get_S2prot_noexecute(int s2ap)
 {
     int prot = 0;
 
@@ -919,6 +919,12 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
     if (s2ap & 2) {
         prot |= PAGE_WRITE;
     }
+    return prot;
+}
+
+static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
+{
+    int prot = get_S2prot_noexecute(s2ap);
 
     if (cpu_isar_feature(any_tts2uxn, env_archcpu(env))) {
         switch (xn) {
@@ -956,12 +962,14 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
  * @mmu_idx: MMU index indicating required translation regime
  * @is_aa64: TRUE if AArch64
  * @ap:      The 2-bit simple AP (AP[2:1])
- * @ns:      NS (non-secure) bit
  * @xn:      XN (execute-never) bit
  * @pxn:     PXN (privileged execute-never) bit
+ * @in_pa:   The original input pa space
+ * @out_pa:  The output pa space, modified by NSTable, NS, and NSE
  */
 static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
-                      int ap, int ns, int xn, int pxn)
+                      int ap, int xn, int pxn,
+                      ARMSecuritySpace in_pa, ARMSecuritySpace out_pa)
 {
     bool is_user = regime_is_user(env, mmu_idx);
     int prot_rw, user_rw;
@@ -982,8 +990,39 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
         }
     }
 
-    if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) {
-        return prot_rw;
+    if (in_pa != out_pa) {
+        switch (in_pa) {
+        case ARMSS_Root:
+            /*
+             * R_ZWRVD: permission fault for insn fetched from non-Root,
+             * I_WWBFB: SIF has no effect in EL3.
+             */
+            return prot_rw;
+        case ARMSS_Realm:
+            /*
+             * R_PKTDS: permission fault for insn fetched from non-Realm,
+             * for Realm EL2 or EL2&0.  The corresponding fault for EL1&0
+             * happens during any stage2 translation.
+             */
+            switch (mmu_idx) {
+            case ARMMMUIdx_E2:
+            case ARMMMUIdx_E20_0:
+            case ARMMMUIdx_E20_2:
+            case ARMMMUIdx_E20_2_PAN:
+                return prot_rw;
+            default:
+                break;
+            }
+            break;
+        case ARMSS_Secure:
+            if (env->cp15.scr_el3 & SCR_SIF) {
+                return prot_rw;
+            }
+            break;
+        default:
+            /* Input NonSecure must have output NonSecure. */
+            g_assert_not_reached();
+        }
     }
 
     /* TODO have_wxn should be replaced with
@@ -1556,12 +1595,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
         /*
          * R_GYNXY: For stage2 in Realm security state, bit 55 is NS.
          * The bit remains ignored for other security states.
+         * R_YMCSL: Executing an insn fetched from non-Realm causes
+         * a stage2 permission fault.
          */
         if (out_space == ARMSS_Realm && extract64(attrs, 55, 1)) {
             out_space = ARMSS_NonSecure;
+            result->f.prot = get_S2prot_noexecute(ap);
+        } else {
+            xn = extract64(attrs, 53, 2);
+            result->f.prot = get_S2prot(env, ap, xn, s1_is_el0);
         }
-        xn = extract64(attrs, 53, 2);
-        result->f.prot = get_S2prot(env, ap, xn, s1_is_el0);
     } else {
         int ns = extract32(attrs, 5, 1);
         switch (out_space) {
@@ -1613,7 +1656,14 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
         }
         xn = extract64(attrs, 54, 1);
         pxn = extract64(attrs, 53, 1);
-        result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
+
+        /*
+         * Note that we modified ptw->in_space earlier for NSTable,
+         * and result->f.attrs was initialized by get_phys_addr, so
+         * that retains a copy of the original security space.
+         */
+        result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, xn, pxn,
+                                    result->f.attrs.space, out_space);
     }
 
     if (!(result->f.prot & (1 << access_type))) {
-- 
2.34.1
Re: [PATCH 14/22] target/arm: Handle no-execute for Realm and Root regimes
Posted by Peter Maydell 2 years, 4 months ago
On Tue, 24 Jan 2023 at 00:02, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> While Root and Realm may read and write data from other spaces,
> neither may execute from other pa spaces.
>
> This happens for Stage1 EL3, EL2, EL2&0, but stage2 EL1&0.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  target/arm/ptw.c | 66 ++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 58 insertions(+), 8 deletions(-)
>
> diff --git a/target/arm/ptw.c b/target/arm/ptw.c
> index 849f5e89ca..6b6f8195eb 100644
> --- a/target/arm/ptw.c
> +++ b/target/arm/ptw.c
> @@ -909,7 +909,7 @@ do_fault:
>   * @xn:      XN (execute-never) bits
>   * @s1_is_el0: true if this is S2 of an S1+2 walk for EL0
>   */
> -static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
> +static int get_S2prot_noexecute(int s2ap)
>  {
>      int prot = 0;
>
> @@ -919,6 +919,12 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
>      if (s2ap & 2) {
>          prot |= PAGE_WRITE;
>      }
> +    return prot;
> +}
> +
> +static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
> +{
> +    int prot = get_S2prot_noexecute(s2ap);
>
>      if (cpu_isar_feature(any_tts2uxn, env_archcpu(env))) {
>          switch (xn) {
> @@ -956,12 +962,14 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0)
>   * @mmu_idx: MMU index indicating required translation regime
>   * @is_aa64: TRUE if AArch64
>   * @ap:      The 2-bit simple AP (AP[2:1])
> - * @ns:      NS (non-secure) bit
>   * @xn:      XN (execute-never) bit
>   * @pxn:     PXN (privileged execute-never) bit
> + * @in_pa:   The original input pa space
> + * @out_pa:  The output pa space, modified by NSTable, NS, and NSE
>   */
>  static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
> -                      int ap, int ns, int xn, int pxn)
> +                      int ap, int xn, int pxn,
> +                      ARMSecuritySpace in_pa, ARMSecuritySpace out_pa)
>  {
>      bool is_user = regime_is_user(env, mmu_idx);
>      int prot_rw, user_rw;
> @@ -982,8 +990,39 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64,
>          }
>      }
>
> -    if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) {
> -        return prot_rw;

Ah, this looks like it fixes the bug introduced in patch 12;
I guess some of this needs rearranging between patches.

> +    if (in_pa != out_pa) {
> +        switch (in_pa) {
> +        case ARMSS_Root:
> +            /*
> +             * R_ZWRVD: permission fault for insn fetched from non-Root,
> +             * I_WWBFB: SIF has no effect in EL3.
> +             */
> +            return prot_rw;
> +        case ARMSS_Realm:
> +            /*
> +             * R_PKTDS: permission fault for insn fetched from non-Realm,
> +             * for Realm EL2 or EL2&0.  The corresponding fault for EL1&0
> +             * happens during any stage2 translation.
> +             */
> +            switch (mmu_idx) {
> +            case ARMMMUIdx_E2:
> +            case ARMMMUIdx_E20_0:
> +            case ARMMMUIdx_E20_2:
> +            case ARMMMUIdx_E20_2_PAN:
> +                return prot_rw;
> +            default:
> +                break;
> +            }
> +            break;
> +        case ARMSS_Secure:
> +            if (env->cp15.scr_el3 & SCR_SIF) {
> +                return prot_rw;
> +            }
> +            break;
> +        default:
> +            /* Input NonSecure must have output NonSecure. */
> +            g_assert_not_reached();
> +        }
>      }
>
>      /* TODO have_wxn should be replaced with
> @@ -1556,12 +1595,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
>          /*
>           * R_GYNXY: For stage2 in Realm security state, bit 55 is NS.
>           * The bit remains ignored for other security states.
> +         * R_YMCSL: Executing an insn fetched from non-Realm causes
> +         * a stage2 permission fault.
>           */
>          if (out_space == ARMSS_Realm && extract64(attrs, 55, 1)) {
>              out_space = ARMSS_NonSecure;
> +            result->f.prot = get_S2prot_noexecute(ap);
> +        } else {
> +            xn = extract64(attrs, 53, 2);
> +            result->f.prot = get_S2prot(env, ap, xn, s1_is_el0);
>          }
> -        xn = extract64(attrs, 53, 2);
> -        result->f.prot = get_S2prot(env, ap, xn, s1_is_el0);
>      } else {
>          int ns = extract32(attrs, 5, 1);
>          switch (out_space) {
> @@ -1613,7 +1656,14 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
>          }
>          xn = extract64(attrs, 54, 1);
>          pxn = extract64(attrs, 53, 1);
> -        result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
> +
> +        /*
> +         * Note that we modified ptw->in_space earlier for NSTable,
> +         * and result->f.attrs was initialized by get_phys_addr, so
> +         * that retains a copy of the original security space.
> +         */
> +        result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, xn, pxn,
> +                                    result->f.attrs.space, out_space);
>      }
>
>      if (!(result->f.prot & (1 << access_type))) {
> --
> 2.34.1

thanks
-- PMM