]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Handle WXN attribute
authorMarc Zyngier <maz@kernel.org>
Wed, 23 Oct 2024 14:53:45 +0000 (15:53 +0100)
committerOliver Upton <oliver.upton@linux.dev>
Thu, 31 Oct 2024 02:44:23 +0000 (02:44 +0000)
Until now, we didn't really care about WXN as it didn't have an
effect on the R/W permissions (only the execution could be droppped),
and therefore not of interest for AT.

However, with S1POE, WXN can revoke the Write permission if an
overlay is active and that execution is allowed. This *is* relevant
to AT.

Add full handling of WXN so that we correctly handle this case.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20241023145345.1613824-38-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/at.c

index d300cd1a0d8a728f21755fc5ab9cb0d7997af0de..8c5d7990e5b31207e9ded0da9319591d3c69e47f 100644 (file)
@@ -40,10 +40,12 @@ struct s1_walk_result {
                        u8      APTable;
                        bool    UXNTable;
                        bool    PXNTable;
+                       bool    uwxn;
                        bool    uov;
                        bool    ur;
                        bool    uw;
                        bool    ux;
+                       bool    pwxn;
                        bool    pov;
                        bool    pr;
                        bool    pw;
@@ -847,6 +849,8 @@ static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu,
                                          struct s1_walk_info *wi,
                                          struct s1_walk_result *wr)
 {
+       bool wxn;
+
        /* Non-hierarchical part of AArch64.S1DirectBasePermissions() */
        if (wi->regime != TR_EL2) {
                switch (FIELD_GET(PTE_USER | PTE_RDONLY, wr->desc)) {
@@ -884,6 +888,17 @@ static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu,
                wr->px = !(wr->desc & PTE_UXN);
        }
 
+       switch (wi->regime) {
+       case TR_EL2:
+       case TR_EL20:
+               wxn = (vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_WXN);
+               break;
+       case TR_EL10:
+               wxn = (__vcpu_sys_reg(vcpu, SCTLR_EL1) & SCTLR_ELx_WXN);
+               break;
+       }
+
+       wr->pwxn = wr->uwxn = wxn;
        wr->pov = wi->poe;
        wr->uov = wi->e0poe;
 }
@@ -935,6 +950,16 @@ static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
                (wr)->ux = (x);         \
        } while (0)
 
+#define set_priv_wxn(wr, v)            \
+       do {                            \
+               (wr)->pwxn = (v);       \
+       } while (0)
+
+#define set_unpriv_wxn(wr, v)          \
+       do {                            \
+               (wr)->uwxn = (v);       \
+       } while (0)
+
 /* Similar to AArch64.S1IndirectBasePermissions(), without GCS  */
 #define set_perms(w, wr, ip)                                           \
        do {                                                            \
@@ -989,6 +1014,10 @@ static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
                        set_ ## w ## _perms((wr), false, false, false); \
                        break;                                          \
                }                                                       \
+                                                                       \
+               /* R_HJYGR */                                           \
+               set_ ## w ## _wxn((wr), ((ip) == 0b0110));              \
+                                                                       \
        } while (0)
 
 static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
@@ -1090,6 +1119,22 @@ static void compute_s1_permissions(struct kvm_vcpu *vcpu,
        if (wi->poe || wi->e0poe)
                compute_s1_overlay_permissions(vcpu, wi, wr);
 
+       /* R_QXXPC */
+       if (wr->pwxn) {
+               if (!wr->pov && wr->pw)
+                       wr->px = false;
+               if (wr->pov && wr->px)
+                       wr->pw = false;
+       }
+
+       /* R_NPBXC */
+       if (wr->uwxn) {
+               if (!wr->uov && wr->uw)
+                       wr->ux = false;
+               if (wr->uov && wr->ux)
+                       wr->uw = false;
+       }
+
        pan = wi->pan && (wr->ur || wr->uw ||
                          (pan3_enabled(vcpu, wi->regime) && wr->ux));
        wr->pw &= !pan;