Commit c317c6d2 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch kvm-arm64/aarch32-raz-idregs into kvmarm-master/next



* kvm-arm64/aarch32-raz-idregs:
  : .
  : Rework AArch32 ID registers exposed by KVM on AArch64-only
  : systems by treating them as RAZ/WI instead as UNKOWN as
  : architected, which allows them to be trivially migrated
  : between different systems.
  :
  : Patches courtesy of Oliver Upton.
  : .
  KVM: selftests: Add test for AArch32 ID registers
  KVM: arm64: Treat 32bit ID registers as RAZ/WI on 64bit-only system
  KVM: arm64: Add a visibility bit to ignore user writes
  KVM: arm64: Spin off helper for calling visibility hook
  KVM: arm64: Drop raz parameter from read_id_reg()
  KVM: arm64: Remove internal accessor helpers for id regs
  KVM: arm64: Use visibility hook to treat ID regs as RAZ

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents b90cb105 797b8451
Loading
Loading
Loading
Loading
+71 −79
Original line number Diff line number Diff line
@@ -1063,13 +1063,12 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
}

/* Read a sanitised cpufeature ID register by sys_reg_desc */
static u64 read_id_reg(const struct kvm_vcpu *vcpu,
		struct sys_reg_desc const *r, bool raz)
static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r)
{
	u32 id = reg_to_encoding(r);
	u64 val;

	if (raz)
	if (sysreg_visible_as_raz(vcpu, r))
		return 0;

	val = read_sanitised_ftr_reg(id);
@@ -1145,34 +1144,37 @@ static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
	return 0;
}

/* cpufeature ID register access trap handlers */

static bool __access_id_reg(struct kvm_vcpu *vcpu,
			    struct sys_reg_params *p,
			    const struct sys_reg_desc *r,
			    bool raz)
static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
				       const struct sys_reg_desc *r)
{
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);
	/*
	 * AArch32 ID registers are UNKNOWN if AArch32 isn't implemented at any
	 * EL. Promote to RAZ/WI in order to guarantee consistency between
	 * systems.
	 */
	if (!kvm_supports_32bit_el0())
		return REG_RAZ | REG_USER_WI;

	p->regval = read_id_reg(vcpu, r, raz);
	return true;
	return id_visibility(vcpu, r);
}

static bool access_id_reg(struct kvm_vcpu *vcpu,
			  struct sys_reg_params *p,
static unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
				   const struct sys_reg_desc *r)
{
	bool raz = sysreg_visible_as_raz(vcpu, r);

	return __access_id_reg(vcpu, p, r, raz);
	return REG_RAZ;
}

static bool access_raz_id_reg(struct kvm_vcpu *vcpu,
/* cpufeature ID register access trap handlers */

static bool access_id_reg(struct kvm_vcpu *vcpu,
			  struct sys_reg_params *p,
			  const struct sys_reg_desc *r)
{
	return __access_id_reg(vcpu, p, r, true);
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);

	p->regval = read_id_reg(vcpu, r);
	return true;
}

/* Visibility overrides for SVE-specific control registers */
@@ -1208,7 +1210,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
		return -EINVAL;

	/* We can only differ with CSV[23], and anything else is an error */
	val ^= read_id_reg(vcpu, rd, false);
	val ^= read_id_reg(vcpu, rd);
	val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
		 (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
	if (val)
@@ -1227,45 +1229,21 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 * are stored, and for set_id_reg() we don't allow the effective value
 * to be changed.
 */
static int __get_id_reg(const struct kvm_vcpu *vcpu,
			const struct sys_reg_desc *rd, u64 *val,
			bool raz)
{
	*val = read_id_reg(vcpu, rd, raz);
	return 0;
}

static int __set_id_reg(const struct kvm_vcpu *vcpu,
			const struct sys_reg_desc *rd, u64 val,
			bool raz)
{
	/* This is what we mean by invariant: you can't change it. */
	if (val != read_id_reg(vcpu, rd, raz))
		return -EINVAL;

	return 0;
}

static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
		      u64 *val)
{
	bool raz = sysreg_visible_as_raz(vcpu, rd);

	return __get_id_reg(vcpu, rd, val, raz);
	*val = read_id_reg(vcpu, rd);
	return 0;
}

