[PATCH v5 5/6] KVM: arm64: Introduce ID register specific descriptor

Jing Zhang jingzhangos at google.com
Sun Apr 2 11:37:34 PDT 2023


Introduce an ID feature register specific descriptor to include ID
register specific fields and callbacks besides its corresponding
general system register descriptor.

No functional change intended.

Co-developed-by: Reiji Watanabe <reijiw at google.com>
Signed-off-by: Reiji Watanabe <reijiw at google.com>
Signed-off-by: Jing Zhang <jingzhangos at google.com>
---
 arch/arm64/kvm/id_regs.c  | 233 ++++++++++++++++++++++++++++----------
 arch/arm64/kvm/sys_regs.c |   2 +-
 arch/arm64/kvm/sys_regs.h |   1 +
 3 files changed, 178 insertions(+), 58 deletions(-)

diff --git a/arch/arm64/kvm/id_regs.c b/arch/arm64/kvm/id_regs.c
index e92eacb0ad32..af86001e2686 100644
--- a/arch/arm64/kvm/id_regs.c
+++ b/arch/arm64/kvm/id_regs.c
@@ -18,6 +18,27 @@
 
 #include "sys_regs.h"
 
+struct id_reg_desc {
+	const struct sys_reg_desc	reg_desc;
+	/*
+	 * ftr_bits points to the feature bits array defined in cpufeature.c for
+	 * writable CPU ID feature register.
+	 */
+	const struct arm64_ftr_bits *ftr_bits;
+	/*
+	 * Only bits with 1 are writable from userspace.
+	 * This mask might not be necessary in the future whenever all ID
+	 * registers are enabled as writable from userspace.
+	 */
+	const u64 writable_mask;
+	/*
+	 * This function returns the KVM sanitised register value.
+	 * The value would be the same as the host kernel sanitised value if
+	 * there is no KVM sanitisation for this id register.
+	 */
+	u64 (*read_kvm_sanitised_reg)(const struct id_reg_desc *idr);
+};
+
 static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
 {
 	if (kvm_vcpu_has_pmu(vcpu))
@@ -193,6 +214,11 @@ static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
 	return id_visibility(vcpu, r);
 }
 
+static u64 general_read_kvm_sanitised_reg(const struct id_reg_desc *idr)
+{
+	return read_sanitised_ftr_reg(reg_to_encoding(&idr->reg_desc));
+}
+
 static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 			       const struct sys_reg_desc *rd,
 			       u64 val)
@@ -328,21 +354,31 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 }
 
 /* sys_reg_desc initialiser for known cpufeature ID registers */
-#define ID_SANITISED(name) {			\
-	SYS_DESC(SYS_##name),			\
-	.access	= access_id_reg,		\
-	.get_user = get_id_reg,			\
-	.set_user = set_id_reg,			\
-	.visibility = id_visibility,		\
+#define ID_SANITISED(name) {						\
+	.reg_desc = {							\
+		SYS_DESC(SYS_##name),					\
+		.access	= access_id_reg,				\
+		.get_user = get_id_reg,					\
+		.set_user = set_id_reg,					\
+		.visibility = id_visibility,				\
+	},								\
+	.ftr_bits = NULL,						\
+	.writable_mask = 0,						\
+	.read_kvm_sanitised_reg = general_read_kvm_sanitised_reg,	\
 }
 
 /* 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,	\
+#define AA32_ID_SANITISED(name) {					\
+	.reg_desc = {							\
+		SYS_DESC(SYS_##name),					\
+		.access	= access_id_reg,				\
+		.get_user = get_id_reg,					\
+		.set_user = set_id_reg,					\
+		.visibility = aa32_id_visibility,			\
+	},								\
+	.ftr_bits = NULL,						\
+	.writable_mask = 0,						\
+	.read_kvm_sanitised_reg = general_read_kvm_sanitised_reg,	\
 }
 
 /*
@@ -350,12 +386,17 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
  * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
  * (1 <= crm < 8, 0 <= Op2 < 8).
  */
-#define ID_UNALLOCATED(crm, op2) {			\
-	Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),	\
-	.access = access_id_reg,			\
-	.get_user = get_id_reg,				\
-	.set_user = set_id_reg,				\
-	.visibility = raz_visibility			\
+#define ID_UNALLOCATED(crm, op2) {				\
+	.reg_desc = {						\
+		Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),	\
+		.access = access_id_reg,			\
+		.get_user = get_id_reg,				\
+		.set_user = set_id_reg,				\
+		.visibility = raz_visibility			\
+	},							\
+	.ftr_bits = NULL,					\
+	.writable_mask = 0,					\
+	.read_kvm_sanitised_reg = NULL,				\
 }
 
 /*
@@ -363,15 +404,20 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
  * For now, these are exposed just like unallocated ID regs: they appear
  * RAZ for the guest.
  */
