KVM: arm64: nv: Extract translation helper from the AT code
authorMarc Zyngier <maz@kernel.org>
Wed, 14 May 2025 10:34:46 +0000 (11:34 +0100)
committerMarc Zyngier <maz@kernel.org>
Mon, 19 May 2025 06:59:46 +0000 (07:59 +0100)
The address translation infrastructure is currently pretty tied to
the AT emulation.

However, we also need to features that require the use of VAs, such
as VNCR_EL2 (and maybe one of these days SPE), meaning that we need
a slightly more generic infrastructure.

Start this by introducing a new helper (__kvm_translate_va()) that
performs a S1 walk for a given translation regime, EL and PAN
settings.

Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20250514103501.2225951-4-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_nested.h
arch/arm64/kvm/at.c

index 692f403..c8a779b 100644 (file)
@@ -245,4 +245,58 @@ static inline unsigned int ps_to_output_size(unsigned int ps)
        }
 }
 
+enum trans_regime {
+       TR_EL10,
+       TR_EL20,
+       TR_EL2,
+};
+
+struct s1_walk_info {
+       u64                     baddr;
+       enum trans_regime       regime;
+       unsigned int            max_oa_bits;
+       unsigned int            pgshift;
+       unsigned int            txsz;
+       int                     sl;
+       bool                    as_el0;
+       bool                    hpd;
+       bool                    e0poe;
+       bool                    poe;
+       bool                    pan;
+       bool                    be;
+       bool                    s2;
+};
+
+struct s1_walk_result {
+       union {
+               struct {
+                       u64     desc;
+                       u64     pa;
+                       s8      level;
+                       u8      APTable;
+                       bool    UXNTable;
+                       bool    PXNTable;
+                       bool    uwxn;
+                       bool    uov;
+                       bool    ur;
+                       bool    uw;
+                       bool    ux;
+                       bool    pwxn;
+                       bool    pov;
+                       bool    pr;
+                       bool    pw;
+                       bool    px;
+               };
+               struct {
+                       u8      fst;
+                       bool    ptw;
+                       bool    s2;
+               };
+       };
+       bool    failed;
+};
+
+int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
+                      struct s1_walk_result *wr, u64 va);
+
 #endif /* __ARM64_KVM_NESTED_H */
index 7a5267f..7140690 100644 (file)
 #include <asm/kvm_hyp.h>
 #include <asm/kvm_mmu.h>
 
-enum trans_regime {
-       TR_EL10,
-       TR_EL20,
-       TR_EL2,
-};
-
-struct s1_walk_info {
-       u64                     baddr;
-       enum trans_regime       regime;
-       unsigned int            max_oa_bits;
-       unsigned int            pgshift;
-       unsigned int            txsz;
-       int                     sl;
-       bool                    hpd;
-       bool                    e0poe;
-       bool                    poe;
-       bool                    pan;
-       bool                    be;
-       bool                    s2;
-};
-
-struct s1_walk_result {
-       union {
-               struct {
-                       u64     desc;
-                       u64     pa;
-                       s8      level;
-                       u8      APTable;
-                       bool    UXNTable;
-                       bool    PXNTable;
-                       bool    uwxn;
-                       bool    uov;
-                       bool    ur;
-                       bool    uw;
-                       bool    ux;
-                       bool    pwxn;
-                       bool    pov;
-                       bool    pr;
-                       bool    pw;
-                       bool    px;
-               };
-               struct {
-                       u8      fst;
-                       bool    ptw;
-                       bool    s2;
-               };
-       };
-       bool    failed;
-};
-
 static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
 {
        wr->fst         = fst;
@@ -145,20 +95,15 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi)
        }
 }
 
-static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi,
+static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                         struct s1_walk_result *wr, u64 va)
 {
        u64 hcr, sctlr, tcr, tg, ps, ia_bits, ttbr;
        unsigned int stride, x;
-       bool va55, tbi, lva, as_el0;
+       bool va55, tbi, lva;
 
        hcr = __vcpu_sys_reg(vcpu, HCR_EL2);
 
-       wi->regime = compute_translation_regime(vcpu, op);
-       as_el0 = (op == OP_AT_S1E0R || op == OP_AT_S1E0W);
-       wi->pan = (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) &&
-                 (*vcpu_cpsr(vcpu) & PSR_PAN_BIT);
-
        va55 = va & BIT(55);
 
        if (wi->regime == TR_EL2 && va55)
@@ -319,7 +264,7 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi,
 
        /* R_BNDVG and following statements */
        if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR2_EL1, E0PD, IMP) &&
-           as_el0 && (tcr & (va55 ? TCR_E0PD1 : TCR_E0PD0)))
+           wi->as_el0 && (tcr & (va55 ? TCR_E0PD1 : TCR_E0PD0)))
                goto transfault_l0;
 
        /* AArch64.S1StartLevel() */
@@ -1155,7 +1100,12 @@ static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
        bool perm_fail = false;
        int ret, idx;
 
-       ret = setup_s1_walk(vcpu, op, &wi, &wr, vaddr);
+       wi.regime = compute_translation_regime(vcpu, op);
+       wi.as_el0 = (op == OP_AT_S1E0R || op == OP_AT_S1E0W);
+       wi.pan = (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) &&
+                (*vcpu_cpsr(vcpu) & PSR_PAN_BIT);
+
+       ret = setup_s1_walk(vcpu, &wi, &wr, vaddr);
        if (ret)
                goto compute_par;
 
@@ -1457,3 +1407,31 @@ void __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
        par = compute_par_s12(vcpu, par, &out);
        vcpu_write_sys_reg(vcpu, par, PAR_EL1);
 }
+
+/*
+ * Translate a VA for a given EL in a given translation regime, with
+ * or without PAN. This requires wi->{regime, as_el0, pan} to be
+ * set. The rest of the wi and wr should be 0-initialised.
+ */
+int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
+                      struct s1_walk_result *wr, u64 va)
+{
+       int ret;
+
+       ret = setup_s1_walk(vcpu, wi, wr, va);
+       if (ret)
+               return ret;
+
+       if (wr->level == S1_MMU_DISABLED) {
+               wr->ur = wr->uw = wr->ux = true;
+               wr->pr = wr->pw = wr->px = true;
+       } else {
+               ret = walk_s1(vcpu, wi, wr, va);
+               if (ret)
+                       return ret;
+
+               compute_s1_permissions(vcpu, wi, wr);
+       }
+
+       return 0;
+}