KVM: arm64: Fix PAR_EL1.{PTW,S} reporting on AT S1E*
authorMarc Zyngier <maz@kernel.org>
Tue, 22 Apr 2025 12:26:10 +0000 (13:26 +0100)
committerMarc Zyngier <maz@kernel.org>
Wed, 14 May 2025 09:43:50 +0000 (10:43 +0100)
When an AT S1E* operation fails, we need to report whether the
translation failed at S2, and whether this was during a S1 PTW.

But these two bits are not independent. PAR_EL1.PTW can only be
set of PAR_EL1.S is also set, and PAR_EL1.S can only be set on
its own when the full S1 PTW has succeeded, but that the access
itself is reporting a fault at S2.

As a result, it makes no sense to carry both ptw and s2 as parameters
to fail_s1_walk(), and they should be unified.

This fixes a number of cases where we were reporting PTW=1 *and*
S=0, which makes no sense.

Link: https://lore.kernel.org/r/20250422122612.2675672-2-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/at.c

index f74a66ce3064b7a39905dfc9af10c4cb260e96f1..3a4568e2de9103a8d0543867ffa21bb63b51df5c 100644 (file)
@@ -60,11 +60,11 @@ struct s1_walk_result {
        bool    failed;
 };
 
-static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool ptw, bool s2)
+static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
 {
        wr->fst         = fst;
-       wr->ptw         = ptw;
-       wr->s2          = s2;
+       wr->ptw         = s1ptw;
+       wr->s2          = s1ptw;
        wr->failed      = true;
 }
 
@@ -345,11 +345,11 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi,
        return 0;
 
 addrsz:                                /* Address Size Fault level 0 */
-       fail_s1_walk(wr, ESR_ELx_FSC_ADDRSZ_L(0), false, false);
+       fail_s1_walk(wr, ESR_ELx_FSC_ADDRSZ_L(0), false);
        return -EFAULT;
 
 transfault_l0:                 /* Translation Fault level 0 */
-       fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(0), false, false);
+       fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(0), false);
        return -EFAULT;
 }
 
@@ -380,13 +380,13 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
                        if (ret) {
                                fail_s1_walk(wr,
                                             (s2_trans.esr & ~ESR_ELx_FSC_LEVEL) | level,
-                                            true, true);
+                                            true);
                                return ret;
                        }
 
                        if (!kvm_s2_trans_readable(&s2_trans)) {
                                fail_s1_walk(wr, ESR_ELx_FSC_PERM_L(level),
-                                            true, true);
+                                            true);
 
                                return -EPERM;
                        }
@@ -396,8 +396,7 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
 
                ret = kvm_read_guest(vcpu->kvm, ipa, &desc, sizeof(desc));
                if (ret) {
-                       fail_s1_walk(wr, ESR_ELx_FSC_SEA_TTW(level),
-                                    true, false);
+                       fail_s1_walk(wr, ESR_ELx_FSC_SEA_TTW(level), false);
                        return ret;
                }
 
@@ -468,10 +467,10 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
        return 0;
 
 addrsz:
-       fail_s1_walk(wr, ESR_ELx_FSC_ADDRSZ_L(level), true, false);
+       fail_s1_walk(wr, ESR_ELx_FSC_ADDRSZ_L(level), false);
        return -EINVAL;
 transfault:
-       fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(level), true, false);
+       fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(level), false);
        return -ENOENT;
 }
 
@@ -1198,7 +1197,7 @@ static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
        }
 
        if (perm_fail)
-               fail_s1_walk(&wr, ESR_ELx_FSC_PERM_L(wr.level), false, false);
+               fail_s1_walk(&wr, ESR_ELx_FSC_PERM_L(wr.level), false);
 
 compute_par:
        return compute_par_s1(vcpu, &wr, wi.regime);