]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW
authorOliver Upton <oupton@kernel.org>
Mon, 24 Nov 2025 19:01:55 +0000 (11:01 -0800)
committerOliver Upton <oupton@kernel.org>
Mon, 1 Dec 2025 08:44:02 +0000 (00:44 -0800)
Give the stage-2 walk similar treatment to stage-1: update the access
flag during the table walk and do so for any walk context.

Reviewed-by: Marc Zyngier <maz@kernel.org>
Tested-by: Marc Zyngier <maz@kernel.org>
Link: https://msgid.link/20251124190158.177318-14-oupton@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>
arch/arm64/kvm/mmu.c
arch/arm64/kvm/nested.c

index 96f1786c72fe0a762e2b27331b8ab2d741847392..b9aebca90f59618bec1e0e7c1d63e17780ec2119 100644 (file)
@@ -2012,6 +2012,11 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
                u32 esr;
 
                ret = kvm_walk_nested_s2(vcpu, fault_ipa, &nested_trans);
+               if (ret == -EAGAIN) {
+                       ret = 1;
+                       goto out_unlock;
+               }
+
                if (ret) {
                        esr = kvm_s2_trans_esr(&nested_trans);
                        kvm_inject_s2_fault(vcpu, esr);
index a096766c6ec3e598ca6cf0296ae5560d0854f86c..6495442f400af99bfba8fe901d34e6b1eca79692 100644 (file)
@@ -124,12 +124,13 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
 }
 
 struct s2_walk_info {
-       u64          baddr;
-       unsigned int max_oa_bits;
-       unsigned int pgshift;
-       unsigned int sl;
-       unsigned int t0sz;
-       bool         be;
+       u64             baddr;
+       unsigned int    max_oa_bits;
+       unsigned int    pgshift;
+       unsigned int    sl;
+       unsigned int    t0sz;
+       bool            be;
+       bool            ha;
 };
 
 static u32 compute_fsc(int level, u32 fsc)
@@ -219,6 +220,20 @@ static int read_guest_s2_desc(struct kvm_vcpu *vcpu, phys_addr_t pa, u64 *desc,
        return 0;
 }
 
+static int swap_guest_s2_desc(struct kvm_vcpu *vcpu, phys_addr_t pa, u64 old, u64 new,
+                             struct s2_walk_info *wi)
+{
+       if (wi->be) {
+               old = cpu_to_be64(old);
+               new = cpu_to_be64(new);
+       } else {
+               old = cpu_to_le64(old);
+               new = cpu_to_le64(new);
+       }
+
+       return __kvm_at_swap_desc(vcpu->kvm, pa, old, new);
+}
+
 /*
  * This is essentially a C-version of the pseudo code from the ARM ARM
  * AArch64.TranslationTableWalk  function.  I strongly recommend looking at
@@ -232,7 +247,7 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,
        int first_block_level, level, stride, input_size, base_lower_bound;
        phys_addr_t base_addr;
        unsigned int addr_top, addr_bottom;
-       u64 desc;  /* page table entry */
+       u64 desc, new_desc;  /* page table entry */
        int ret;
        phys_addr_t paddr;
 
@@ -281,6 +296,8 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,
                if (ret < 0)
                        return ret;
 
+               new_desc = desc;
+
                /* Check for valid descriptor at this point */
                if (!(desc & KVM_PTE_VALID)) {
                        out->esr = compute_fsc(level, ESR_ELx_FSC_FAULT);
@@ -325,6 +342,17 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,
                return 1;
        }
 
+       if (wi->ha)
+               new_desc |= KVM_PTE_LEAF_ATTR_LO_S2_AF;
+
+       if (new_desc != desc) {
+               ret = swap_guest_s2_desc(vcpu, paddr, desc, new_desc, wi);
+               if (ret)
+                       return ret;
+
+               desc = new_desc;
+       }
+
        if (!(desc & KVM_PTE_LEAF_ATTR_LO_S2_AF)) {
                out->esr = compute_fsc(level, ESR_ELx_FSC_ACCESS);
                out->desc = desc;
@@ -363,6 +391,8 @@ static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi)
        /* Global limit for now, should eventually be per-VM */
        wi->max_oa_bits = min(get_kvm_ipa_limit(),
                              ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false));
+
+       wi->ha = vtcr & VTCR_EL2_HA;
 }
 
 int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,