mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 18:12:25 -04:00
KVM: arm64: Fix PAR_EL1.{PTW,S} reporting on AT S1E*
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>
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user