static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
		      u64 val)
{
	bool raz = sysreg_visible_as_raz(vcpu, rd);

	return __set_id_reg(vcpu, rd, val, raz);
}
	/* This is what we mean by invariant: you can't change it. */
	if (val != read_id_reg(vcpu, rd))
		return -EINVAL;

static int set_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
			  u64 val)
{
	return __set_id_reg(vcpu, rd, val, true);
	return 0;
}

static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -1367,6 +1345,15 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
	.visibility = id_visibility,		\
}

/* sys_reg_desc initialiser for known cpufeature ID registers */
#define AA32_ID_SANITISED(name) {		\
	SYS_DESC(SYS_##name),			\
	.access	= access_id_reg,		\
	.get_user = get_id_reg,			\
	.set_user = set_id_reg,			\
	.visibility = aa32_id_visibility,	\
}

/*
 * sys_reg_desc initialiser for architecturally unallocated cpufeature ID
 * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
@@ -1374,9 +1361,10 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
 */
#define ID_UNALLOCATED(crm, op2) {			\
	Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),	\
	.access = access_raz_id_reg,			\
	.get_user = get_raz_reg,			\
	.set_user = set_raz_id_reg,			\
	.access = access_id_reg,			\
	.get_user = get_id_reg,				\
	.set_user = set_id_reg,				\
	.visibility = raz_visibility			\
}

/*
@@ -1386,9 +1374,10 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
 */
#define ID_HIDDEN(name) {			\
	SYS_DESC(SYS_##name),			\
	.access = access_raz_id_reg,		\
	.get_user = get_raz_reg,		\
	.set_user = set_raz_id_reg,		\
	.access = access_id_reg,		\
	.get_user = get_id_reg,			\
	.set_user = set_id_reg,			\
	.visibility = raz_visibility,		\
}

/*
@@ -1452,33 +1441,33 @@ static const struct sys_reg_desc sys_reg_descs[] = {

	/* AArch64 mappings of the AArch32 ID registers */
	/* CRm=1 */
	ID_SANITISED(ID_PFR0_EL1),
	ID_SANITISED(ID_PFR1_EL1),
	ID_SANITISED(ID_DFR0_EL1),
	AA32_ID_SANITISED(ID_PFR0_EL1),
	AA32_ID_SANITISED(ID_PFR1_EL1),
	AA32_ID_SANITISED(ID_DFR0_EL1),
	ID_HIDDEN(ID_AFR0_EL1),
	ID_SANITISED(ID_MMFR0_EL1),
	ID_SANITISED(ID_MMFR1_EL1),
	ID_SANITISED(ID_MMFR2_EL1),
	ID_SANITISED(ID_MMFR3_EL1),
	AA32_ID_SANITISED(ID_MMFR0_EL1),
	AA32_ID_SANITISED(ID_MMFR1_EL1),
	AA32_ID_SANITISED(ID_MMFR2_EL1),
	AA32_ID_SANITISED(ID_MMFR3_EL1),

	/* CRm=2 */
	ID_SANITISED(ID_ISAR0_EL1),
	ID_SANITISED(ID_ISAR1_EL1),
	ID_SANITISED(ID_ISAR2_EL1),
	ID_SANITISED(ID_ISAR3_EL1),
	ID_SANITISED(ID_ISAR4_EL1),
	ID_SANITISED(ID_ISAR5_EL1),
	ID_SANITISED(ID_MMFR4_EL1),
	ID_SANITISED(ID_ISAR6_EL1),
	AA32_ID_SANITISED(ID_ISAR0_EL1),
	AA32_ID_SANITISED(ID_ISAR1_EL1),
	AA32_ID_SANITISED(ID_ISAR2_EL1),
	AA32_ID_SANITISED(ID_ISAR3_EL1),
	AA32_ID_SANITISED(ID_ISAR4_EL1),
	AA32_ID_SANITISED(ID_ISAR5_EL1),
	AA32_ID_SANITISED(ID_MMFR4_EL1),
	AA32_ID_SANITISED(ID_ISAR6_EL1),

	/* CRm=3 */
	ID_SANITISED(MVFR0_EL1),
	ID_SANITISED(MVFR1_EL1),
	ID_SANITISED(MVFR2_EL1),
	AA32_ID_SANITISED(MVFR0_EL1),
	AA32_ID_SANITISED(MVFR1_EL1),
	AA32_ID_SANITISED(MVFR2_EL1),
	ID_UNALLOCATED(3,3),
	ID_SANITISED(ID_PFR2_EL1),
	AA32_ID_SANITISED(ID_PFR2_EL1),
	ID_HIDDEN(ID_DFR1_EL1),
	ID_SANITISED(ID_MMFR5_EL1),
	AA32_ID_SANITISED(ID_MMFR5_EL1),
	ID_UNALLOCATED(3,7),

	/* AArch64 ID registers */
