Commit 493b01de authored by Marc Zyngier's avatar Marc Zyngier
Browse files

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: default avatarMarc Zyngier <maz@kernel.org>
parent 9c32cda4
Loading
Loading
Loading
Loading
+11 −12
Original line number Diff line number Diff line
@@ -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);