Commit 105485a1 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

KVM: arm64: Fix handling of FEAT_GTG for unimplemented granule sizes



Booting an EL2 guest on a system only supporting a subset of the
possible page sizes leads to interesting situations.

For example, on a system that only supports 4kB and 64kB, and is
booted with a 4kB kernel, we end-up advertising 16kB support at
stage-2, which is pretty weird.

That's because we consider that any S2 bigger than our base granule
is fair game, irrespective of what the HW actually supports. While this
is not impossible to support (KVM would happily handle it), it is likely
to be confusing for the guest.

Add new checks that will verify that this granule size is actually
supported before publishing it to the guest.

Fixes: e7ef6ed4 ("KVM: arm64: Enforce NV limits on a per-idregs basis")
Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 0e02219f
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -1402,6 +1402,21 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
	}
}

#define has_tgran_2(__r, __sz)						\
	({								\
		u64 _s1, _s2, _mmfr0 = __r;				\
									\
		_s2 = SYS_FIELD_GET(ID_AA64MMFR0_EL1,			\
				    TGRAN##__sz##_2, _mmfr0);		\
									\
		_s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1,			\
				    TGRAN##__sz, _mmfr0);		\
									\
		((_s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI &&		\
		  _s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \
		 (_s2 == ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \
		  _s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI));		\
	})
/*
 * Our emulated CPU doesn't support all the possible features. For the
 * sake of simplicity (and probably mental sanity), wipe out a number
@@ -1411,6 +1426,8 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
 */
u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
{
	u64 orig_val = val;

	switch (reg) {
	case SYS_ID_AA64ISAR0_EL1:
		/* Support everything but TME */
@@ -1480,12 +1497,15 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)
		 */
		switch (PAGE_SIZE) {
		case SZ_4K:
			if (has_tgran_2(orig_val, 4))
				val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN4_2, IMP);
			fallthrough;
		case SZ_16K:
			if (has_tgran_2(orig_val, 16))
				val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN16_2, IMP);
			fallthrough;
		case SZ_64K:
			if (has_tgran_2(orig_val, 64))
				val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN64_2, IMP);
			break;
		}