Commit 089606c0 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch kvm-arm64/selftest/ipa into kvmarm-master/next



* kvm-arm64/selftest/ipa:
  : .
  : Expand the KVM/arm64 selftest infrastructure to discover
  : supported page sizes at runtime, support 16kB pages, and
  : find out about the original M1 stupidly small IPA space.
  : .
  KVM: selftests: arm64: Add support for various modes with 16kB page size
  KVM: selftests: arm64: Add support for VM_MODE_P36V48_{4K,64K}
  KVM: selftests: arm64: Rework TCR_EL1 configuration
  KVM: selftests: arm64: Check for supported page sizes
  KVM: selftests: arm64: Introduce a variable default IPA size
  KVM: selftests: arm64: Initialise default guest mode at test startup time

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents 43d8ac22 aa674de1
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -113,6 +113,9 @@ enum {
#define ESR_EC_WP_CURRENT	0x35
#define ESR_EC_BRK_INS		0x3c

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

void vm_init_descriptor_tables(struct kvm_vm *vm);
void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid);

+9 −1
Original line number Diff line number Diff line
@@ -42,18 +42,26 @@ enum vm_guest_mode {
	VM_MODE_P52V48_4K,
	VM_MODE_P52V48_64K,
	VM_MODE_P48V48_4K,
	VM_MODE_P48V48_16K,
	VM_MODE_P48V48_64K,
	VM_MODE_P40V48_4K,
	VM_MODE_P40V48_16K,
	VM_MODE_P40V48_64K,
	VM_MODE_PXXV48_4K,	/* For 48bits VA but ANY bits PA */
	VM_MODE_P47V64_4K,
	VM_MODE_P44V64_4K,
	VM_MODE_P36V48_4K,
	VM_MODE_P36V48_16K,
	VM_MODE_P36V48_64K,
	VM_MODE_P36V47_16K,
	NUM_VM_MODES,
};

#if defined(__aarch64__)

#define VM_MODE_DEFAULT			VM_MODE_P40V48_4K
extern enum vm_guest_mode vm_mode_default;

#define VM_MODE_DEFAULT			vm_mode_default
#define MIN_PAGE_SHIFT			12U
#define ptes_per_page(page_size)	((page_size) / 8)

+75 −7
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/compiler.h>
#include <assert.h>