@@ -2809,6 +2798,9 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
	if (!r)
		return -ENOENT;

	if (sysreg_user_write_ignore(vcpu, r))
		return 0;

	if (r->set_user) {
		ret = (r->set_user)(vcpu, r, val);
	} else {
+17 −7
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ struct sys_reg_desc {

#define REG_HIDDEN		(1 << 0) /* hidden from userspace and guest */
#define REG_RAZ			(1 << 1) /* RAZ from userspace and guest */
#define REG_USER_WI		(1 << 2) /* WI from userspace only */

static __printf(2, 3)
inline void print_sys_reg_msg(const struct sys_reg_params *p,
@@ -136,22 +137,31 @@ static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r
	__vcpu_sys_reg(vcpu, r->reg) = r->val;
}

static inline bool sysreg_hidden(const struct kvm_vcpu *vcpu,
static inline unsigned int sysreg_visibility(const struct kvm_vcpu *vcpu,
					     const struct sys_reg_desc *r)
{
	if (likely(!r->visibility))
		return false;
		return 0;

	return r->visibility(vcpu, r);
}

	return r->visibility(vcpu, r) & REG_HIDDEN;
static inline bool sysreg_hidden(const struct kvm_vcpu *vcpu,
				 const struct sys_reg_desc *r)
{
	return sysreg_visibility(vcpu, r) & REG_HIDDEN;
}

static inline bool sysreg_visible_as_raz(const struct kvm_vcpu *vcpu,
					 const struct sys_reg_desc *r)
{
	if (likely(!r->visibility))
		return false;
	return sysreg_visibility(vcpu, r) & REG_RAZ;
}

	return r->visibility(vcpu, r) & REG_RAZ;
static inline bool sysreg_user_write_ignore(const struct kvm_vcpu *vcpu,
					    const struct sys_reg_desc *r)
{
	return sysreg_visibility(vcpu, r) & REG_USER_WI;
}

static inline int cmp_sys_reg(const struct sys_reg_desc *i1,
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
/aarch64/aarch32_id_regs
/aarch64/arch_timer
/aarch64/debug-exceptions
/aarch64/get-reg-list
+1 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test

TEST_GEN_PROGS_aarch64 += aarch64/aarch32_id_regs
TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
+169 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * aarch32_id_regs - Test for ID register behavior on AArch64-only systems
 *
 * Copyright (c) 2022 Google LLC.
 *
 * Test that KVM handles the AArch64 views of the AArch32 ID registers as RAZ
 * and WI from userspace.
 */

#include <stdint.h>

#include "kvm_util.h"
#include "processor.h"
#include "test_util.h"

#define BAD_ID_REG_VAL	0x1badc0deul

#define GUEST_ASSERT_REG_RAZ(reg)	GUEST_ASSERT_EQ(read_sysreg_s(reg), 0)

static void guest_main(void)
{
	GUEST_ASSERT_REG_RAZ(SYS_ID_PFR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_PFR1_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_DFR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_AFR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR1_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR2_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR3_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR1_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR2_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR3_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR4_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR5_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR4_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR6_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_MVFR0_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_MVFR1_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_MVFR2_EL1);
	GUEST_ASSERT_REG_RAZ(sys_reg(3, 0, 0, 3, 3));
	GUEST_ASSERT_REG_RAZ(SYS_ID_PFR2_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_DFR1_EL1);
	GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR5_EL1);
	GUEST_ASSERT_REG_RAZ(sys_reg(3, 0, 0, 3, 7));

	GUEST_DONE();
}

static void test_guest_raz(struct kvm_vcpu *vcpu)
{
	struct ucall uc;

	vcpu_run(vcpu);

	switch (get_ucall(vcpu, &uc)) {
	case UCALL_ABORT:
		REPORT_GUEST_ASSERT(uc);
		break;
	case UCALL_DONE:
		break;
	default:
		TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
	}
}

static uint64_t raz_wi_reg_ids[] = {
	KVM_ARM64_SYS_REG(SYS_ID_PFR0_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_PFR1_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_DFR0_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR0_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR1_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR2_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR3_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR0_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR1_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR2_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR3_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR4_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR5_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR4_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_ISAR6_EL1),
	KVM_ARM64_SYS_REG(SYS_MVFR0_EL1),
	KVM_ARM64_SYS_REG(SYS_MVFR1_EL1),
	KVM_ARM64_SYS_REG(SYS_MVFR2_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_PFR2_EL1),
	KVM_ARM64_SYS_REG(SYS_ID_MMFR5_EL1),
};

static void test_user_raz_wi(struct kvm_vcpu *vcpu)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(raz_wi_reg_ids); i++) {
		uint64_t reg_id = raz_wi_reg_ids[i];
		uint64_t val;

		vcpu_get_reg(vcpu, reg_id, &val);
		ASSERT_EQ(val, 0);

		/*
		 * Expect the ioctl to succeed with no effect on the register
		 * value.
		 */
		vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL);

		vcpu_get_reg(vcpu, reg_id, &val);
		ASSERT_EQ(val, 0);
	}
}

