Commit 72324ac5 authored by Ryan Roberts's avatar Ryan Roberts Committed by Marc Zyngier
Browse files

KVM: selftests: arm64: Determine max ipa size per-page size



We are about to add 52 bit PA guest modes for 4K and 16K pages when the
system supports LPA2. In preparation beef up the logic that parses mmfr0
to also tell us what the maximum supported PA size is for each page
size. Max PA size = 0 implies the page size is not supported at all.

Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarRyan Roberts <ryan.roberts@arm.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231127111737.1897081-12-ryan.roberts@arm.com
parent d782ac5b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -119,8 +119,8 @@ enum {
/* Access flag update enable/disable */
#define TCR_EL1_HA		(1ULL << 39)

void aarch64_get_supported_page_sizes(uint32_t ipa,
				      bool *ps4k, bool *ps16k, bool *ps64k);
void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k,
					uint32_t *ipa16k, uint32_t *ipa64k);

void vm_init_descriptor_tables(struct kvm_vm *vm);
void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu);
+2 −2
Original line number Diff line number Diff line
@@ -11,8 +11,8 @@ struct guest_mode {

extern struct guest_mode guest_modes[NUM_VM_MODES];

#define guest_mode_append(mode, supported, enabled) ({ \
	guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
#define guest_mode_append(mode, enabled) ({ \
	guest_modes[mode] = (struct guest_mode){ (enabled), (enabled) }; \
})

void guest_modes_append_default(void);
+25 −5
Original line number Diff line number Diff line
@@ -492,12 +492,24 @@ uint32_t guest_get_vcpuid(void)
	return read_sysreg(tpidr_el1);
}

void aarch64_get_supported_page_sizes(uint32_t ipa,
				      bool *ps4k, bool *ps16k, bool *ps64k)
static uint32_t max_ipa_for_page_size(uint32_t vm_ipa, uint32_t gran,
				uint32_t not_sup_val, uint32_t ipa52_min_val)
{
	if (gran == not_sup_val)
		return 0;
	else if (gran >= ipa52_min_val && vm_ipa >= 52)
		return 52;
	else
		return min(vm_ipa, 48U);
}

void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k,
					uint32_t *ipa16k, uint32_t *ipa64k)
{
	struct kvm_vcpu_init preferred_init;
	int kvm_fd, vm_fd, vcpu_fd, err;
	uint64_t val;
	uint32_t gran;
	struct kvm_one_reg reg = {
		.id	= KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1),
		.addr	= (uint64_t)&val,
@@ -518,9 +530,17 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
	err = ioctl(vcpu_fd, KVM_GET_ONE_REG, &reg);
	TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));

	*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val) != 0xf;
	*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val) == 0;
	*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val) != 0;
	gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val);
	*ipa4k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN4_NI,
					ID_AA64MMFR0_EL1_TGRAN4_52_BIT);

	gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val);
	*ipa64k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN64_NI,
					ID_AA64MMFR0_EL1_TGRAN64_IMP);

	gran = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val);
	*ipa16k = max_ipa_for_page_size(ipa, gran, ID_AA64MMFR0_EL1_TGRAN16_NI,
					ID_AA64MMFR0_EL1_TGRAN16_52_BIT);

	close(vcpu_fd);
	close(vm_fd);
+21 −27
Original line number Diff line number Diff line
@@ -14,37 +14,31 @@ struct guest_mode guest_modes[NUM_VM_MODES];
void guest_modes_append_default(void)
{
#ifndef __aarch64__
	guest_mode_append(VM_MODE_DEFAULT, true, true);
	guest_mode_append(VM_MODE_DEFAULT, true);
#else
	{
		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
		bool ps4k, ps16k, ps64k;
		uint32_t ipa4k, ipa16k, ipa64k;
		int i;

		aarch64_get_supported_page_sizes(limit, &ps4k, &ps16k, &ps64k);
		aarch64_get_supported_page_sizes(limit, &ipa4k, &ipa16k, &ipa64k);

		vm_mode_default = NUM_VM_MODES;
		guest_mode_append(VM_MODE_P52V48_64K, ipa64k >= 52);

		if (limit >= 52)
			guest_mode_append(VM_MODE_P52V48_64K, ps64k, ps64k);
		if (limit >= 48) {
			guest_mode_append(VM_MODE_P48V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P48V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P48V48_64K, ps64k, ps64k);
		}
		if (limit >= 40) {
			guest_mode_append(VM_MODE_P40V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P40V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P40V48_64K, ps64k, ps64k);
			if (ps4k)
				vm_mode_default = VM_MODE_P40V48_4K;
		}
		if (limit >= 36) {
			guest_mode_append(VM_MODE_P36V48_4K, ps4k, ps4k);
			guest_mode_append(VM_MODE_P36V48_16K, ps16k, ps16k);
			guest_mode_append(VM_MODE_P36V48_64K, ps64k, ps64k);
			guest_mode_append(VM_MODE_P36V47_16K, ps16k, ps16k);
		}
		guest_mode_append(VM_MODE_P48V48_4K, ipa4k >= 48);
		guest_mode_append(VM_MODE_P48V48_16K, ipa16k >= 48);
		guest_mode_append(VM_MODE_P48V48_64K, ipa64k >= 48);

		guest_mode_append(VM_MODE_P40V48_4K, ipa4k >= 40);
		guest_mode_append(VM_MODE_P40V48_16K, ipa16k >= 40);
		guest_mode_append(VM_MODE_P40V48_64K, ipa64k >= 40);

		guest_mode_append(VM_MODE_P36V48_4K, ipa4k >= 36);
		guest_mode_append(VM_MODE_P36V48_16K, ipa16k >= 36);
		guest_mode_append(VM_MODE_P36V48_64K, ipa64k >= 36);
		guest_mode_append(VM_MODE_P36V47_16K, ipa16k >= 36);

		vm_mode_default = ipa4k >= 40 ? VM_MODE_P40V48_4K : NUM_VM_MODES;

		/*
		 * Pick the first supported IPA size if the default
@@ -72,7 +66,7 @@ void guest_modes_append_default(void)
		close(kvm_fd);
		/* Starting with z13 we have 47bits of physical address */
		if (info.ibc >= 0x30)
			guest_mode_append(VM_MODE_P47V64_4K, true, true);
			guest_mode_append(VM_MODE_P47V64_4K, true);
	}
#endif
#ifdef __riscv
@@ -80,9 +74,9 @@ void guest_modes_append_default(void)
		unsigned int sz = kvm_check_cap(KVM_CAP_VM_GPA_BITS);

		if (sz >= 52)
			guest_mode_append(VM_MODE_P52V48_4K, true, true);
			guest_mode_append(VM_MODE_P52V48_4K, true);
		if (sz >= 48)
			guest_mode_append(VM_MODE_P48V48_4K, true, true);
			guest_mode_append(VM_MODE_P48V48_4K, true);
	}
#endif
}