#include "guest_modes.h"
#include "kvm_util.h"
#include "../kvm_util_internal.h"
#include "processor.h"
@@ -237,6 +238,7 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init
	get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &sctlr_el1);
	get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TCR_EL1), &tcr_el1);

	/* Configure base granule size */
	switch (vm->mode) {
	case VM_MODE_P52V48_4K:
		TEST_FAIL("AArch64 does not support 4K sized pages "
@@ -245,25 +247,47 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init
		TEST_FAIL("AArch64 does not support 4K sized pages "
			  "with ANY-bit physical address ranges");
	case VM_MODE_P52V48_64K:
	case VM_MODE_P48V48_64K:
	case VM_MODE_P40V48_64K:
	case VM_MODE_P36V48_64K:
		tcr_el1 |= 1ul << 14; /* TG0 = 64KB */
		tcr_el1 |= 6ul << 32; /* IPS = 52 bits */
		break;
	case VM_MODE_P48V48_16K:
	case VM_MODE_P40V48_16K:
	case VM_MODE_P36V48_16K:
	case VM_MODE_P36V47_16K:
		tcr_el1 |= 2ul << 14; /* TG0 = 16KB */
		break;
	case VM_MODE_P48V48_4K:
	case VM_MODE_P40V48_4K:
	case VM_MODE_P36V48_4K:
		tcr_el1 |= 0ul << 14; /* TG0 = 4KB */
		tcr_el1 |= 5ul << 32; /* IPS = 48 bits */
		break;
	default:
		TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
	}

	/* Configure output size */
	switch (vm->mode) {
	case VM_MODE_P52V48_64K:
		tcr_el1 |= 6ul << 32; /* IPS = 52 bits */
		break;
	case VM_MODE_P48V48_4K:
	case VM_MODE_P48V48_16K:
	case VM_MODE_P48V48_64K:
		tcr_el1 |= 1ul << 14; /* TG0 = 64KB */
		tcr_el1 |= 5ul << 32; /* IPS = 48 bits */
		break;
	case VM_MODE_P40V48_4K:
		tcr_el1 |= 0ul << 14; /* TG0 = 4KB */
		tcr_el1 |= 2ul << 32; /* IPS = 40 bits */
		break;
	case VM_MODE_P40V48_16K:
	case VM_MODE_P40V48_64K:
		tcr_el1 |= 1ul << 14; /* TG0 = 64KB */
		tcr_el1 |= 2ul << 32; /* IPS = 40 bits */
		break;
	case VM_MODE_P36V48_4K:
	case VM_MODE_P36V48_16K:
	case VM_MODE_P36V48_64K:
	case VM_MODE_P36V47_16K:
		tcr_el1 |= 1ul << 32; /* IPS = 36 bits */
		break;
	default:
		TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
	}
@@ -432,3 +456,47 @@ 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)
{
	struct kvm_vcpu_init preferred_init;
	int kvm_fd, vm_fd, vcpu_fd, err;
	uint64_t val;
	struct kvm_one_reg reg = {
		.id	= KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1),
		.addr	= (uint64_t)&val,
	};

	kvm_fd = open_kvm_dev_path_or_exit();
	vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, ipa);
	TEST_ASSERT(vm_fd >= 0, "Can't create VM");

	vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);
	TEST_ASSERT(vcpu_fd >= 0, "Can't create vcpu");

	err = ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &preferred_init);
	TEST_ASSERT(err == 0, "Can't get target");
	err = ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &preferred_init);
	TEST_ASSERT(err == 0, "Can't get init vcpu");

	err = ioctl(vcpu_fd, KVM_GET_ONE_REG, &reg);
	TEST_ASSERT(err == 0, "Can't get MMFR0");

	*ps4k = ((val >> 28) & 0xf) != 0xf;
	*ps64k = ((val >> 24) & 0xf) == 0;
	*ps16k = ((val >> 20) & 0xf) != 0;

	close(vcpu_fd);
	close(vm_fd);
	close(kvm_fd);
}

/*
 * arm64 doesn't have a true default mode, so start by computing the
 * available IPA space and page sizes early.
 */
void __attribute__((constructor)) init_guest_modes(void)
{
       guest_modes_append_default();
}
+43 −6
Original line number Diff line number Diff line
@@ -4,22 +4,59 @@
 */
#include "guest_modes.h"

#ifdef __aarch64__
#include "processor.h"
enum vm_guest_mode vm_mode_default;
#endif

struct guest_mode guest_modes[NUM_VM_MODES];

void guest_modes_append_default(void)
{
#ifndef __aarch64__
	guest_mode_append(VM_MODE_DEFAULT, true, true);

#ifdef __aarch64__
	guest_mode_append(VM_MODE_P40V48_64K, true, true);
#else
	{
		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
		bool ps4k, ps16k, ps64k;
		int i;

		aarch64_get_supported_page_sizes(limit, &ps4k, &ps16k, &ps64k);

		vm_mode_default = NUM_VM_MODES;

		if (limit >= 52)
			guest_mode_append(VM_MODE_P52V48_64K, true, true);
			guest_mode_append(VM_MODE_P52V48_64K, ps64k, ps64k);
		if (limit >= 48) {
			guest_mode_append(VM_MODE_P48V48_4K, true, true);
			guest_mode_append(VM_MODE_P48V48_64K, true, true);
			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);
		}

		/*
		 * Pick the first supported IPA size if the default
		 * isn't available.
		 */
		for (i = 0; vm_mode_default == NUM_VM_MODES && i < NUM_VM_MODES; i++) {
			if (guest_modes[i].supported && guest_modes[i].enabled)
				vm_mode_default = i;
		}

		TEST_ASSERT(vm_mode_default != NUM_VM_MODES,
			    "No supported mode!");
	}
