diff mbox

tcg-arm: more instruction execution control

Message ID 1420823193-17609-1-git-send-email-drjones@redhat.com
State New
Headers show

Commit Message

Andrew Jones Jan. 9, 2015, 5:06 p.m. UTC
Cleanup XN/PXN handling in get_phys_addr_lpae, and implement all but
EL2 support of the following ARMv8 sections

  D4.5.1 Memory access control: Access permissions for instruction
         execution
  G4.7.2 Execute-never restrictions on instruction fetching

G4.7.2 matches the ARMv7 section B3.7.2 when long-descriptors are used.

Signed-off-by: Andrew Jones <drjones@redhat.com>

---
I didn't test this with secure mode (I didn't even check if that's
currently possible), but I did test all other EL1&0 XN controls with
both tcg-arm (cortex-a15) and tcg-aarch64 (cortex-a57) by hacking up
some tests with kvm-unit-tests/arm[64]. I also booted Linux (just
up to looking for a rootfs) with both.
---
 target-arm/helper.c | 99 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 79 insertions(+), 20 deletions(-)

Comments

Andrew Jones Jan. 12, 2015, 12:42 p.m. UTC | #1
On Fri, Jan 09, 2015 at 06:06:33PM +0100, Andrew Jones wrote:
> Cleanup XN/PXN handling in get_phys_addr_lpae, and implement all but
> EL2 support of the following ARMv8 sections
> 
>   D4.5.1 Memory access control: Access permissions for instruction
>          execution
>   G4.7.2 Execute-never restrictions on instruction fetching
> 
> G4.7.2 matches the ARMv7 section B3.7.2 when long-descriptors are used.
> 
> Signed-off-by: Andrew Jones <drjones@redhat.com>
> 
> ---
> I didn't test this with secure mode (I didn't even check if that's
> currently possible), but I did test all other EL1&0 XN controls with
> both tcg-arm (cortex-a15) and tcg-aarch64 (cortex-a57) by hacking up
> some tests with kvm-unit-tests/arm[64]. I also booted Linux (just
> up to looking for a rootfs) with both.
> ---
>  target-arm/helper.c | 99 ++++++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 79 insertions(+), 20 deletions(-)
> 
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 3ef0f1f38eda5..48a83cff957fb 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -4516,6 +4516,69 @@ static inline int check_ap(CPUARMState *env, int ap, int domain_prot,
>    }
>  }
>  
> +/* Check section/page execution permission.
> + *
> + * Returns PAGE_EXEC if execution is permitted, otherwise zero.
> + *
> + * Supports AArch64, AArch32, and v7-LPAE. Doesn't yet support
> + * EL2. Actually, for v7-LPAE, we'd also need to emulate Virt
> + * Extensions to truly have WXN/UWXN. We don't support checking
> + * for that yet, so we just assume we have them.
> + *
> + * @env         : CPUARMState
> + * @is_user     : 0 for privileged access, 1 for user
> + * @ap          : Access permissions (AP[2:1]) from descriptor
> + * @ns          : (NSTable || NS) from descriptors
> + * @xn          : ([U]XNTable || [U]XN) descriptors
> + * @pxn         : (PXNTable || PXN) descriptors

Just realized I'm missing 'from' in the last two lines above.

> + */
> +static int check_xn_lpae(CPUARMState *env, int is_user, int ap,
> +                         int ns, int xn, int pxn)
> +{
> +    int wxn;
> +
> +    switch (arm_current_el(env)) {
> +    case 0:
> +    case 1:
> +        if (is_user && !(ap & 1)) {
> +            return 0;
> +        }
> +        wxn = env->cp15.sctlr_el[1] & SCTLR_WXN;
> +        if (arm_el_is_aa64(env, 1)) {
> +            pxn = pxn || ((ap & 1) && !(ap & 2));
> +            xn = is_user ? xn : pxn;
> +        } else {
> +            int uwxn = env->cp15.sctlr_el[1] & SCTLR_UWXN;
> +            pxn = pxn || (uwxn && (ap & 1) && !(ap & 2));
> +            xn = xn || (!is_user && pxn);
> +        }
> +        if (arm_is_secure(env)) {
> +            xn = xn || (ns && (env->cp15.scr_el3 & SCR_SIF));
> +        }
> +        break;
> +    case 2:
> +        /* TODO actually support EL2 */
> +        assert(true);

And, argh... brain wires got crossed here. Of course this should
be 'assert(false)'. I'll send a v2.

> +
> +        if (arm_el_is_aa64(env, 2)) {
> +            wxn = env->cp15.sctlr_el[2] & SCTLR_WXN;
> +        } else {
> +            /* wxn = HSCTLR.WXN */
> +        }
> +        break;
> +    case 3:
> +        if (arm_el_is_aa64(env, 3)) {
> +            wxn = env->cp15.sctlr_el[3] & SCTLR_WXN;
> +        } else {
> +            wxn = 0;
> +        }
> +        xn = xn || (ns && (env->cp15.scr_el3 & SCR_SIF));
> +        break;
> +    }
> +
> +    return xn || (!(ap & 2) && wxn) ? 0 : PAGE_EXEC;
> +}
> +
>  static bool get_level1_table_address(CPUARMState *env, uint32_t *table,
>                                           uint32_t address)
>  {
> @@ -4787,7 +4850,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
>      hwaddr descaddr, descmask;
>      uint32_t tableattrs;
>      target_ulong page_size;
> -    uint32_t attrs;
> +    uint32_t attrs, ap;
>      int32_t granule_sz = 9;
>      int32_t va_size = 32;
>      int32_t tbi = 0;
> @@ -4941,7 +5004,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
>          if (extract32(tableattrs, 2, 1)) {
>              attrs &= ~(1 << 4);
>          }
> -        /* Since we're always in the Non-secure state, NSTable is ignored. */
> +        attrs |= extract32(tableattrs, 4, 1) << 3; /* NS */
>          break;
>      }
>      /* Here descaddr is the final physical address, and attributes
> @@ -4952,29 +5015,25 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
>          /* Access flag */
>          goto do_fault;
>      }
> +
>      fault_type = permission_fault;
> -    if (is_user && !(attrs & (1 << 4))) {
> +    ap = extract32(attrs, 4, 2); /* AP[2:1] */
> +
> +    if (is_user && !(ap & 1)) {
>          /* Unprivileged access not enabled */
>          goto do_fault;
>      }
> -    *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
> -    if ((arm_feature(env, ARM_FEATURE_V8) && is_user && (attrs & (1 << 12))) ||
> -        (!arm_feature(env, ARM_FEATURE_V8) && (attrs & (1 << 12))) ||
> -        (!is_user && (attrs & (1 << 11)))) {
> -        /* XN/UXN or PXN. Since we only implement EL0/EL1 we unconditionally
> -         * treat XN/UXN as UXN for v8.
> -         */
> -        if (access_type == 2) {
> -            goto do_fault;
> -        }
> -        *prot &= ~PAGE_EXEC;
> +
> +    *prot = PAGE_READ;
> +    *prot |= check_xn_lpae(env, is_user, ap, extract32(attrs, 3, 1),
> +                           extract32(attrs, 12, 1), extract32(attrs, 11, 1));
> +    if (!(*prot & PAGE_EXEC) && access_type == 2) {
> +        goto do_fault;
>      }
> -    if (attrs & (1 << 5)) {
> -        /* Write access forbidden */
> -        if (access_type == 1) {
> -            goto do_fault;
> -        }
> -        *prot &= ~PAGE_WRITE;
> +
> +    *prot |= !(ap & 2) ? PAGE_WRITE : 0;
> +    if (!(*prot & PAGE_WRITE) && access_type == 1) {
> +        goto do_fault;
>      }
>  
>      *phys_ptr = descaddr;
> -- 
> 1.9.3
>
diff mbox

Patch

diff --git a/target-arm/helper.c b/target-arm/helper.c
index 3ef0f1f38eda5..48a83cff957fb 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4516,6 +4516,69 @@  static inline int check_ap(CPUARMState *env, int ap, int domain_prot,
   }
 }
 
+/* Check section/page execution permission.
+ *
+ * Returns PAGE_EXEC if execution is permitted, otherwise zero.
+ *
+ * Supports AArch64, AArch32, and v7-LPAE. Doesn't yet support
+ * EL2. Actually, for v7-LPAE, we'd also need to emulate Virt
+ * Extensions to truly have WXN/UWXN. We don't support checking
+ * for that yet, so we just assume we have them.
+ *
+ * @env         : CPUARMState
+ * @is_user     : 0 for privileged access, 1 for user
+ * @ap          : Access permissions (AP[2:1]) from descriptor
+ * @ns          : (NSTable || NS) from descriptors
+ * @xn          : ([U]XNTable || [U]XN) descriptors
+ * @pxn         : (PXNTable || PXN) descriptors
+ */
+static int check_xn_lpae(CPUARMState *env, int is_user, int ap,
+                         int ns, int xn, int pxn)
+{
+    int wxn;
+
+    switch (arm_current_el(env)) {
+    case 0:
+    case 1:
+        if (is_user && !(ap & 1)) {
+            return 0;
+        }
+        wxn = env->cp15.sctlr_el[1] & SCTLR_WXN;
+        if (arm_el_is_aa64(env, 1)) {
+            pxn = pxn || ((ap & 1) && !(ap & 2));
+            xn = is_user ? xn : pxn;
+        } else {
+            int uwxn = env->cp15.sctlr_el[1] & SCTLR_UWXN;
+            pxn = pxn || (uwxn && (ap & 1) && !(ap & 2));
+            xn = xn || (!is_user && pxn);
+        }
+        if (arm_is_secure(env)) {
+            xn = xn || (ns && (env->cp15.scr_el3 & SCR_SIF));
+        }
+        break;
+    case 2:
+        /* TODO actually support EL2 */
+        assert(true);
+
+        if (arm_el_is_aa64(env, 2)) {
+            wxn = env->cp15.sctlr_el[2] & SCTLR_WXN;
+        } else {
+            /* wxn = HSCTLR.WXN */
+        }
+        break;
+    case 3:
+        if (arm_el_is_aa64(env, 3)) {
+            wxn = env->cp15.sctlr_el[3] & SCTLR_WXN;
+        } else {
+            wxn = 0;
+        }
+        xn = xn || (ns && (env->cp15.scr_el3 & SCR_SIF));
+        break;
+    }
+
+    return xn || (!(ap & 2) && wxn) ? 0 : PAGE_EXEC;
+}
+
 static bool get_level1_table_address(CPUARMState *env, uint32_t *table,
                                          uint32_t address)
 {
@@ -4787,7 +4850,7 @@  static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     hwaddr descaddr, descmask;
     uint32_t tableattrs;
     target_ulong page_size;
-    uint32_t attrs;
+    uint32_t attrs, ap;
     int32_t granule_sz = 9;
     int32_t va_size = 32;
     int32_t tbi = 0;
@@ -4941,7 +5004,7 @@  static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         if (extract32(tableattrs, 2, 1)) {
             attrs &= ~(1 << 4);
         }
-        /* Since we're always in the Non-secure state, NSTable is ignored. */
+        attrs |= extract32(tableattrs, 4, 1) << 3; /* NS */
         break;
     }
     /* Here descaddr is the final physical address, and attributes
@@ -4952,29 +5015,25 @@  static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
         /* Access flag */
         goto do_fault;
     }
+
     fault_type = permission_fault;
-    if (is_user && !(attrs & (1 << 4))) {
+    ap = extract32(attrs, 4, 2); /* AP[2:1] */
+
+    if (is_user && !(ap & 1)) {
         /* Unprivileged access not enabled */
         goto do_fault;
     }
-    *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
-    if ((arm_feature(env, ARM_FEATURE_V8) && is_user && (attrs & (1 << 12))) ||
-        (!arm_feature(env, ARM_FEATURE_V8) && (attrs & (1 << 12))) ||
-        (!is_user && (attrs & (1 << 11)))) {
-        /* XN/UXN or PXN. Since we only implement EL0/EL1 we unconditionally
-         * treat XN/UXN as UXN for v8.
-         */
-        if (access_type == 2) {
-            goto do_fault;
-        }
-        *prot &= ~PAGE_EXEC;
+
+    *prot = PAGE_READ;
+    *prot |= check_xn_lpae(env, is_user, ap, extract32(attrs, 3, 1),
+                           extract32(attrs, 12, 1), extract32(attrs, 11, 1));
+    if (!(*prot & PAGE_EXEC) && access_type == 2) {
+        goto do_fault;
     }
-    if (attrs & (1 << 5)) {
-        /* Write access forbidden */
-        if (access_type == 1) {
-            goto do_fault;
-        }
-        *prot &= ~PAGE_WRITE;
+
+    *prot |= !(ap & 2) ? PAGE_WRITE : 0;
+    if (!(*prot & PAGE_WRITE) && access_type == 1) {
+        goto do_fault;
     }
 
     *phys_ptr = descaddr;