]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Handle stage-1 permission overlays
authorMarc Zyngier <maz@kernel.org>
Wed, 23 Oct 2024 14:53:44 +0000 (15:53 +0100)
committerOliver Upton <oliver.upton@linux.dev>
Thu, 31 Oct 2024 02:44:23 +0000 (02:44 +0000)
We now have the intrastructure in place to emulate S1POE:

- direct permissions are always overlay-capable
- indirect permissions are overlay-capable if the permissions are
  in the 0b0xxx range
- the overlays are strictly substractive

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

index 2ab2c3578c3a034fc189ae785fa24ad5ef53eab0..d300cd1a0d8a728f21755fc5ab9cb0d7997af0de 100644 (file)
@@ -40,9 +40,11 @@ struct s1_walk_result {
                        u8      APTable;
                        bool    UXNTable;
                        bool    PXNTable;
+                       bool    uov;
                        bool    ur;
                        bool    uw;
                        bool    ux;
+                       bool    pov;
                        bool    pr;
                        bool    pw;
                        bool    px;
@@ -881,6 +883,9 @@ static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu,
                /* XN maps to UXN */
                wr->px = !(wr->desc & PTE_UXN);
        }
+
+       wr->pov = wi->poe;
+       wr->uov = wi->e0poe;
 }
 
 static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
@@ -1016,6 +1021,9 @@ static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
        else
                set_unpriv_perms(wr, false, false, false);
 
+       wr->pov = wi->poe && !(pp & BIT(3));
+       wr->uov = wi->e0poe && !(up & BIT(3));
+
        /* R_VFPJF */
        if (wr->px && wr->uw) {
                set_priv_perms(wr, false, false, false);
@@ -1023,6 +1031,48 @@ static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
        }
 }
 
+static void compute_s1_overlay_permissions(struct kvm_vcpu *vcpu,
+                                          struct s1_walk_info *wi,
+                                          struct s1_walk_result *wr)
+{
+       u8 idx, pov_perms, uov_perms;
+
+       idx = FIELD_GET(PTE_PO_IDX_MASK, wr->desc);
+
+       switch (wi->regime) {
+       case TR_EL10:
+               pov_perms = perm_idx(vcpu, POR_EL1, idx);
+               uov_perms = perm_idx(vcpu, POR_EL0, idx);
+               break;
+       case TR_EL20:
+               pov_perms = perm_idx(vcpu, POR_EL2, idx);
+               uov_perms = perm_idx(vcpu, POR_EL0, idx);
+               break;
+       case TR_EL2:
+               pov_perms = perm_idx(vcpu, POR_EL2, idx);
+               uov_perms = 0;
+               break;
+       }
+
+       if (pov_perms & ~POE_RXW)
+               pov_perms = POE_NONE;
+
+       if (wi->poe && wr->pov) {
+               wr->pr &= pov_perms & POE_R;
+               wr->px &= pov_perms & POE_X;
+               wr->pw &= pov_perms & POE_W;
+       }
+
+       if (uov_perms & ~POE_RXW)
+               uov_perms = POE_NONE;
+
+       if (wi->e0poe && wr->uov) {
+               wr->ur &= uov_perms & POE_R;
+               wr->ux &= uov_perms & POE_X;
+               wr->uw &= uov_perms & POE_W;
+       }
+}
+
 static void compute_s1_permissions(struct kvm_vcpu *vcpu,
                                   struct s1_walk_info *wi,
                                   struct s1_walk_result *wr)
@@ -1037,6 +1087,9 @@ static void compute_s1_permissions(struct kvm_vcpu *vcpu,
        if (!wi->hpd)
                compute_s1_hierarchical_permissions(vcpu, wi, wr);
 
+       if (wi->poe || wi->e0poe)
+               compute_s1_overlay_permissions(vcpu, wi, wr);
+
        pan = wi->pan && (wr->ur || wr->uw ||
                          (pan3_enabled(vcpu, wi->regime) && wr->ux));
        wr->pw &= !pan;