static uint64_t raz_invariant_reg_ids[] = {
	KVM_ARM64_SYS_REG(SYS_ID_AFR0_EL1),
	KVM_ARM64_SYS_REG(sys_reg(3, 0, 0, 3, 3)),
	KVM_ARM64_SYS_REG(SYS_ID_DFR1_EL1),
	KVM_ARM64_SYS_REG(sys_reg(3, 0, 0, 3, 7)),
};

static void test_user_raz_invariant(struct kvm_vcpu *vcpu)
{
	int i, r;

	for (i = 0; i < ARRAY_SIZE(raz_invariant_reg_ids); i++) {
		uint64_t reg_id = raz_invariant_reg_ids[i];
		uint64_t val;

		vcpu_get_reg(vcpu, reg_id, &val);
		ASSERT_EQ(val, 0);

		r = __vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL);
		TEST_ASSERT(r < 0 && errno == EINVAL,
			    "unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);

		vcpu_get_reg(vcpu, reg_id, &val);
		ASSERT_EQ(val, 0);
	}
}



static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu)
{
	uint64_t val, el0;

	vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);

	el0 = (val & ARM64_FEATURE_MASK(ID_AA64PFR0_EL0)) >> ID_AA64PFR0_EL0_SHIFT;
	return el0 == ID_AA64PFR0_ELx_64BIT_ONLY;
}

int main(void)
{
	struct kvm_vcpu *vcpu;
	struct kvm_vm *vm;

	vm = vm_create_with_one_vcpu(&vcpu, guest_main);

	TEST_REQUIRE(vcpu_aarch64_only(vcpu));

	ucall_init(vm, NULL);

	test_user_raz_wi(vcpu);
	test_user_raz_invariant(vcpu);
	test_guest_raz(vcpu);

	ucall_uninit(vm);
	kvm_vm_free(vm);
}