-#define ID_HIDDEN(name) {			\
-	SYS_DESC(SYS_##name),			\
-	.access = access_id_reg,		\
-	.get_user = get_id_reg,			\
-	.set_user = set_id_reg,			\
-	.visibility = raz_visibility,		\
+#define ID_HIDDEN(name) {				\
+	.reg_desc = {					\
+		SYS_DESC(SYS_##name),			\
+		.access = access_id_reg,		\
+		.get_user = get_id_reg,			\
+		.set_user = set_id_reg,			\
+		.visibility = raz_visibility,		\
+	},						\
+	.ftr_bits = NULL,				\
+	.writable_mask = 0,				\
+	.read_kvm_sanitised_reg = NULL,			\
 }
 
-static const struct sys_reg_desc id_reg_descs[] = {
+static const struct id_reg_desc id_reg_descs[KVM_ARM_ID_REG_NUM] = {
 	/*
 	 * ID regs: all ID_SANITISED() entries here must have corresponding
 	 * entries in arm64_ftr_regs[].
@@ -381,9 +427,13 @@ static const struct sys_reg_desc id_reg_descs[] = {
 	/* CRm=1 */
 	AA32_ID_SANITISED(ID_PFR0_EL1),
 	AA32_ID_SANITISED(ID_PFR1_EL1),
-	{ SYS_DESC(SYS_ID_DFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_dfr0_el1,
-	  .visibility = aa32_id_visibility, },
+	{ .reg_desc = {
+		SYS_DESC(SYS_ID_DFR0_EL1),
+		.access = access_id_reg,
+		.get_user = get_id_reg,
+		.set_user = set_id_dfr0_el1,
+		.visibility = aa32_id_visibility, },
+	},
 	ID_HIDDEN(ID_AFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR1_EL1),
@@ -412,8 +462,12 @@ static const struct sys_reg_desc id_reg_descs[] = {
 
 	/* AArch64 ID registers */
 	/* CRm=4 */
-	{ SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
+	{ .reg_desc = {
+		SYS_DESC(SYS_ID_AA64PFR0_EL1),
+		.access = access_id_reg,
+		.get_user = get_id_reg,
+		.set_user = set_id_aa64pfr0_el1, },
+	},
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4, 2),
 	ID_UNALLOCATED(4, 3),
@@ -423,8 +477,12 @@ static const struct sys_reg_desc id_reg_descs[] = {
 	ID_UNALLOCATED(4, 7),
 
 	/* CRm=5 */
-	{ SYS_DESC(SYS_ID_AA64DFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_aa64dfr0_el1, },
+	{ .reg_desc = {
+		SYS_DESC(SYS_ID_AA64DFR0_EL1),
+		.access = access_id_reg,
+		.get_user = get_id_reg,
+		.set_user = set_id_aa64dfr0_el1, },
+	},
 	ID_SANITISED(ID_AA64DFR1_EL1),
 	ID_UNALLOCATED(5, 2),
 	ID_UNALLOCATED(5, 3),
@@ -454,6 +512,17 @@ static const struct sys_reg_desc id_reg_descs[] = {
 	ID_UNALLOCATED(7, 7),
 };
 
+static const struct sys_reg_desc *id_params_to_desc(struct sys_reg_params *params)
+{
+	u32 id;
+
+	id = reg_to_encoding(params);
+	if (is_id_reg(id))
+		return &id_reg_descs[IDREG_IDX(id)].reg_desc;
+
+	return NULL;
+}
+
 /**
  * emulate_id_reg - Emulate a guest access to an AArch64 CPU ID feature register
  * @vcpu: The VCPU pointer
@@ -465,9 +534,9 @@ int emulate_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params)
 {
 	const struct sys_reg_desc *r;
 
-	r = find_reg(params, id_reg_descs, ARRAY_SIZE(id_reg_descs));
+	r = id_params_to_desc(params);
 
-	if (likely(r)) {
+	if (r) {
 		perform_access(vcpu, params, r);
 	} else {
 		print_sys_reg_msg(params,
@@ -481,32 +550,91 @@ int emulate_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params)
 
 int kvm_arm_get_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
-	return kvm_sys_reg_get_user(vcpu, reg,
-				    id_reg_descs, ARRAY_SIZE(id_reg_descs));
+	u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
+	const struct sys_reg_desc *r;
+	struct sys_reg_params params;
+	u64 val;
+	int ret;
+
+	if (!index_to_params(reg->id, &params))
+		return -ENOENT;
+
+	r = id_params_to_desc(&params);
+	if (!r)
+		return -ENOENT;
+
+	if (r->get_user) {
+		ret = (r->get_user)(vcpu, r, &val);
+	} else {
+		ret = 0;
+		val = IDREG(vcpu->kvm, reg_to_encoding(r));
+	}
+
+	if (!ret)
+		ret = put_user(val, uaddr);
+
+	return ret;
 }
 
 int kvm_arm_set_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
-	return kvm_sys_reg_set_user(vcpu, reg,
-				    id_reg_descs, ARRAY_SIZE(id_reg_descs));
+	u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
+	const struct sys_reg_desc *r;
+	struct sys_reg_params params;
+	u64 val;
+	int ret;
+
+	if (!index_to_params(reg->id, &params))
+		return -ENOENT;
+
+	r = id_params_to_desc(&params);
+	if (!r)
+		return -ENOENT;
+
+	if (get_user(val, uaddr))
+		return -EFAULT;
+
+	if (sysreg_user_write_ignore(vcpu, r))
+		return 0;
+
+	if (r->set_user) {
+		ret = (r->set_user)(vcpu, r, val);
+	} else {
+		WARN_ONCE(1, "ID register set_user callback is NULL\n");
+		ret = 0;
+	}
+
+	return ret;
 }
 
 bool kvm_arm_check_idreg_table(void)
 {
-	return check_sysreg_table(id_reg_descs, ARRAY_SIZE(id_reg_descs), false);
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++) {
+		const struct sys_reg_desc *r = &id_reg_descs[i].reg_desc;
+
+		if (!is_id_reg(reg_to_encoding(r))) {
+			kvm_err("id_reg table %pS entry %d not set correctly\n",
+				&id_reg_descs[i].reg_desc, i);
+			return false;
+		}
+	}
+
+	return true;
 }
 
 int kvm_arm_walk_id_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
 {
-	const struct sys_reg_desc *i2, *end2;
+	const struct id_reg_desc *i2, *end2;
 	unsigned int total = 0;
 	int err;
 
 	i2 = id_reg_descs;
 	end2 = id_reg_descs + ARRAY_SIZE(id_reg_descs);
 
-	while (i2 != end2) {
-		err = walk_one_sys_reg(vcpu, i2++, &uind, &total);
+	for (; i2 != end2; i2++) {
+		err = walk_one_sys_reg(vcpu, &(i2->reg_desc), &uind, &total);
 		if (err)
 			return err;
 	}
@@ -524,21 +652,12 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 	u64 val;
 
 	for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++) {
-		id = reg_to_encoding(&id_reg_descs[i]);
-		if (WARN_ON_ONCE(!is_id_reg(id)))
-			/* Shouldn't happen */
-			continue;
-
-		/*
-		 * Some hidden ID registers which are not in arm64_ftr_regs[]
-		 * would cause warnings from read_sanitised_ftr_reg().
-		 * Skip those ID registers to avoid the warnings.
-		 */
-		if (id_reg_descs[i].visibility == raz_visibility)
-			/* Hidden or reserved ID register */
-			continue;
-
-		val = read_sanitised_ftr_reg(id);
+		id = reg_to_encoding(&id_reg_descs[i].reg_desc);
+
+		val = 0;
+		if (id_reg_descs[i].read_kvm_sanitised_reg)
+			val = id_reg_descs[i].read_kvm_sanitised_reg(&id_reg_descs[i]);
+
 		IDREG(kvm, id) = val;
 	}
 
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 3af9f85aa976..1d7fb0acf154 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2541,7 +2541,7 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
  * Userspace API
  *****************************************************************************/
 
-static bool index_to_params(u64 id, struct sys_reg_params *params)
+bool index_to_params(u64 id, struct sys_reg_params *params)
 {
 	switch (id & KVM_REG_SIZE_MASK) {
 	case KVM_REG_SIZE_U64:
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index 326ca28c1117..15a0a1e2fe99 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -223,6 +223,7 @@ static inline bool is_id_reg(u32 id)
 
 void perform_access(struct kvm_vcpu *vcpu, struct sys_reg_params *params,
 		    const struct sys_reg_desc *r);
+bool index_to_params(u64 id, struct sys_reg_params *params);
 const struct sys_reg_desc *get_reg_by_id(u64 id,
 					 const struct sys_reg_desc table[],
 					 unsigned int num);
-- 
2.40.0.348.gf938b09366-goog




More information about the linux-arm-kernel mailing list