]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: nv: Respect stage-2 write permssion when setting stage-1 AF
authorOliver Upton <oupton@kernel.org>
Thu, 8 Jan 2026 20:42:30 +0000 (12:42 -0800)
committerOliver Upton <oupton@kernel.org>
Fri, 9 Jan 2026 23:40:34 +0000 (15:40 -0800)
Naturally, updating the Access Flag in a stage-1 descriptor requires
write permission at stage-2, although this isn't actually enforced in
KVM's software PTW.

Generate a stage-2 permission fault if the stage-1 walk attempts to
update the descriptor and its corresponding stage-2 translation lacks
write permission.

Fixes: bff8aa213dee ("KVM: arm64: Implement HW access flag management in stage-1 SW PTW")
Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://msgid.link/20260108204230.677172-1-oupton@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>
arch/arm64/kvm/at.c

index 53bf70126f81dda8eb1e06f6228e3be7f3794d13..808d26bed1824adfe8bd9d835e6b2a3f49637921 100644 (file)
@@ -403,6 +403,7 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                   struct s1_walk_result *wr, u64 va)
 {
        u64 va_top, va_bottom, baddr, desc, new_desc, ipa;
+       struct kvm_s2_trans s2_trans = {};
        int level, stride, ret;
 
        level = wi->sl;
@@ -420,8 +421,6 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                ipa = baddr | index;
 
                if (wi->s2) {
-                       struct kvm_s2_trans s2_trans = {};
-
                        ret = kvm_walk_nested_s2(vcpu, ipa, &s2_trans);
                        if (ret) {
                                fail_s1_walk(wr,
@@ -515,6 +514,11 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                new_desc |= PTE_AF;
 
        if (new_desc != desc) {
+               if (wi->s2 && !kvm_s2_trans_writable(&s2_trans)) {
+                       fail_s1_walk(wr, ESR_ELx_FSC_PERM_L(level), true);
+                       return -EPERM;
+               }
+
                ret = kvm_swap_s1_desc(vcpu, ipa, desc, new_desc, wi);
                if (ret)
                        return ret;