#endif
#ifdef __s390x__
+22 −0
Original line number Diff line number Diff line
@@ -166,12 +166,18 @@ const char *vm_guest_mode_string(uint32_t i)
		[VM_MODE_P52V48_4K]	= "PA-bits:52,  VA-bits:48,  4K pages",
		[VM_MODE_P52V48_64K]	= "PA-bits:52,  VA-bits:48, 64K pages",
		[VM_MODE_P48V48_4K]	= "PA-bits:48,  VA-bits:48,  4K pages",
		[VM_MODE_P48V48_16K]	= "PA-bits:48,  VA-bits:48, 16K pages",
		[VM_MODE_P48V48_64K]	= "PA-bits:48,  VA-bits:48, 64K pages",
		[VM_MODE_P40V48_4K]	= "PA-bits:40,  VA-bits:48,  4K pages",
		[VM_MODE_P40V48_16K]	= "PA-bits:40,  VA-bits:48, 16K pages",
		[VM_MODE_P40V48_64K]	= "PA-bits:40,  VA-bits:48, 64K pages",
		[VM_MODE_PXXV48_4K]	= "PA-bits:ANY, VA-bits:48,  4K pages",
		[VM_MODE_P47V64_4K]	= "PA-bits:47,  VA-bits:64,  4K pages",
		[VM_MODE_P44V64_4K]	= "PA-bits:44,  VA-bits:64,  4K pages",
		[VM_MODE_P36V48_4K]	= "PA-bits:36,  VA-bits:48,  4K pages",
		[VM_MODE_P36V48_16K]	= "PA-bits:36,  VA-bits:48, 16K pages",
		[VM_MODE_P36V48_64K]	= "PA-bits:36,  VA-bits:48, 64K pages",
		[VM_MODE_P36V47_16K]	= "PA-bits:36,  VA-bits:47, 16K pages",
	};
	_Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES,
		       "Missing new mode strings?");
@@ -185,12 +191,18 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
	[VM_MODE_P52V48_4K]	= { 52, 48,  0x1000, 12 },
	[VM_MODE_P52V48_64K]	= { 52, 48, 0x10000, 16 },
	[VM_MODE_P48V48_4K]	= { 48, 48,  0x1000, 12 },
	[VM_MODE_P48V48_16K]	= { 48, 48,  0x4000, 14 },
	[VM_MODE_P48V48_64K]	= { 48, 48, 0x10000, 16 },
	[VM_MODE_P40V48_4K]	= { 40, 48,  0x1000, 12 },
	[VM_MODE_P40V48_16K]	= { 40, 48,  0x4000, 14 },
	[VM_MODE_P40V48_64K]	= { 40, 48, 0x10000, 16 },
	[VM_MODE_PXXV48_4K]	= {  0,  0,  0x1000, 12 },
	[VM_MODE_P47V64_4K]	= { 47, 64,  0x1000, 12 },
	[VM_MODE_P44V64_4K]	= { 44, 64,  0x1000, 12 },
	[VM_MODE_P36V48_4K]	= { 36, 48,  0x1000, 12 },
	[VM_MODE_P36V48_16K]	= { 36, 48,  0x4000, 14 },
	[VM_MODE_P36V48_64K]	= { 36, 48, 0x10000, 16 },
	[VM_MODE_P36V47_16K]	= { 36, 47,  0x4000, 14 },
};
_Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES,
	       "Missing new mode params?");
@@ -252,9 +264,19 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
		vm->pgtable_levels = 3;
		break;
	case VM_MODE_P40V48_4K:
	case VM_MODE_P36V48_4K:
		vm->pgtable_levels = 4;
		break;
	case VM_MODE_P40V48_64K:
	case VM_MODE_P36V48_64K:
		vm->pgtable_levels = 3;
		break;
	case VM_MODE_P48V48_16K:
	case VM_MODE_P40V48_16K:
	case VM_MODE_P36V48_16K:
		vm->pgtable_levels = 4;
		break;
	case VM_MODE_P36V47_16K:
		vm->pgtable_levels = 3;
		break;
	case VM_MODE_PXXV48_4K: