All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 0/5] Support writable CPU ID registers from userspace
@ 2023-06-02  0:51 ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

This patchset refactors/adds code to support writable per guest CPU ID feature
registers. Part of the code/ideas are from
https://lore.kernel.org/all/20220419065544.3616948-1-reijiw@google.com .
No functional change is intended in this patchset. With the new CPU ID feature
registers infrastructure, only writtings of ID_AA64PFR0_EL1.[CSV2|CSV3],
ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon are allowed as KVM does before.

Writable (Configurable) per guest CPU ID feature registers are useful for
creating/migrating guest on ARM CPUs with different kinds of features.

This patchset uses kvm->arch.config_lock from Oliver's lock inversion fixes at
https://lore.kernel.org/linux-arm-kernel/20230327164747.2466958-1-oliver.upton@linux.dev/

---

* v10 - v11
  - Rebased to v6.4-rc4.
  - Move one time searching of first ID reg from kvm_arm_init_id_regs() to
    kvm_sys_reg_table_init().
  - Move the lock/unlock of arch.config_lock and whether vm has ran to
    kvm_sys_reg_get_user()/kvm_sys_reg_set_user().
  - Addressed some other review comments from Marc and Oliver.

* v9 - v10
  - Rebased to v6.4-rc3
  - Addressed some review comments from v8/v9.

* v8 -> v9
  - Rebased to v6.4-rc2.
  - Don't create new file id_regs.c and don't move out id regs from
    sys_reg_descs array to reduce the changes.

* v7 -> v8
  - Move idregs table sanity check to kvm_sys_reg_table_init.
  - Only allow userspace writing before VM running.
  - No lock is hold for guest access to idregs.
  - Addressed some other comments from Reiji and Oliver.

* v6 -> v7
  - Rebased to v6.3-rc7.
  - Add helpers for idregs read/write.
  - Guard all idregs reads/writes.
  - Add code to fix features' safe value type which is different for KVM than
    for the host.

* v5 -> v6
  - Rebased to v6.3-rc5.
  - Reuse struct sys_reg_desc's reset() callback and field val for KVM.
    sanitisation function and writable mask instead of creating a new data
    structure for idregs.
  - Use get_arm64_ftr_reg() instead of exposing idregs ftr_bits array.

* v4 -> v5
  - Rebased to 2fad20ae05cb (kvmarm/next)
    Merge branch kvm-arm64/selftest/misc-6,4 into kvmarm-master/next
  - Use kvm->arch.config_lock to guard update to multiple VM scope idregs
    to avoid lock inversion
  - Add back IDREG() macro for idregs access
  - Refactor struct id_reg_desc by using existing infrastructure.
  - Addressed many other comments from Marc.

* v3 -> v4
  - Remove IDREG() macro for ID reg access, use simple array access instead
  - Rename kvm_arm_read_id_reg_with_encoding() to kvm_arm_read_id_reg()
  - Save perfmon value in ID_DFR0_EL1 instead of pmuver
  - Update perfmon in ID_DFR0_EL1 and pmuver in ID_AA64DFR0_EL1 atomically
  - Remove kvm_vcpu_has_pmu() in macro kvm_pmu_is_3p5()
  - Improve ID register sanity checking in kvm_arm_check_idreg_table()

* v2 -> v3
  - Rebased to 96a4627dbbd4 (kvmarm/next)
    Merge tag ' https://github.com/oupton/linux tags/kvmarm-6.3' from into kvmarm-master/next
  - Add id registere emulation entry point function emulate_id_reg
  - Fix consistency for ID_AA64DFR0_EL1.PMUVer and ID_DFR0_EL1.PerfMon
  - Improve the checking for id register table by ensuring that every entry has
    the correct id register encoding.
  - Addressed other comments from Reiji and Marc.

* v1 -> v2
  - Rebase to 7121a2e1d107 (kvmarm/next) Merge branch kvm-arm64/nv-prefix into kvmarm/next
  - Address writing issue for PMUVer

[1] https://lore.kernel.org/all/20230201025048.205820-1-jingzhangos@google.com
[2] https://lore.kernel.org/all/20230212215830.2975485-1-jingzhangos@google.com
[3] https://lore.kernel.org/all/20230228062246.1222387-1-jingzhangos@google.com
[4] https://lore.kernel.org/all/20230317050637.766317-1-jingzhangos@google.com
[5] https://lore.kernel.org/all/20230402183735.3011540-1-jingzhangos@google.com
[6] https://lore.kernel.org/all/20230404035344.4043856-1-jingzhangos@google.com
[7] https://lore.kernel.org/all/20230424234704.2571444-1-jingzhangos@google.com
[8] https://lore.kernel.org/all/20230503171618.2020461-1-jingzhangos@google.com
[9] https://lore.kernel.org/all/20230517061015.1915934-1-jingzhangos@google.com
[10] https://lore.kernel.org/all/20230522221835.957419-1-jingzhangos@google.com

---

Jing Zhang (5):
  KVM: arm64: Save ID registers' sanitized value per guest
  KVM: arm64: Use per guest ID register for ID_AA64PFR0_EL1.[CSV2|CSV3]
  KVM: arm64: Use per guest ID register for ID_AA64DFR0_EL1.PMUVer
  KVM: arm64: Reuse fields of sys_reg_desc for idreg
  KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3

 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/include/asm/kvm_host.h   |  34 +-
 arch/arm64/kernel/cpufeature.c      |   2 +-
 arch/arm64/kvm/arm.c                |  24 +-
 arch/arm64/kvm/sys_regs.c           | 467 ++++++++++++++++++++++------
 arch/arm64/kvm/sys_regs.h           |  22 +-
 include/kvm/arm_pmu.h               |   9 +-
 7 files changed, 419 insertions(+), 140 deletions(-)


base-commit: 7877cb91f1081754a1487c144d85dc0d2e2e7fc4
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v11 0/5] Support writable CPU ID registers from userspace
@ 2023-06-02  0:51 ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

This patchset refactors/adds code to support writable per guest CPU ID feature
registers. Part of the code/ideas are from
https://lore.kernel.org/all/20220419065544.3616948-1-reijiw@google.com .
No functional change is intended in this patchset. With the new CPU ID feature
registers infrastructure, only writtings of ID_AA64PFR0_EL1.[CSV2|CSV3],
ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon are allowed as KVM does before.

Writable (Configurable) per guest CPU ID feature registers are useful for
creating/migrating guest on ARM CPUs with different kinds of features.

This patchset uses kvm->arch.config_lock from Oliver's lock inversion fixes at
https://lore.kernel.org/linux-arm-kernel/20230327164747.2466958-1-oliver.upton@linux.dev/

---

* v10 - v11
  - Rebased to v6.4-rc4.
  - Move one time searching of first ID reg from kvm_arm_init_id_regs() to
    kvm_sys_reg_table_init().
  - Move the lock/unlock of arch.config_lock and whether vm has ran to
    kvm_sys_reg_get_user()/kvm_sys_reg_set_user().
  - Addressed some other review comments from Marc and Oliver.

* v9 - v10
  - Rebased to v6.4-rc3
  - Addressed some review comments from v8/v9.

* v8 -> v9
  - Rebased to v6.4-rc2.
  - Don't create new file id_regs.c and don't move out id regs from
    sys_reg_descs array to reduce the changes.

* v7 -> v8
  - Move idregs table sanity check to kvm_sys_reg_table_init.
  - Only allow userspace writing before VM running.
  - No lock is hold for guest access to idregs.
  - Addressed some other comments from Reiji and Oliver.

* v6 -> v7
  - Rebased to v6.3-rc7.
  - Add helpers for idregs read/write.
  - Guard all idregs reads/writes.
  - Add code to fix features' safe value type which is different for KVM than
    for the host.

* v5 -> v6
  - Rebased to v6.3-rc5.
  - Reuse struct sys_reg_desc's reset() callback and field val for KVM.
    sanitisation function and writable mask instead of creating a new data
    structure for idregs.
  - Use get_arm64_ftr_reg() instead of exposing idregs ftr_bits array.

* v4 -> v5
  - Rebased to 2fad20ae05cb (kvmarm/next)
    Merge branch kvm-arm64/selftest/misc-6,4 into kvmarm-master/next
  - Use kvm->arch.config_lock to guard update to multiple VM scope idregs
    to avoid lock inversion
  - Add back IDREG() macro for idregs access
  - Refactor struct id_reg_desc by using existing infrastructure.
  - Addressed many other comments from Marc.

* v3 -> v4
  - Remove IDREG() macro for ID reg access, use simple array access instead
  - Rename kvm_arm_read_id_reg_with_encoding() to kvm_arm_read_id_reg()
  - Save perfmon value in ID_DFR0_EL1 instead of pmuver
  - Update perfmon in ID_DFR0_EL1 and pmuver in ID_AA64DFR0_EL1 atomically
  - Remove kvm_vcpu_has_pmu() in macro kvm_pmu_is_3p5()
  - Improve ID register sanity checking in kvm_arm_check_idreg_table()

* v2 -> v3
  - Rebased to 96a4627dbbd4 (kvmarm/next)
    Merge tag ' https://github.com/oupton/linux tags/kvmarm-6.3' from into kvmarm-master/next
  - Add id registere emulation entry point function emulate_id_reg
  - Fix consistency for ID_AA64DFR0_EL1.PMUVer and ID_DFR0_EL1.PerfMon
  - Improve the checking for id register table by ensuring that every entry has
    the correct id register encoding.
  - Addressed other comments from Reiji and Marc.

* v1 -> v2
  - Rebase to 7121a2e1d107 (kvmarm/next) Merge branch kvm-arm64/nv-prefix into kvmarm/next
  - Address writing issue for PMUVer

[1] https://lore.kernel.org/all/20230201025048.205820-1-jingzhangos@google.com
[2] https://lore.kernel.org/all/20230212215830.2975485-1-jingzhangos@google.com
[3] https://lore.kernel.org/all/20230228062246.1222387-1-jingzhangos@google.com
[4] https://lore.kernel.org/all/20230317050637.766317-1-jingzhangos@google.com
[5] https://lore.kernel.org/all/20230402183735.3011540-1-jingzhangos@google.com
[6] https://lore.kernel.org/all/20230404035344.4043856-1-jingzhangos@google.com
[7] https://lore.kernel.org/all/20230424234704.2571444-1-jingzhangos@google.com
[8] https://lore.kernel.org/all/20230503171618.2020461-1-jingzhangos@google.com
[9] https://lore.kernel.org/all/20230517061015.1915934-1-jingzhangos@google.com
[10] https://lore.kernel.org/all/20230522221835.957419-1-jingzhangos@google.com

---

Jing Zhang (5):
  KVM: arm64: Save ID registers' sanitized value per guest
  KVM: arm64: Use per guest ID register for ID_AA64PFR0_EL1.[CSV2|CSV3]
  KVM: arm64: Use per guest ID register for ID_AA64DFR0_EL1.PMUVer
  KVM: arm64: Reuse fields of sys_reg_desc for idreg
  KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3

 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/include/asm/kvm_host.h   |  34 +-
 arch/arm64/kernel/cpufeature.c      |   2 +-
 arch/arm64/kvm/arm.c                |  24 +-
 arch/arm64/kvm/sys_regs.c           | 467 ++++++++++++++++++++++------
 arch/arm64/kvm/sys_regs.h           |  22 +-
 include/kvm/arm_pmu.h               |   9 +-
 7 files changed, 419 insertions(+), 140 deletions(-)


base-commit: 7877cb91f1081754a1487c144d85dc0d2e2e7fc4
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v11 1/5] KVM: arm64: Save ID registers' sanitized value per guest
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02  0:51   ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
and save ID registers' sanitized value in the array at KVM_CREATE_VM.
Use the saved ones when ID registers are read by the guest or
userspace (via KVM_GET_ONE_REG).

No functional change intended.

Co-developed-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 20 +++++++++
 arch/arm64/kvm/arm.c              |  1 +
 arch/arm64/kvm/sys_regs.c         | 72 +++++++++++++++++++++++++------
 arch/arm64/kvm/sys_regs.h         |  7 +++
 4 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7e7e19ef6993..069606170c82 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -178,6 +178,21 @@ struct kvm_smccc_features {
 	unsigned long vendor_hyp_bmap;
 };
 
+/*
+ * Emulated CPU ID registers per VM
+ * (Op0, Op1, CRn, CRm, Op2) of the ID registers to be saved in it
+ * is (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8.
+ *
+ * These emulated idregs are VM-wide, but accessed from the context of a vCPU.
+ * Atomic access to multiple idregs are guarded by kvm_arch.config_lock.
+ */
+#define IDREG_IDX(id)		(((sys_reg_CRm(id) - 1) << 3) | sys_reg_Op2(id))
+#define IDREG(kvm, id)		((kvm)->arch.idregs.regs[IDREG_IDX(id)])
+#define KVM_ARM_ID_REG_NUM	(IDREG_IDX(sys_reg(3, 0, 0, 7, 7)) + 1)
+struct kvm_idregs {
+	u64 regs[KVM_ARM_ID_REG_NUM];
+};
+
 typedef unsigned int pkvm_handle_t;
 
 struct kvm_protected_vm {
@@ -253,6 +268,9 @@ struct kvm_arch {
 	struct kvm_smccc_features smccc_feat;
 	struct maple_tree smccc_filter;
 
+	/* Emulated CPU ID registers */
+	struct kvm_idregs idregs;
+
 	/*
 	 * For an untrusted host VM, 'pkvm.handle' is used to lookup
 	 * the associated pKVM instance in the hypervisor.
@@ -1045,6 +1063,8 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
 				    struct kvm_arm_counter_offset *offset);
 
+void kvm_arm_init_id_regs(struct kvm *kvm);
+
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 14391826241c..774656a0718d 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -163,6 +163,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
 	set_default_spectre(kvm);
 	kvm_arm_init_hypercalls(kvm);
+	kvm_arm_init_id_regs(kvm);
 
 	/*
 	 * Initialise the default PMUver before there is a chance to
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 71b12094d613..40a9315015af 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -41,6 +41,7 @@
  * 64bit interface.
  */
 
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
 static bool read_from_write_only(struct kvm_vcpu *vcpu,
@@ -364,7 +365,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	u64 val = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
 	if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
@@ -1208,18 +1209,11 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
-/* 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)
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
-	u32 id = reg_to_encoding(r);
-	u64 val;
+	u64 val = IDREG(vcpu->kvm, encoding);
 
-	if (sysreg_visible_as_raz(vcpu, r))
-		return 0;
-
-	val = read_sanitised_ftr_reg(id);
-
-	switch (id) {
+	switch (encoding) {
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
@@ -1280,6 +1274,26 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r
 	return val;
 }
 
+/* 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)
+{
+	if (sysreg_visible_as_raz(vcpu, r))
+		return 0;
+
+	return kvm_arm_read_id_reg(vcpu, reg_to_encoding(r));
+}
+
+/*
+ * Return true if the register's (Op0, Op1, CRn, CRm, Op2) is
+ * (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8.
+ */
+static inline bool is_id_reg(u32 id)
+{
+	return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
+		sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
+		sys_reg_CRm(id) < 8);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
 				  const struct sys_reg_desc *r)
 {
@@ -2237,6 +2251,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	EL2_REG(SP_EL2, NULL, reset_unknown, 0),
 };
 
+static const struct sys_reg_desc *first_idreg;
+
 static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 			struct sys_reg_params *p,
 			const struct sys_reg_desc *r)
@@ -2244,8 +2260,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		return ignore_write(vcpu, p);
 	} else {
-		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+		u64 dfr = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
+		u64 pfr = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
 		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL1_EL3_SHIFT);
 
 		p->regval = ((((dfr >> ID_AA64DFR0_EL1_WRPs_SHIFT) & 0xf) << 28) |
@@ -3343,8 +3359,32 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/*
+ * Set the guest's ID registers with ID_SANITISED() to the host's sanitized value.
+ */
+void kvm_arm_init_id_regs(struct kvm *kvm)
+{
+	const struct sys_reg_desc *idreg = first_idreg;
+	u32 id = reg_to_encoding(idreg);
+
+	/* Initialize all idregs */
+	while (is_id_reg(id)) {
+		/*
+		 * 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 (idreg->visibility != raz_visibility)
+			IDREG(kvm, id) = read_sanitised_ftr_reg(id);
+
+		idreg++;
+		id = reg_to_encoding(idreg);
+	}
+}
+
 int __init kvm_sys_reg_table_init(void)
 {
+	struct sys_reg_params params;
 	bool valid = true;
 	unsigned int i;
 
@@ -3363,5 +3403,11 @@ int __init kvm_sys_reg_table_init(void)
 	for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
 		invariant_sys_regs[i].reset(NULL, &invariant_sys_regs[i]);
 
+	/* Find the first idreg (SYS_ID_PFR0_EL1) in sys_reg_descs. */
+	params = encoding_to_params(SYS_ID_PFR0_EL1);
+	first_idreg = find_reg(&params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
+	if (!first_idreg)
+		return -EINVAL;
+
 	return 0;
 }
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index 6b11f2cc7146..eba10de2e7ae 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -27,6 +27,13 @@ struct sys_reg_params {
 	bool	is_write;
 };
 
+#define encoding_to_params(reg)						\
+	((struct sys_reg_params){ .Op0 = sys_reg_Op0(reg),		\
+				  .Op1 = sys_reg_Op1(reg),		\
+				  .CRn = sys_reg_CRn(reg),		\
+				  .CRm = sys_reg_CRm(reg),		\
+				  .Op2 = sys_reg_Op2(reg) })
+
 #define esr_sys64_to_params(esr)                                               \
 	((struct sys_reg_params){ .Op0 = ((esr) >> 20) & 3,                    \
 				  .Op1 = ((esr) >> 14) & 0x7,                  \
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 1/5] KVM: arm64: Save ID registers' sanitized value per guest
@ 2023-06-02  0:51   ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
and save ID registers' sanitized value in the array at KVM_CREATE_VM.
Use the saved ones when ID registers are read by the guest or
userspace (via KVM_GET_ONE_REG).

No functional change intended.

Co-developed-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 20 +++++++++
 arch/arm64/kvm/arm.c              |  1 +
 arch/arm64/kvm/sys_regs.c         | 72 +++++++++++++++++++++++++------
 arch/arm64/kvm/sys_regs.h         |  7 +++
 4 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7e7e19ef6993..069606170c82 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -178,6 +178,21 @@ struct kvm_smccc_features {
 	unsigned long vendor_hyp_bmap;
 };
 
+/*
+ * Emulated CPU ID registers per VM
+ * (Op0, Op1, CRn, CRm, Op2) of the ID registers to be saved in it
+ * is (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8.
+ *
+ * These emulated idregs are VM-wide, but accessed from the context of a vCPU.
+ * Atomic access to multiple idregs are guarded by kvm_arch.config_lock.
+ */
+#define IDREG_IDX(id)		(((sys_reg_CRm(id) - 1) << 3) | sys_reg_Op2(id))
+#define IDREG(kvm, id)		((kvm)->arch.idregs.regs[IDREG_IDX(id)])
+#define KVM_ARM_ID_REG_NUM	(IDREG_IDX(sys_reg(3, 0, 0, 7, 7)) + 1)
+struct kvm_idregs {
+	u64 regs[KVM_ARM_ID_REG_NUM];
+};
+
 typedef unsigned int pkvm_handle_t;
 
 struct kvm_protected_vm {
@@ -253,6 +268,9 @@ struct kvm_arch {
 	struct kvm_smccc_features smccc_feat;
 	struct maple_tree smccc_filter;
 
+	/* Emulated CPU ID registers */
+	struct kvm_idregs idregs;
+
 	/*
 	 * For an untrusted host VM, 'pkvm.handle' is used to lookup
 	 * the associated pKVM instance in the hypervisor.
@@ -1045,6 +1063,8 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
 				    struct kvm_arm_counter_offset *offset);
 
+void kvm_arm_init_id_regs(struct kvm *kvm);
+
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 14391826241c..774656a0718d 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -163,6 +163,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
 	set_default_spectre(kvm);
 	kvm_arm_init_hypercalls(kvm);
+	kvm_arm_init_id_regs(kvm);
 
 	/*
 	 * Initialise the default PMUver before there is a chance to
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 71b12094d613..40a9315015af 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -41,6 +41,7 @@
  * 64bit interface.
  */
 
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
 static bool read_from_write_only(struct kvm_vcpu *vcpu,
@@ -364,7 +365,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	u64 val = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
 	if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
@@ -1208,18 +1209,11 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
-/* 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)
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
-	u32 id = reg_to_encoding(r);
-	u64 val;
+	u64 val = IDREG(vcpu->kvm, encoding);
 
-	if (sysreg_visible_as_raz(vcpu, r))
-		return 0;
-
-	val = read_sanitised_ftr_reg(id);
-
-	switch (id) {
+	switch (encoding) {
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
@@ -1280,6 +1274,26 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r
 	return val;
 }
 
+/* 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)
+{
+	if (sysreg_visible_as_raz(vcpu, r))
+		return 0;
+
+	return kvm_arm_read_id_reg(vcpu, reg_to_encoding(r));
+}
+
+/*
+ * Return true if the register's (Op0, Op1, CRn, CRm, Op2) is
+ * (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8.
+ */
+static inline bool is_id_reg(u32 id)
+{
+	return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
+		sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
+		sys_reg_CRm(id) < 8);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
 				  const struct sys_reg_desc *r)
 {
@@ -2237,6 +2251,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	EL2_REG(SP_EL2, NULL, reset_unknown, 0),
 };
 
+static const struct sys_reg_desc *first_idreg;
+
 static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 			struct sys_reg_params *p,
 			const struct sys_reg_desc *r)
@@ -2244,8 +2260,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		return ignore_write(vcpu, p);
 	} else {
-		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+		u64 dfr = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
+		u64 pfr = kvm_arm_read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
 		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL1_EL3_SHIFT);
 
 		p->regval = ((((dfr >> ID_AA64DFR0_EL1_WRPs_SHIFT) & 0xf) << 28) |
@@ -3343,8 +3359,32 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/*
+ * Set the guest's ID registers with ID_SANITISED() to the host's sanitized value.
+ */
+void kvm_arm_init_id_regs(struct kvm *kvm)
+{
+	const struct sys_reg_desc *idreg = first_idreg;
+	u32 id = reg_to_encoding(idreg);
+
+	/* Initialize all idregs */
+	while (is_id_reg(id)) {
+		/*
+		 * 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 (idreg->visibility != raz_visibility)
+			IDREG(kvm, id) = read_sanitised_ftr_reg(id);
+
+		idreg++;
+		id = reg_to_encoding(idreg);
+	}
+}
+
 int __init kvm_sys_reg_table_init(void)
 {
+	struct sys_reg_params params;
 	bool valid = true;
 	unsigned int i;
 
@@ -3363,5 +3403,11 @@ int __init kvm_sys_reg_table_init(void)
 	for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
 		invariant_sys_regs[i].reset(NULL, &invariant_sys_regs[i]);
 
+	/* Find the first idreg (SYS_ID_PFR0_EL1) in sys_reg_descs. */
+	params = encoding_to_params(SYS_ID_PFR0_EL1);
+	first_idreg = find_reg(&params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
+	if (!first_idreg)
+		return -EINVAL;
+
 	return 0;
 }
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index 6b11f2cc7146..eba10de2e7ae 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -27,6 +27,13 @@ struct sys_reg_params {
 	bool	is_write;
 };
 
+#define encoding_to_params(reg)						\
+	((struct sys_reg_params){ .Op0 = sys_reg_Op0(reg),		\
+				  .Op1 = sys_reg_Op1(reg),		\
+				  .CRn = sys_reg_CRn(reg),		\
+				  .CRm = sys_reg_CRm(reg),		\
+				  .Op2 = sys_reg_Op2(reg) })
+
 #define esr_sys64_to_params(esr)                                               \
 	((struct sys_reg_params){ .Op0 = ((esr) >> 20) & 3,                    \
 				  .Op1 = ((esr) >> 14) & 0x7,                  \
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 2/5] KVM: arm64: Use per guest ID register for ID_AA64PFR0_EL1.[CSV2|CSV3]
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02  0:51   ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

With per guest ID registers, ID_AA64PFR0_EL1.[CSV2|CSV3] settings from
userspace can be stored in its corresponding ID register.

The setting of CSV bits for protected VMs are removed according to the
discussion from Fuad below:
https://lore.kernel.org/all/CA+EHjTwXA9TprX4jeG+-D+c8v9XG+oFdU1o6TSkvVye145_OvA@mail.gmail.com

Besides the removal of CSV bits setting for protected VMs and using
kvm_arch.config_lock to guard VM-scope idreg accesses, no other
functional change intended.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 --
 arch/arm64/kvm/arm.c              | 17 ---------
 arch/arm64/kvm/sys_regs.c         | 57 +++++++++++++++++++++++++------
 3 files changed, 47 insertions(+), 29 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 069606170c82..8a2fde6c04c4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -257,8 +257,6 @@ struct kvm_arch {
 
 	cpumask_var_t supported_cpus;
 
-	u8 pfr0_csv2;
-	u8 pfr0_csv3;
 	struct {
 		u8 imp:4;
 		u8 unimp:4;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 774656a0718d..5114521ace60 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -102,22 +102,6 @@ static int kvm_arm_default_max_vcpus(void)
 	return vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
 }
 
-static void set_default_spectre(struct kvm *kvm)
-{
-	/*
-	 * The default is to expose CSV2 == 1 if the HW isn't affected.
-	 * Although this is a per-CPU feature, we make it global because
-	 * asymmetric systems are just a nuisance.
-	 *
-	 * Userspace can override this as long as it doesn't promise
-	 * the impossible.
-	 */
-	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
-		kvm->arch.pfr0_csv2 = 1;
-	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
-		kvm->arch.pfr0_csv3 = 1;
-}
-
 /**
  * kvm_arch_init_vm - initializes a VM data structure
  * @kvm:	pointer to the KVM struct
@@ -161,7 +145,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	/* The maximum number of VCPUs is limited by the host's GIC model */
 	kvm->max_vcpus = kvm_arm_default_max_vcpus();
 
-	set_default_spectre(kvm);
 	kvm_arm_init_hypercalls(kvm);
 	kvm_arm_init_id_regs(kvm);
 
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 40a9315015af..f043811a6725 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1218,10 +1218,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
 		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
 		if (kvm_vgic_global_state.type == VGIC_V3) {
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
 			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
@@ -1359,6 +1355,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 			       const struct sys_reg_desc *rd,
 			       u64 val)
 {
+	u64 new_val = val;
 	u8 csv2, csv3;
 
 	/*
@@ -1384,9 +1381,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	vcpu->kvm->arch.pfr0_csv2 = csv2;
-	vcpu->kvm->arch.pfr0_csv3 = csv3;
-
+	IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
 	return 0;
 }
 
@@ -1472,9 +1467,9 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 /*
  * cpufeature ID register user accessors
  *
- * For now, these registers are immutable for userspace, so no values
- * are stored, and for set_id_reg() we don't allow the effective value
- * to be changed.
+ * For now, only some registers or some part of registers are mutable for
+ * userspace. For those registers immutable for userspace, in set_id_reg()
+ * we don't allow the effective value to be changed.
  */
 static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 		      u64 *val)
@@ -3177,6 +3172,9 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 	if (!r || sysreg_hidden_user(vcpu, r))
 		return -ENOENT;
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_lock(&vcpu->kvm->arch.config_lock);
+
 	if (r->get_user) {
 		ret = (r->get_user)(vcpu, r, &val);
 	} else {
@@ -3184,6 +3182,9 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 		ret = 0;
 	}
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_unlock(&vcpu->kvm->arch.config_lock);
+
 	if (!ret)
 		ret = put_user(val, uaddr);
 
@@ -3221,9 +3222,20 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 	if (!r || sysreg_hidden_user(vcpu, r))
 		return -ENOENT;
 
+	/* Only allow userspace to change the idregs before VM running */
+	if (is_id_reg(reg_to_encoding(r)) && kvm_vm_has_ran_once(vcpu->kvm)) {
+		if (val == read_id_reg(vcpu, r))
+			return 0;
+		return -EBUSY;
+	}
+
 	if (sysreg_user_write_ignore(vcpu, r))
 		return 0;
 
+	/* ID regs are global to the VM and cannot be updated concurrently */
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_lock(&vcpu->kvm->arch.config_lock);
+
 	if (r->set_user) {
 		ret = (r->set_user)(vcpu, r, val);
 	} else {
@@ -3231,6 +3243,9 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 		ret = 0;
 	}
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_unlock(&vcpu->kvm->arch.config_lock);
+
 	return ret;
 }
 
@@ -3366,6 +3381,7 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 {
 	const struct sys_reg_desc *idreg = first_idreg;
 	u32 id = reg_to_encoding(idreg);
+	u64 val;
 
 	/* Initialize all idregs */
 	while (is_id_reg(id)) {
@@ -3380,6 +3396,27 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
+
+	/*
+	 * The default is to expose CSV2 == 1 if the HW isn't affected.
+	 * Although this is a per-CPU feature, we make it global because
+	 * asymmetric systems are just a nuisance.
+	 *
+	 * Userspace can override this as long as it doesn't promise
+	 * the impossible.
+	 */
+	val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
+
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
+	}
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
+	}
+
+	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 2/5] KVM: arm64: Use per guest ID register for ID_AA64PFR0_EL1.[CSV2|CSV3]
@ 2023-06-02  0:51   ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

With per guest ID registers, ID_AA64PFR0_EL1.[CSV2|CSV3] settings from
userspace can be stored in its corresponding ID register.

The setting of CSV bits for protected VMs are removed according to the
discussion from Fuad below:
https://lore.kernel.org/all/CA+EHjTwXA9TprX4jeG+-D+c8v9XG+oFdU1o6TSkvVye145_OvA@mail.gmail.com

Besides the removal of CSV bits setting for protected VMs and using
kvm_arch.config_lock to guard VM-scope idreg accesses, no other
functional change intended.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 --
 arch/arm64/kvm/arm.c              | 17 ---------
 arch/arm64/kvm/sys_regs.c         | 57 +++++++++++++++++++++++++------
 3 files changed, 47 insertions(+), 29 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 069606170c82..8a2fde6c04c4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -257,8 +257,6 @@ struct kvm_arch {
 
 	cpumask_var_t supported_cpus;
 
-	u8 pfr0_csv2;
-	u8 pfr0_csv3;
 	struct {
 		u8 imp:4;
 		u8 unimp:4;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 774656a0718d..5114521ace60 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -102,22 +102,6 @@ static int kvm_arm_default_max_vcpus(void)
 	return vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
 }
 
-static void set_default_spectre(struct kvm *kvm)
-{
-	/*
-	 * The default is to expose CSV2 == 1 if the HW isn't affected.
-	 * Although this is a per-CPU feature, we make it global because
-	 * asymmetric systems are just a nuisance.
-	 *
-	 * Userspace can override this as long as it doesn't promise
-	 * the impossible.
-	 */
-	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
-		kvm->arch.pfr0_csv2 = 1;
-	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
-		kvm->arch.pfr0_csv3 = 1;
-}
-
 /**
  * kvm_arch_init_vm - initializes a VM data structure
  * @kvm:	pointer to the KVM struct
@@ -161,7 +145,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	/* The maximum number of VCPUs is limited by the host's GIC model */
 	kvm->max_vcpus = kvm_arm_default_max_vcpus();
 
-	set_default_spectre(kvm);
 	kvm_arm_init_hypercalls(kvm);
 	kvm_arm_init_id_regs(kvm);
 
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 40a9315015af..f043811a6725 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1218,10 +1218,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
 		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
 		if (kvm_vgic_global_state.type == VGIC_V3) {
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
 			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
@@ -1359,6 +1355,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 			       const struct sys_reg_desc *rd,
 			       u64 val)
 {
+	u64 new_val = val;
 	u8 csv2, csv3;
 
 	/*
@@ -1384,9 +1381,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	vcpu->kvm->arch.pfr0_csv2 = csv2;
-	vcpu->kvm->arch.pfr0_csv3 = csv3;
-
+	IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
 	return 0;
 }
 
@@ -1472,9 +1467,9 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 /*
  * cpufeature ID register user accessors
  *
- * For now, these registers are immutable for userspace, so no values
- * are stored, and for set_id_reg() we don't allow the effective value
- * to be changed.
+ * For now, only some registers or some part of registers are mutable for
+ * userspace. For those registers immutable for userspace, in set_id_reg()
+ * we don't allow the effective value to be changed.
  */
 static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 		      u64 *val)
@@ -3177,6 +3172,9 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 	if (!r || sysreg_hidden_user(vcpu, r))
 		return -ENOENT;
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_lock(&vcpu->kvm->arch.config_lock);
+
 	if (r->get_user) {
 		ret = (r->get_user)(vcpu, r, &val);
 	} else {
@@ -3184,6 +3182,9 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 		ret = 0;
 	}
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_unlock(&vcpu->kvm->arch.config_lock);
+
 	if (!ret)
 		ret = put_user(val, uaddr);
 
@@ -3221,9 +3222,20 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 	if (!r || sysreg_hidden_user(vcpu, r))
 		return -ENOENT;
 
+	/* Only allow userspace to change the idregs before VM running */
+	if (is_id_reg(reg_to_encoding(r)) && kvm_vm_has_ran_once(vcpu->kvm)) {
+		if (val == read_id_reg(vcpu, r))
+			return 0;
+		return -EBUSY;
+	}
+
 	if (sysreg_user_write_ignore(vcpu, r))
 		return 0;
 
+	/* ID regs are global to the VM and cannot be updated concurrently */
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_lock(&vcpu->kvm->arch.config_lock);
+
 	if (r->set_user) {
 		ret = (r->set_user)(vcpu, r, val);
 	} else {
@@ -3231,6 +3243,9 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 		ret = 0;
 	}
 
+	if (is_id_reg(reg_to_encoding(r)))
+		mutex_unlock(&vcpu->kvm->arch.config_lock);
+
 	return ret;
 }
 
@@ -3366,6 +3381,7 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 {
 	const struct sys_reg_desc *idreg = first_idreg;
 	u32 id = reg_to_encoding(idreg);
+	u64 val;
 
 	/* Initialize all idregs */
 	while (is_id_reg(id)) {
@@ -3380,6 +3396,27 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
+
+	/*
+	 * The default is to expose CSV2 == 1 if the HW isn't affected.
+	 * Although this is a per-CPU feature, we make it global because
+	 * asymmetric systems are just a nuisance.
+	 *
+	 * Userspace can override this as long as it doesn't promise
+	 * the impossible.
+	 */
+	val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
+
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
+	}
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
+	}
+
+	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 3/5] KVM: arm64: Use per guest ID register for ID_AA64DFR0_EL1.PMUVer
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02  0:51   ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

With per guest ID registers, PMUver settings from userspace
can be stored in its corresponding ID register.

No functional change intended.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 12 ++++----
 arch/arm64/kvm/arm.c              |  6 ----
 arch/arm64/kvm/sys_regs.c         | 50 +++++++++++++++++++++++--------
 include/kvm/arm_pmu.h             |  9 ++++--
 4 files changed, 52 insertions(+), 25 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 8a2fde6c04c4..7b0f43373dbe 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -246,6 +246,13 @@ struct kvm_arch {
 #define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
 	/* SMCCC filter initialized for the VM */
 #define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
+	/*
+	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
+	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
+	 * userspace for VCPUs without PMU.
+	 */
+#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
+
 	unsigned long flags;
 
 	/*
@@ -257,11 +264,6 @@ struct kvm_arch {
 
 	cpumask_var_t supported_cpus;
 
-	struct {
-		u8 imp:4;
-		u8 unimp:4;
-	} dfr0_pmuver;
-
 	/* Hypercall features firmware registers' descriptor */
 	struct kvm_smccc_features smccc_feat;
 	struct maple_tree smccc_filter;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5114521ace60..ca18c09ccf82 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -148,12 +148,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	kvm_arm_init_hypercalls(kvm);
 	kvm_arm_init_id_regs(kvm);
 
-	/*
-	 * Initialise the default PMUver before there is a chance to
-	 * create an actual PMU.
-	 */
-	kvm->arch.dfr0_pmuver.imp = kvm_arm_pmu_get_pmuver_limit();
-
 	return 0;
 
 err_free_cpumask:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index f043811a6725..0179df50fcf5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1178,9 +1178,12 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
 {
 	if (kvm_vcpu_has_pmu(vcpu))
-		return vcpu->kvm->arch.dfr0_pmuver.imp;
+		return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+				 IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
+	else if (test_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags))
+		return ID_AA64DFR0_EL1_PMUVer_IMP_DEF;
 
-	return vcpu->kvm->arch.dfr0_pmuver.unimp;
+	return 0;
 }
 
 static u8 perfmon_to_pmuver(u8 perfmon)
@@ -1209,6 +1212,26 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
+static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
+{
+	u64 val;
+
+	if (valid_pmu) {
+		val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
+		val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
+		val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
+		IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
+
+		val = IDREG(vcpu->kvm, SYS_ID_DFR0_EL1);
+		val &= ~ID_DFR0_EL1_PerfMon_MASK;
+		val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, pmuver_to_perfmon(pmuver));
+		IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
+	} else {
+		assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
+			   pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
+	}
+}
+
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
 	u64 val = IDREG(vcpu->kvm, encoding);
@@ -1416,11 +1439,7 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	if (valid_pmu)
-		vcpu->kvm->arch.dfr0_pmuver.imp = pmuver;
-	else
-		vcpu->kvm->arch.dfr0_pmuver.unimp = pmuver;
-
+	pmuver_update(vcpu, pmuver, valid_pmu);
 	return 0;
 }
 
@@ -1456,11 +1475,7 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	if (valid_pmu)
-		vcpu->kvm->arch.dfr0_pmuver.imp = perfmon_to_pmuver(perfmon);
-	else
-		vcpu->kvm->arch.dfr0_pmuver.unimp = perfmon_to_pmuver(perfmon);
-
+	pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
 	return 0;
 }
 
@@ -3417,6 +3432,17 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 	}
 
 	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
+
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+			  kvm_arm_pmu_get_pmuver_limit());
+
+	IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 1a6a695ca67a..5300d91b1e9b 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -92,8 +92,13 @@ void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
 /*
  * Evaluates as true when emulating PMUv3p5, and false otherwise.
  */
-#define kvm_pmu_is_3p5(vcpu)						\
-	(vcpu->kvm->arch.dfr0_pmuver.imp >= ID_AA64DFR0_EL1_PMUVer_V3P5)
+#define kvm_pmu_is_3p5(vcpu) ({							\
+		u64 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);		\
+		u8 v;								\
+										\
+		v = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), val);	\
+		v >= ID_AA64DFR0_EL1_PMUVer_V3P5;				\
+})
 
 u8 kvm_arm_pmu_get_pmuver_limit(void);
 
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 3/5] KVM: arm64: Use per guest ID register for ID_AA64DFR0_EL1.PMUVer
@ 2023-06-02  0:51   ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

With per guest ID registers, PMUver settings from userspace
can be stored in its corresponding ID register.

No functional change intended.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 12 ++++----
 arch/arm64/kvm/arm.c              |  6 ----
 arch/arm64/kvm/sys_regs.c         | 50 +++++++++++++++++++++++--------
 include/kvm/arm_pmu.h             |  9 ++++--
 4 files changed, 52 insertions(+), 25 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 8a2fde6c04c4..7b0f43373dbe 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -246,6 +246,13 @@ struct kvm_arch {
 #define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
 	/* SMCCC filter initialized for the VM */
 #define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
+	/*
+	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
+	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
+	 * userspace for VCPUs without PMU.
+	 */
+#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
+
 	unsigned long flags;
 
 	/*
@@ -257,11 +264,6 @@ struct kvm_arch {
 
 	cpumask_var_t supported_cpus;
 
-	struct {
-		u8 imp:4;
-		u8 unimp:4;
-	} dfr0_pmuver;
-
 	/* Hypercall features firmware registers' descriptor */
 	struct kvm_smccc_features smccc_feat;
 	struct maple_tree smccc_filter;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5114521ace60..ca18c09ccf82 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -148,12 +148,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	kvm_arm_init_hypercalls(kvm);
 	kvm_arm_init_id_regs(kvm);
 
-	/*
-	 * Initialise the default PMUver before there is a chance to
-	 * create an actual PMU.
-	 */
-	kvm->arch.dfr0_pmuver.imp = kvm_arm_pmu_get_pmuver_limit();
-
 	return 0;
 
 err_free_cpumask:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index f043811a6725..0179df50fcf5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1178,9 +1178,12 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
 {
 	if (kvm_vcpu_has_pmu(vcpu))
-		return vcpu->kvm->arch.dfr0_pmuver.imp;
+		return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+				 IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
+	else if (test_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags))
+		return ID_AA64DFR0_EL1_PMUVer_IMP_DEF;
 
-	return vcpu->kvm->arch.dfr0_pmuver.unimp;
+	return 0;
 }
 
 static u8 perfmon_to_pmuver(u8 perfmon)
@@ -1209,6 +1212,26 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
+static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
+{
+	u64 val;
+
+	if (valid_pmu) {
+		val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
+		val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
+		val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
+		IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
+
+		val = IDREG(vcpu->kvm, SYS_ID_DFR0_EL1);
+		val &= ~ID_DFR0_EL1_PerfMon_MASK;
+		val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, pmuver_to_perfmon(pmuver));
+		IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
+	} else {
+		assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
+			   pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
+	}
+}
+
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
 	u64 val = IDREG(vcpu->kvm, encoding);
@@ -1416,11 +1439,7 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	if (valid_pmu)
-		vcpu->kvm->arch.dfr0_pmuver.imp = pmuver;
-	else
-		vcpu->kvm->arch.dfr0_pmuver.unimp = pmuver;
-
+	pmuver_update(vcpu, pmuver, valid_pmu);
 	return 0;
 }
 
@@ -1456,11 +1475,7 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	if (val)
 		return -EINVAL;
 
-	if (valid_pmu)
-		vcpu->kvm->arch.dfr0_pmuver.imp = perfmon_to_pmuver(perfmon);
-	else
-		vcpu->kvm->arch.dfr0_pmuver.unimp = perfmon_to_pmuver(perfmon);
-
+	pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
 	return 0;
 }
 
@@ -3417,6 +3432,17 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 	}
 
 	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
+
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+			  kvm_arm_pmu_get_pmuver_limit());
+
+	IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 1a6a695ca67a..5300d91b1e9b 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -92,8 +92,13 @@ void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
 /*
  * Evaluates as true when emulating PMUv3p5, and false otherwise.
  */
-#define kvm_pmu_is_3p5(vcpu)						\
-	(vcpu->kvm->arch.dfr0_pmuver.imp >= ID_AA64DFR0_EL1_PMUVer_V3P5)
+#define kvm_pmu_is_3p5(vcpu) ({							\
+		u64 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);		\
+		u8 v;								\
+										\
+		v = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), val);	\
+		v >= ID_AA64DFR0_EL1_PMUVer_V3P5;				\
+})
 
 u8 kvm_arm_pmu_get_pmuver_limit(void);
 
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 4/5] KVM: arm64: Reuse fields of sys_reg_desc for idreg
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02  0:51   ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

sys_reg_desc::{reset, val} are presently unused for ID register
descriptors. Repurpose these fields to support user-configurable ID
registers.
Use the ::reset() function pointer to return the sanitised value of a
given ID register, optionally with KVM-specific feature sanitisation.
Additionally, keep a mask of writable register fields in ::val.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/kvm/sys_regs.c | 101 +++++++++++++++++++++++++++-----------
 arch/arm64/kvm/sys_regs.h |  15 ++++--
 2 files changed, 82 insertions(+), 34 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 0179df50fcf5..1a534e0fc4ca 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -541,10 +541,11 @@ static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_bvr(struct kvm_vcpu *vcpu,
+static u64 reset_bvr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_bcr(struct kvm_vcpu *vcpu,
@@ -577,10 +578,11 @@ static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_bcr(struct kvm_vcpu *vcpu,
+static u64 reset_bcr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_wvr(struct kvm_vcpu *vcpu,
@@ -614,10 +616,11 @@ static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_wvr(struct kvm_vcpu *vcpu,
+static u64 reset_wvr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_wcr(struct kvm_vcpu *vcpu,
@@ -650,25 +653,28 @@ static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_wcr(struct kvm_vcpu *vcpu,
+static u64 reset_wcr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
-static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 amair = read_sysreg(amair_el1);
 	vcpu_write_sys_reg(vcpu, amair, AMAIR_EL1);
+	return amair;
 }
 
-static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 actlr = read_sysreg(actlr_el1);
 	vcpu_write_sys_reg(vcpu, actlr, ACTLR_EL1);
+	return actlr;
 }
 
-static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 mpidr;
 
@@ -682,7 +688,10 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 	mpidr = (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0);
 	mpidr |= ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1);
 	mpidr |= ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2);
-	vcpu_write_sys_reg(vcpu, (1ULL << 31) | mpidr, MPIDR_EL1);
+	mpidr |= (1ULL << 31);
+	vcpu_write_sys_reg(vcpu, mpidr, MPIDR_EL1);
+
+	return mpidr;
 }
 
 static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
@@ -694,13 +703,13 @@ static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static void reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 n, mask = BIT(ARMV8_PMU_CYCLE_IDX);
 
 	/* No PMU available, any PMU reg may UNDEF... */
 	if (!kvm_arm_support_pmu_v3())
-		return;
+		return 0;
 
 	n = read_sysreg(pmcr_el0) >> ARMV8_PMU_PMCR_N_SHIFT;
 	n &= ARMV8_PMU_PMCR_N_MASK;
@@ -709,33 +718,41 @@ static void reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= mask;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= GENMASK(31, 0);
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_EVTYPE_MASK;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_COUNTER_MASK;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 pmcr;
 
 	/* No PMU available, PMCR_EL0 may UNDEF... */
 	if (!kvm_arm_support_pmu_v3())
-		return;
+		return 0;
 
 	/* Only preserve PMCR_EL0.N, and reset the rest to 0 */
 	pmcr = read_sysreg(pmcr_el0) & (ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
@@ -743,6 +760,8 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 		pmcr |= ARMV8_PMU_PMCR_LC;
 
 	__vcpu_sys_reg(vcpu, r->reg) = pmcr;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags)
@@ -1232,6 +1251,11 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
 	}
 }
 
+static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
+{
+	return read_sanitised_ftr_reg(reg_to_encoding(rd));
+}
+
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
 	u64 val = IDREG(vcpu->kvm, encoding);
@@ -1540,7 +1564,7 @@ static bool access_clidr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
  * Fabricate a CLIDR_EL1 value instead of using the real value, which can vary
  * by the physical CPU which the vcpu currently resides in.
  */
-static void reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 ctr_el0 = read_sanitised_ftr_reg(SYS_CTR_EL0);
 	u64 clidr;
@@ -1588,6 +1612,8 @@ static void reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 		clidr |= 2 << CLIDR_TTYPE_SHIFT(loc);
 
 	__vcpu_sys_reg(vcpu, r->reg) = clidr;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static int set_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -1687,6 +1713,17 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.visibility = elx2_visibility,		\
 }
 
+/*
+ * Since reset() callback and field val are not used for idregs, they will be
+ * used for specific purposes for idregs.
+ * The reset() would return KVM sanitised register value. The value would be the
+ * same as the host kernel sanitised value if there is no KVM sanitisation.
+ * The val would be used as a mask indicating writable fields for the idreg.
+ * 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.
+ */
+
 /* sys_reg_desc initialiser for known cpufeature ID registers */
 #define ID_SANITISED(name) {			\
 	SYS_DESC(SYS_##name),			\
@@ -1694,6 +1731,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = id_visibility,		\
+	.reset = general_read_kvm_sanitised_reg,\
+	.val = 0,				\
 }
 
 /* sys_reg_desc initialiser for known cpufeature ID registers */
@@ -1703,6 +1742,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = aa32_id_visibility,	\
+	.reset = general_read_kvm_sanitised_reg,\
+	.val = 0,				\
 }
 
 /*
@@ -1715,7 +1756,9 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.access = access_id_reg,			\
 	.get_user = get_id_reg,				\
 	.set_user = set_id_reg,				\
-	.visibility = raz_visibility			\
+	.visibility = raz_visibility,			\
+	.reset = NULL,					\
+	.val = 0,					\
 }
 
 /*
@@ -1729,6 +1772,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = raz_visibility,		\
+	.reset = NULL,				\
+	.val = 0,				\
 }
 
 static bool access_sp_el1(struct kvm_vcpu *vcpu,
@@ -3067,19 +3112,21 @@ id_to_sys_reg_desc(struct kvm_vcpu *vcpu, u64 id,
  */
 
 #define FUNCTION_INVARIANT(reg)						\
-	static void get_##reg(struct kvm_vcpu *v,			\
+	static u64 get_##reg(struct kvm_vcpu *v,			\
 			      const struct sys_reg_desc *r)		\
 	{								\
 		((struct sys_reg_desc *)r)->val = read_sysreg(reg);	\
+		return ((struct sys_reg_desc *)r)->val;			\
 	}
 
 FUNCTION_INVARIANT(midr_el1)
 FUNCTION_INVARIANT(revidr_el1)
 FUNCTION_INVARIANT(aidr_el1)
 
-static void get_ctr_el0(struct kvm_vcpu *v, const struct sys_reg_desc *r)
+static u64 get_ctr_el0(struct kvm_vcpu *v, const struct sys_reg_desc *r)
 {
 	((struct sys_reg_desc *)r)->val = read_sanitised_ftr_reg(SYS_CTR_EL0);
+	return ((struct sys_reg_desc *)r)->val;
 }
 
 /* ->val is filled in by kvm_sys_reg_table_init() */
@@ -3389,9 +3436,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
-/*
- * Set the guest's ID registers with ID_SANITISED() to the host's sanitized value.
- */
+/* Initialize the guest's ID registers with KVM sanitised values. */
 void kvm_arm_init_id_regs(struct kvm *kvm)
 {
 	const struct sys_reg_desc *idreg = first_idreg;
@@ -3400,13 +3445,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 
 	/* Initialize all idregs */
 	while (is_id_reg(id)) {
-		/*
-		 * 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 (idreg->visibility != raz_visibility)
-			IDREG(kvm, id) = read_sanitised_ftr_reg(id);
+		val = 0;
+		/* Read KVM sanitised register value if available */
+		if (idreg->reset)
+			val = idreg->reset(NULL, idreg);
+		IDREG(kvm, id) = val;
 
 		idreg++;
 		id = reg_to_encoding(idreg);
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index eba10de2e7ae..c65c129b3500 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -71,13 +71,16 @@ struct sys_reg_desc {
 		       struct sys_reg_params *,
 		       const struct sys_reg_desc *);
 
-	/* Initialization for vcpu. */
-	void (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *);
+	/*
+	 * Initialization for vcpu. Return initialized value, or KVM
+	 * sanitized value for ID registers.
+	 */
+	u64 (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *);
 
 	/* Index into sys_reg[], or 0 if we don't need to save it. */
 	int reg;
 
-	/* Value (usually reset value) */
+	/* Value (usually reset value), or write mask for idregs */
 	u64 val;
 
 	/* Custom get/set_user functions, fallback to generic if NULL */
@@ -130,19 +133,21 @@ static inline bool read_zero(struct kvm_vcpu *vcpu,
 }
 
 /* Reset functions */
-static inline void reset_unknown(struct kvm_vcpu *vcpu,
+static inline u64 reset_unknown(struct kvm_vcpu *vcpu,
 				 const struct sys_reg_desc *r)
 {
 	BUG_ON(!r->reg);
 	BUG_ON(r->reg >= NR_SYS_REGS);
 	__vcpu_sys_reg(vcpu, r->reg) = 0x1de7ec7edbadc0deULL;
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static inline u64 reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	BUG_ON(!r->reg);
 	BUG_ON(r->reg >= NR_SYS_REGS);
 	__vcpu_sys_reg(vcpu, r->reg) = r->val;
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static inline unsigned int sysreg_visibility(const struct kvm_vcpu *vcpu,
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 4/5] KVM: arm64: Reuse fields of sys_reg_desc for idreg
@ 2023-06-02  0:51   ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

sys_reg_desc::{reset, val} are presently unused for ID register
descriptors. Repurpose these fields to support user-configurable ID
registers.
Use the ::reset() function pointer to return the sanitised value of a
given ID register, optionally with KVM-specific feature sanitisation.
Additionally, keep a mask of writable register fields in ::val.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/kvm/sys_regs.c | 101 +++++++++++++++++++++++++++-----------
 arch/arm64/kvm/sys_regs.h |  15 ++++--
 2 files changed, 82 insertions(+), 34 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 0179df50fcf5..1a534e0fc4ca 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -541,10 +541,11 @@ static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_bvr(struct kvm_vcpu *vcpu,
+static u64 reset_bvr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_bcr(struct kvm_vcpu *vcpu,
@@ -577,10 +578,11 @@ static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_bcr(struct kvm_vcpu *vcpu,
+static u64 reset_bcr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_wvr(struct kvm_vcpu *vcpu,
@@ -614,10 +616,11 @@ static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_wvr(struct kvm_vcpu *vcpu,
+static u64 reset_wvr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
 static bool trap_wcr(struct kvm_vcpu *vcpu,
@@ -650,25 +653,28 @@ static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 	return 0;
 }
 
-static void reset_wcr(struct kvm_vcpu *vcpu,
+static u64 reset_wcr(struct kvm_vcpu *vcpu,
 		      const struct sys_reg_desc *rd)
 {
 	vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm] = rd->val;
+	return rd->val;
 }
 
-static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 amair = read_sysreg(amair_el1);
 	vcpu_write_sys_reg(vcpu, amair, AMAIR_EL1);
+	return amair;
 }
 
-static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 actlr = read_sysreg(actlr_el1);
 	vcpu_write_sys_reg(vcpu, actlr, ACTLR_EL1);
+	return actlr;
 }
 
-static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 mpidr;
 
@@ -682,7 +688,10 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 	mpidr = (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0);
 	mpidr |= ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1);
 	mpidr |= ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2);
-	vcpu_write_sys_reg(vcpu, (1ULL << 31) | mpidr, MPIDR_EL1);
+	mpidr |= (1ULL << 31);
+	vcpu_write_sys_reg(vcpu, mpidr, MPIDR_EL1);
+
+	return mpidr;
 }
 
 static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
@@ -694,13 +703,13 @@ static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static void reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 n, mask = BIT(ARMV8_PMU_CYCLE_IDX);
 
 	/* No PMU available, any PMU reg may UNDEF... */
 	if (!kvm_arm_support_pmu_v3())
-		return;
+		return 0;
 
 	n = read_sysreg(pmcr_el0) >> ARMV8_PMU_PMCR_N_SHIFT;
 	n &= ARMV8_PMU_PMCR_N_MASK;
@@ -709,33 +718,41 @@ static void reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= mask;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= GENMASK(31, 0);
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_EVTYPE_MASK;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	reset_unknown(vcpu, r);
 	__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_COUNTER_MASK;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 pmcr;
 
 	/* No PMU available, PMCR_EL0 may UNDEF... */
 	if (!kvm_arm_support_pmu_v3())
-		return;
+		return 0;
 
 	/* Only preserve PMCR_EL0.N, and reset the rest to 0 */
 	pmcr = read_sysreg(pmcr_el0) & (ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
@@ -743,6 +760,8 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 		pmcr |= ARMV8_PMU_PMCR_LC;
 
 	__vcpu_sys_reg(vcpu, r->reg) = pmcr;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags)
@@ -1232,6 +1251,11 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
 	}
 }
 
+static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
+{
+	return read_sanitised_ftr_reg(reg_to_encoding(rd));
+}
+
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 {
 	u64 val = IDREG(vcpu->kvm, encoding);
@@ -1540,7 +1564,7 @@ static bool access_clidr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
  * Fabricate a CLIDR_EL1 value instead of using the real value, which can vary
  * by the physical CPU which the vcpu currently resides in.
  */
-static void reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static u64 reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	u64 ctr_el0 = read_sanitised_ftr_reg(SYS_CTR_EL0);
 	u64 clidr;
@@ -1588,6 +1612,8 @@ static void reset_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 		clidr |= 2 << CLIDR_TTYPE_SHIFT(loc);
 
 	__vcpu_sys_reg(vcpu, r->reg) = clidr;
+
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static int set_clidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -1687,6 +1713,17 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.visibility = elx2_visibility,		\
 }
 
+/*
+ * Since reset() callback and field val are not used for idregs, they will be
+ * used for specific purposes for idregs.
+ * The reset() would return KVM sanitised register value. The value would be the
+ * same as the host kernel sanitised value if there is no KVM sanitisation.
+ * The val would be used as a mask indicating writable fields for the idreg.
+ * 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.
+ */
+
 /* sys_reg_desc initialiser for known cpufeature ID registers */
 #define ID_SANITISED(name) {			\
 	SYS_DESC(SYS_##name),			\
@@ -1694,6 +1731,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = id_visibility,		\
+	.reset = general_read_kvm_sanitised_reg,\
+	.val = 0,				\
 }
 
 /* sys_reg_desc initialiser for known cpufeature ID registers */
@@ -1703,6 +1742,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = aa32_id_visibility,	\
+	.reset = general_read_kvm_sanitised_reg,\
+	.val = 0,				\
 }
 
 /*
@@ -1715,7 +1756,9 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.access = access_id_reg,			\
 	.get_user = get_id_reg,				\
 	.set_user = set_id_reg,				\
-	.visibility = raz_visibility			\
+	.visibility = raz_visibility,			\
+	.reset = NULL,					\
+	.val = 0,					\
 }
 
 /*
@@ -1729,6 +1772,8 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
 	.get_user = get_id_reg,			\
 	.set_user = set_id_reg,			\
 	.visibility = raz_visibility,		\
+	.reset = NULL,				\
+	.val = 0,				\
 }
 
 static bool access_sp_el1(struct kvm_vcpu *vcpu,
@@ -3067,19 +3112,21 @@ id_to_sys_reg_desc(struct kvm_vcpu *vcpu, u64 id,
  */
 
 #define FUNCTION_INVARIANT(reg)						\
-	static void get_##reg(struct kvm_vcpu *v,			\
+	static u64 get_##reg(struct kvm_vcpu *v,			\
 			      const struct sys_reg_desc *r)		\
 	{								\
 		((struct sys_reg_desc *)r)->val = read_sysreg(reg);	\
+		return ((struct sys_reg_desc *)r)->val;			\
 	}
 
 FUNCTION_INVARIANT(midr_el1)
 FUNCTION_INVARIANT(revidr_el1)
 FUNCTION_INVARIANT(aidr_el1)
 
-static void get_ctr_el0(struct kvm_vcpu *v, const struct sys_reg_desc *r)
+static u64 get_ctr_el0(struct kvm_vcpu *v, const struct sys_reg_desc *r)
 {
 	((struct sys_reg_desc *)r)->val = read_sanitised_ftr_reg(SYS_CTR_EL0);
+	return ((struct sys_reg_desc *)r)->val;
 }
 
 /* ->val is filled in by kvm_sys_reg_table_init() */
@@ -3389,9 +3436,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
-/*
- * Set the guest's ID registers with ID_SANITISED() to the host's sanitized value.
- */
+/* Initialize the guest's ID registers with KVM sanitised values. */
 void kvm_arm_init_id_regs(struct kvm *kvm)
 {
 	const struct sys_reg_desc *idreg = first_idreg;
@@ -3400,13 +3445,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 
 	/* Initialize all idregs */
 	while (is_id_reg(id)) {
-		/*
-		 * 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 (idreg->visibility != raz_visibility)
-			IDREG(kvm, id) = read_sanitised_ftr_reg(id);
+		val = 0;
+		/* Read KVM sanitised register value if available */
+		if (idreg->reset)
+			val = idreg->reset(NULL, idreg);
+		IDREG(kvm, id) = val;
 
 		idreg++;
 		id = reg_to_encoding(idreg);
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index eba10de2e7ae..c65c129b3500 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -71,13 +71,16 @@ struct sys_reg_desc {
 		       struct sys_reg_params *,
 		       const struct sys_reg_desc *);
 
-	/* Initialization for vcpu. */
-	void (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *);
+	/*
+	 * Initialization for vcpu. Return initialized value, or KVM
+	 * sanitized value for ID registers.
+	 */
+	u64 (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *);
 
 	/* Index into sys_reg[], or 0 if we don't need to save it. */
 	int reg;
 
-	/* Value (usually reset value) */
+	/* Value (usually reset value), or write mask for idregs */
 	u64 val;
 
 	/* Custom get/set_user functions, fallback to generic if NULL */
@@ -130,19 +133,21 @@ static inline bool read_zero(struct kvm_vcpu *vcpu,
 }
 
 /* Reset functions */
-static inline void reset_unknown(struct kvm_vcpu *vcpu,
+static inline u64 reset_unknown(struct kvm_vcpu *vcpu,
 				 const struct sys_reg_desc *r)
 {
 	BUG_ON(!r->reg);
 	BUG_ON(r->reg >= NR_SYS_REGS);
 	__vcpu_sys_reg(vcpu, r->reg) = 0x1de7ec7edbadc0deULL;
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
-static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+static inline u64 reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
 	BUG_ON(!r->reg);
 	BUG_ON(r->reg >= NR_SYS_REGS);
 	__vcpu_sys_reg(vcpu, r->reg) = r->val;
+	return __vcpu_sys_reg(vcpu, r->reg);
 }
 
 static inline unsigned int sysreg_visibility(const struct kvm_vcpu *vcpu,
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02  0:51   ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
specific to ID register.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/kernel/cpufeature.c      |   2 +-
 arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++---------
 3 files changed, 203 insertions(+), 91 deletions(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 6bf013fb110d..dc769c2eb7a4 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
 	return 8;
 }
 
+s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
 struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
 
 extern struct arm64_ftr_override id_aa64mmfr1_override;
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 7d7128c65161..3317a7b6deac 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
 	return reg;
 }
 
-static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
+s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
 				s64 cur)
 {
 	s64 ret = 0;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1a534e0fc4ca..50d4e25f42d3 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -41,6 +41,7 @@
  * 64bit interface.
  */
 
+static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
@@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
+				    s64 new, s64 cur)
+{
+	struct arm64_ftr_bits kvm_ftr = *ftrp;
+
+	/* Some features have different safe value type in KVM than host features */
+	switch (id) {
+	case SYS_ID_AA64DFR0_EL1:
+		if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
+			kvm_ftr.type = FTR_LOWER_SAFE;
+		break;
+	case SYS_ID_DFR0_EL1:
+		if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
+			kvm_ftr.type = FTR_LOWER_SAFE;
+		break;
+	}
+
+	return arm64_ftr_safe_value(&kvm_ftr, new, cur);
+}
+
+/**
+ * arm64_check_features() - Check if a feature register value constitutes
+ * a subset of features indicated by the idreg's KVM sanitised limit.
+ *
+ * This function will check if each feature field of @val is the "safe" value
+ * against idreg's KVM sanitised limit return from reset() callback.
+ * If a field value in @val is the same as the one in limit, it is always
+ * considered the safe value regardless For register fields that are not in
+ * writable, only the value in limit is considered the safe value.
+ *
+ * Return: 0 if all the fields are safe. Otherwise, return negative errno.
+ */
+static int arm64_check_features(struct kvm_vcpu *vcpu,
+				const struct sys_reg_desc *rd,
+				u64 val)
+{
+	const struct arm64_ftr_reg *ftr_reg;
+	const struct arm64_ftr_bits *ftrp = NULL;
+	u32 id = reg_to_encoding(rd);
+	u64 writable_mask = rd->val;
+	u64 limit = 0;
+	u64 mask = 0;
+
+	/* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
+	if (rd->reset) {
+		limit = rd->reset(vcpu, rd);
+		ftr_reg = get_arm64_ftr_reg(id);
+		if (!ftr_reg)
+			return -EINVAL;
+		ftrp = ftr_reg->ftr_bits;
+	}
+
+	for (; ftrp && ftrp->width; ftrp++) {
+		s64 f_val, f_lim, safe_val;
+		u64 ftr_mask;
+
+		ftr_mask = arm64_ftr_mask(ftrp);
+		if ((ftr_mask & writable_mask) != ftr_mask)
+			continue;
+
+		f_val = arm64_ftr_value(ftrp, val);
+		f_lim = arm64_ftr_value(ftrp, limit);
+		mask |= ftr_mask;
+
+		if (f_val == f_lim)
+			safe_val = f_val;
+		else
+			safe_val = kvm_arm64_ftr_safe_value(id, ftrp, f_val, f_lim);
+
+		if (safe_val != f_val)
+			return -E2BIG;
+	}
+
+	/* For fields that are not writable, values in limit are the safe values. */
+	if ((val & ~mask) != (limit & ~mask))
+		return -E2BIG;
+
+	return 0;
+}
+
 static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
 {
 	if (kvm_vcpu_has_pmu(vcpu))
@@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
-static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
+static int pmuver_update(struct kvm_vcpu *vcpu,
+			  const struct sys_reg_desc *rd,
+			  u64 val,
+			  u8 pmuver,
+			  bool valid_pmu)
 {
-	u64 val;
+	int ret;
+
+	ret = set_id_reg(vcpu, rd, val);
+	if (ret)
+		return ret;
 
 	if (valid_pmu) {
 		val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
@@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
 		assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
 			   pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
 	}
+
+	return 0;
 }
 
 static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
@@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 		if (kvm_vgic_global_state.type == VGIC_V3) {
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
 			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
@@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 			val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
 		break;
 	case SYS_ID_AA64DFR0_EL1:
-		/* Limit debug to ARMv8.0 */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
 		/* Set PMUver to the required version */
 		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
 		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
 				  vcpu_pmuver(vcpu));
-		/* Hide SPE from guests */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
 		break;
 	case SYS_ID_DFR0_EL1:
 		val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
@@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-			       const struct sys_reg_desc *rd,
-			       u64 val)
+static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
 {
-	u64 new_val = val;
-	u8 csv2, csv3;
+	u64 val;
+	u32 id = reg_to_encoding(rd);
 
+	val = read_sanitised_ftr_reg(id);
 	/*
-	 * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
-	 * it doesn't promise more than what is actually provided (the
-	 * guest could otherwise be covered in ectoplasmic residue).
+	 * The default is to expose CSV2 == 1 if the HW isn't affected.
+	 * Although this is a per-CPU feature, we make it global because
+	 * asymmetric systems are just a nuisance.
+	 *
+	 * Userspace can override this as long as it doesn't promise
+	 * the impossible.
 	 */
-	csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
-	if (csv2 > 1 ||
-	    (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
+	}
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
+	}
 
-	/* Same thing for CSV3 */
-	csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
-	if (csv3 > 1 ||
-	    (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 
-	/* We can only differ with CSV[23], and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
-		 ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
-	if (val)
-		return -EINVAL;
+	return val;
+}
 
-	IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
-	return 0;
+static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+	/* Limit debug to ARMv8.0 */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+			  kvm_arm_pmu_get_pmuver_limit());
+	/* Hide SPE from guests */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
+
+	return val;
 }
 
 static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
@@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
 		return -EINVAL;
 
-	/* We can only differ with PMUver, and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
-	if (val)
-		return -EINVAL;
+	if (!valid_pmu) {
+		/*
+		 * Ignore the PMUVer field in @val. The PMUVer would be determined
+		 * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
+		 */
+		pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
+				   IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
+		val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
+		val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
+	}
 
-	pmuver_update(vcpu, pmuver, valid_pmu);
-	return 0;
+	return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
+}
+
+static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
+				      const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), kvm_arm_pmu_get_pmuver_limit());
+
+	return val;
 }
 
 static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
@@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
 		return -EINVAL;
 
-	/* We can only differ with PerfMon, and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
-	if (val)
-		return -EINVAL;
+	if (!valid_pmu) {
+		/*
+		 * Ignore the PerfMon field in @val. The PerfMon would be determined
+		 * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
+		 */
+		perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
+				    IDREG(vcpu->kvm, SYS_ID_DFR0_EL1));
+		val &= ~ID_DFR0_EL1_PerfMon_MASK;
+		val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, perfmon);
+	}
 
-	pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
-	return 0;
+	return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
 }
 
 /*
@@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 		      u64 val)
 {
-	/* This is what we mean by invariant: you can't change it. */
-	if (val != read_id_reg(vcpu, rd))
-		return -EINVAL;
+	u32 id = reg_to_encoding(rd);
+	int ret = 0;
 
-	return 0;
+	ret = arm64_check_features(vcpu, rd, val);
+	if (!ret)
+		IDREG(vcpu->kvm, id) = val;
+
+	return ret;
 }
 
 static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -1875,9 +2006,13 @@ static const struct sys_reg_desc sys_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, },
+	{ 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,
+	  .reset = read_sanitised_id_dfr0_el1,
+	  .val = ID_DFR0_EL1_PerfMon_MASK, },
 	ID_HIDDEN(ID_AFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR1_EL1),
@@ -1906,8 +2041,12 @@ static const struct sys_reg_desc sys_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, },
+	{ SYS_DESC(SYS_ID_AA64PFR0_EL1),
+	  .access = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset = read_sanitised_id_aa64pfr0_el1,
+	  .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
@@ -1917,8 +2056,12 @@ static const struct sys_reg_desc sys_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, },
+	{ SYS_DESC(SYS_ID_AA64DFR0_EL1),
+	  .access = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_aa64dfr0_el1,
+	  .reset = read_sanitised_id_aa64dfr0_el1,
+	  .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
 	ID_SANITISED(ID_AA64DFR1_EL1),
 	ID_UNALLOCATED(5,2),
 	ID_UNALLOCATED(5,3),
@@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
-
-	/*
-	 * The default is to expose CSV2 == 1 if the HW isn't affected.
-	 * Although this is a per-CPU feature, we make it global because
-	 * asymmetric systems are just a nuisance.
-	 *
-	 * Userspace can override this as long as it doesn't promise
-	 * the impossible.
-	 */
-	val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
-
-	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
-	}
-	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
-	}
-
-	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
-	/*
-	 * Initialise the default PMUver before there is a chance to
-	 * create an actual PMU.
-	 */
-	val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
-
-	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
-	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
-			  kvm_arm_pmu_get_pmuver_limit());
-
-	IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.41.0.rc0.172.g3f132b7071-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-02  0:51   ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02  0:51 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta, Jing Zhang

Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
specific to ID register.

Signed-off-by: Jing Zhang <jingzhangos@google.com>
---
 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/kernel/cpufeature.c      |   2 +-
 arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++---------
 3 files changed, 203 insertions(+), 91 deletions(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 6bf013fb110d..dc769c2eb7a4 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
 	return 8;
 }
 
+s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
 struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
 
 extern struct arm64_ftr_override id_aa64mmfr1_override;
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 7d7128c65161..3317a7b6deac 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
 	return reg;
 }
 
-static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
+s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
 				s64 cur)
 {
 	s64 ret = 0;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1a534e0fc4ca..50d4e25f42d3 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -41,6 +41,7 @@
  * 64bit interface.
  */
 
+static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
@@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
+				    s64 new, s64 cur)
+{
+	struct arm64_ftr_bits kvm_ftr = *ftrp;
+
+	/* Some features have different safe value type in KVM than host features */
+	switch (id) {
+	case SYS_ID_AA64DFR0_EL1:
+		if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
+			kvm_ftr.type = FTR_LOWER_SAFE;
+		break;
+	case SYS_ID_DFR0_EL1:
+		if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
+			kvm_ftr.type = FTR_LOWER_SAFE;
+		break;
+	}
+
+	return arm64_ftr_safe_value(&kvm_ftr, new, cur);
+}
+
+/**
+ * arm64_check_features() - Check if a feature register value constitutes
+ * a subset of features indicated by the idreg's KVM sanitised limit.
+ *
+ * This function will check if each feature field of @val is the "safe" value
+ * against idreg's KVM sanitised limit return from reset() callback.
+ * If a field value in @val is the same as the one in limit, it is always
+ * considered the safe value regardless For register fields that are not in
+ * writable, only the value in limit is considered the safe value.
+ *
+ * Return: 0 if all the fields are safe. Otherwise, return negative errno.
+ */
+static int arm64_check_features(struct kvm_vcpu *vcpu,
+				const struct sys_reg_desc *rd,
+				u64 val)
+{
+	const struct arm64_ftr_reg *ftr_reg;
+	const struct arm64_ftr_bits *ftrp = NULL;
+	u32 id = reg_to_encoding(rd);
+	u64 writable_mask = rd->val;
+	u64 limit = 0;
+	u64 mask = 0;
+
+	/* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
+	if (rd->reset) {
+		limit = rd->reset(vcpu, rd);
+		ftr_reg = get_arm64_ftr_reg(id);
+		if (!ftr_reg)
+			return -EINVAL;
+		ftrp = ftr_reg->ftr_bits;
+	}
+
+	for (; ftrp && ftrp->width; ftrp++) {
+		s64 f_val, f_lim, safe_val;
+		u64 ftr_mask;
+
+		ftr_mask = arm64_ftr_mask(ftrp);
+		if ((ftr_mask & writable_mask) != ftr_mask)
+			continue;
+
+		f_val = arm64_ftr_value(ftrp, val);
+		f_lim = arm64_ftr_value(ftrp, limit);
+		mask |= ftr_mask;
+
+		if (f_val == f_lim)
+			safe_val = f_val;
+		else
+			safe_val = kvm_arm64_ftr_safe_value(id, ftrp, f_val, f_lim);
+
+		if (safe_val != f_val)
+			return -E2BIG;
+	}
+
+	/* For fields that are not writable, values in limit are the safe values. */
+	if ((val & ~mask) != (limit & ~mask))
+		return -E2BIG;
+
+	return 0;
+}
+
 static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
 {
 	if (kvm_vcpu_has_pmu(vcpu))
@@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 	}
 }
 
-static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
+static int pmuver_update(struct kvm_vcpu *vcpu,
+			  const struct sys_reg_desc *rd,
+			  u64 val,
+			  u8 pmuver,
+			  bool valid_pmu)
 {
-	u64 val;
+	int ret;
+
+	ret = set_id_reg(vcpu, rd, val);
+	if (ret)
+		return ret;
 
 	if (valid_pmu) {
 		val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
@@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
 		assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
 			   pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
 	}
+
+	return 0;
 }
 
 static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
@@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 		if (kvm_vgic_global_state.type == VGIC_V3) {
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
 			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
@@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 			val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
 		break;
 	case SYS_ID_AA64DFR0_EL1:
-		/* Limit debug to ARMv8.0 */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
 		/* Set PMUver to the required version */
 		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
 		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
 				  vcpu_pmuver(vcpu));
-		/* Hide SPE from guests */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
 		break;
 	case SYS_ID_DFR0_EL1:
 		val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
@@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-			       const struct sys_reg_desc *rd,
-			       u64 val)
+static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
 {
-	u64 new_val = val;
-	u8 csv2, csv3;
+	u64 val;
+	u32 id = reg_to_encoding(rd);
 
+	val = read_sanitised_ftr_reg(id);
 	/*
-	 * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
-	 * it doesn't promise more than what is actually provided (the
-	 * guest could otherwise be covered in ectoplasmic residue).
+	 * The default is to expose CSV2 == 1 if the HW isn't affected.
+	 * Although this is a per-CPU feature, we make it global because
+	 * asymmetric systems are just a nuisance.
+	 *
+	 * Userspace can override this as long as it doesn't promise
+	 * the impossible.
 	 */
-	csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
-	if (csv2 > 1 ||
-	    (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
+	}
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
+	}
 
-	/* Same thing for CSV3 */
-	csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
-	if (csv3 > 1 ||
-	    (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 
-	/* We can only differ with CSV[23], and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
-		 ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
-	if (val)
-		return -EINVAL;
+	return val;
+}
 
-	IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
-	return 0;
+static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+	/* Limit debug to ARMv8.0 */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
+			  kvm_arm_pmu_get_pmuver_limit());
+	/* Hide SPE from guests */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
+
+	return val;
 }
 
 static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
@@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
 		return -EINVAL;
 
-	/* We can only differ with PMUver, and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
-	if (val)
-		return -EINVAL;
+	if (!valid_pmu) {
+		/*
+		 * Ignore the PMUVer field in @val. The PMUVer would be determined
+		 * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
+		 */
+		pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
+				   IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
+		val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
+		val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
+	}
 
-	pmuver_update(vcpu, pmuver, valid_pmu);
-	return 0;
+	return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
+}
+
+static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
+				      const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+	/*
+	 * Initialise the default PMUver before there is a chance to
+	 * create an actual PMU.
+	 */
+	val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
+	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), kvm_arm_pmu_get_pmuver_limit());
+
+	return val;
 }
 
 static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
@@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
 		return -EINVAL;
 
-	/* We can only differ with PerfMon, and anything else is an error */
-	val ^= read_id_reg(vcpu, rd);
-	val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
-	if (val)
-		return -EINVAL;
+	if (!valid_pmu) {
+		/*
+		 * Ignore the PerfMon field in @val. The PerfMon would be determined
+		 * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
+		 */
+		perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
+				    IDREG(vcpu->kvm, SYS_ID_DFR0_EL1));
+		val &= ~ID_DFR0_EL1_PerfMon_MASK;
+		val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, perfmon);
+	}
 
-	pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
-	return 0;
+	return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
 }
 
 /*
@@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
 		      u64 val)
 {
-	/* This is what we mean by invariant: you can't change it. */
-	if (val != read_id_reg(vcpu, rd))
-		return -EINVAL;
+	u32 id = reg_to_encoding(rd);
+	int ret = 0;
 
-	return 0;
+	ret = arm64_check_features(vcpu, rd, val);
+	if (!ret)
+		IDREG(vcpu->kvm, id) = val;
+
+	return ret;
 }
 
 static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -1875,9 +2006,13 @@ static const struct sys_reg_desc sys_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, },
+	{ 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,
+	  .reset = read_sanitised_id_dfr0_el1,
+	  .val = ID_DFR0_EL1_PerfMon_MASK, },
 	ID_HIDDEN(ID_AFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR0_EL1),
 	AA32_ID_SANITISED(ID_MMFR1_EL1),
@@ -1906,8 +2041,12 @@ static const struct sys_reg_desc sys_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, },
+	{ SYS_DESC(SYS_ID_AA64PFR0_EL1),
+	  .access = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset = read_sanitised_id_aa64pfr0_el1,
+	  .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
@@ -1917,8 +2056,12 @@ static const struct sys_reg_desc sys_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, },
+	{ SYS_DESC(SYS_ID_AA64DFR0_EL1),
+	  .access = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_aa64dfr0_el1,
+	  .reset = read_sanitised_id_aa64dfr0_el1,
+	  .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
 	ID_SANITISED(ID_AA64DFR1_EL1),
 	ID_UNALLOCATED(5,2),
 	ID_UNALLOCATED(5,3),
@@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
-
-	/*
-	 * The default is to expose CSV2 == 1 if the HW isn't affected.
-	 * Although this is a per-CPU feature, we make it global because
-	 * asymmetric systems are just a nuisance.
-	 *
-	 * Userspace can override this as long as it doesn't promise
-	 * the impossible.
-	 */
-	val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
-
-	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
-	}
-	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
-	}
-
-	IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
-	/*
-	 * Initialise the default PMUver before there is a chance to
-	 * create an actual PMU.
-	 */
-	val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
-
-	val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
-	val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
-			  kvm_arm_pmu_get_pmuver_limit());
-
-	IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.41.0.rc0.172.g3f132b7071-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02  0:51   ` Jing Zhang
@ 2023-06-02 17:15     ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02 17:15 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton, sjitindarsingh
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta

Hi Suraj,

Let's continue the problem here you raised in the v9.
For the SVE example, the problem would be gone by enabling the
writable for SVE field? Since we are going to enable the writable for
all ID regs one by one later.
The double checking for CSV[2|3] is gone in this series.

Thanks,
Jing

On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com> wrote:
>
> Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> specific to ID register.
>
> Signed-off-by: Jing Zhang <jingzhangos@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      |   2 +-
>  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++---------
>  3 files changed, 203 insertions(+), 91 deletions(-)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 6bf013fb110d..dc769c2eb7a4 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
>         return 8;
>  }
>
> +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
>  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
>
>  extern struct arm64_ftr_override id_aa64mmfr1_override;
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 7d7128c65161..3317a7b6deac 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
>         return reg;
>  }
>
> -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
> +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
>                                 s64 cur)
>  {
>         s64 ret = 0;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 1a534e0fc4ca..50d4e25f42d3 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -41,6 +41,7 @@
>   * 64bit interface.
>   */
>
> +static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
>  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
>  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
>
> @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
> +                                   s64 new, s64 cur)
> +{
> +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> +
> +       /* Some features have different safe value type in KVM than host features */
> +       switch (id) {
> +       case SYS_ID_AA64DFR0_EL1:
> +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> +                       kvm_ftr.type = FTR_LOWER_SAFE;
> +               break;
> +       case SYS_ID_DFR0_EL1:
> +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> +                       kvm_ftr.type = FTR_LOWER_SAFE;
> +               break;
> +       }
> +
> +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> +}
> +
> +/**
> + * arm64_check_features() - Check if a feature register value constitutes
> + * a subset of features indicated by the idreg's KVM sanitised limit.
> + *
> + * This function will check if each feature field of @val is the "safe" value
> + * against idreg's KVM sanitised limit return from reset() callback.
> + * If a field value in @val is the same as the one in limit, it is always
> + * considered the safe value regardless For register fields that are not in
> + * writable, only the value in limit is considered the safe value.
> + *
> + * Return: 0 if all the fields are safe. Otherwise, return negative errno.
> + */
> +static int arm64_check_features(struct kvm_vcpu *vcpu,
> +                               const struct sys_reg_desc *rd,
> +                               u64 val)
> +{
> +       const struct arm64_ftr_reg *ftr_reg;
> +       const struct arm64_ftr_bits *ftrp = NULL;
> +       u32 id = reg_to_encoding(rd);
> +       u64 writable_mask = rd->val;
> +       u64 limit = 0;
> +       u64 mask = 0;
> +
> +       /* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
> +       if (rd->reset) {
> +               limit = rd->reset(vcpu, rd);
> +               ftr_reg = get_arm64_ftr_reg(id);
> +               if (!ftr_reg)
> +                       return -EINVAL;
> +               ftrp = ftr_reg->ftr_bits;
> +       }
> +
> +       for (; ftrp && ftrp->width; ftrp++) {
> +               s64 f_val, f_lim, safe_val;
> +               u64 ftr_mask;
> +
> +               ftr_mask = arm64_ftr_mask(ftrp);
> +               if ((ftr_mask & writable_mask) != ftr_mask)
> +                       continue;
> +
> +               f_val = arm64_ftr_value(ftrp, val);
> +               f_lim = arm64_ftr_value(ftrp, limit);
> +               mask |= ftr_mask;
> +
> +               if (f_val == f_lim)
> +                       safe_val = f_val;
> +               else
> +                       safe_val = kvm_arm64_ftr_safe_value(id, ftrp, f_val, f_lim);
> +
> +               if (safe_val != f_val)
> +                       return -E2BIG;
> +       }
> +
> +       /* For fields that are not writable, values in limit are the safe values. */
> +       if ((val & ~mask) != (limit & ~mask))
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +
>  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
>  {
>         if (kvm_vcpu_has_pmu(vcpu))
> @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
>         }
>  }
>
> -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
> +static int pmuver_update(struct kvm_vcpu *vcpu,
> +                         const struct sys_reg_desc *rd,
> +                         u64 val,
> +                         u8 pmuver,
> +                         bool valid_pmu)
>  {
> -       u64 val;
> +       int ret;
> +
> +       ret = set_id_reg(vcpu, rd, val);
> +       if (ret)
> +               return ret;
>
>         if (valid_pmu) {
>                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
>                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
>                            pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
>         }
> +
> +       return 0;
>  }
>
>  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
> @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
>         case SYS_ID_AA64PFR0_EL1:
>                 if (!vcpu_has_sve(vcpu))
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
>                 if (kvm_vgic_global_state.type == VGIC_V3) {
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
>                         val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
>                 break;
>         case SYS_ID_AA64DFR0_EL1:
> -               /* Limit debug to ARMv8.0 */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
>                 /* Set PMUver to the required version */
>                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
>                 val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
>                                   vcpu_pmuver(vcpu));
> -               /* Hide SPE from guests */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
>                 break;
>         case SYS_ID_DFR0_EL1:
>                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              u64 val)
> +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc *rd)
>  {
> -       u64 new_val = val;
> -       u8 csv2, csv3;
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
>
> +       val = read_sanitised_ftr_reg(id);
>         /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> +        * The default is to expose CSV2 == 1 if the HW isn't affected.
> +        * Although this is a per-CPU feature, we make it global because
> +        * asymmetric systems are just a nuisance.
> +        *
> +        * Userspace can override this as long as it doesn't promise
> +        * the impossible.
>          */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> +       }
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> +       }
>
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
>
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> -       if (val)
> -               return -EINVAL;
> +       return val;
> +}
>
> -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> -       return 0;
> +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /* Limit debug to ARMv8.0 */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> +                         kvm_arm_pmu_get_pmuver_limit());
> +       /* Hide SPE from guests */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> +
> +       return val;
>  }
>
>  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
>         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
>                 return -EINVAL;
>
> -       /* We can only differ with PMUver, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -       if (val)
> -               return -EINVAL;
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PMUVer field in @val. The PMUVer would be determined
> +                * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> +                                  IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
> +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
> +       }
>
> -       pmuver_update(vcpu, pmuver, valid_pmu);
> -       return 0;
> +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> +}
> +
> +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                                     const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), kvm_arm_pmu_get_pmuver_limit());
> +
> +       return val;
>  }
>
>  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
>         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
>                 return -EINVAL;
>
> -       /* We can only differ with PerfMon, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> -       if (val)
> -               return -EINVAL;
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PerfMon field in @val. The PerfMon would be determined
> +                * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> +                                   IDREG(vcpu->kvm, SYS_ID_DFR0_EL1));
> +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, perfmon);
> +       }
>
> -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> -       return 0;
> +       return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
>  }
>
>  /*
> @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
>  static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
>                       u64 val)
>  {
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd))
> -               return -EINVAL;
> +       u32 id = reg_to_encoding(rd);
> +       int ret = 0;
>
> -       return 0;
> +       ret = arm64_check_features(vcpu, rd, val);
> +       if (!ret)
> +               IDREG(vcpu->kvm, id) = val;
> +
> +       return ret;
>  }
>
>  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc sys_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, },
> +       { 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,
> +         .reset = read_sanitised_id_dfr0_el1,
> +         .val = ID_DFR0_EL1_PerfMon_MASK, },
>         ID_HIDDEN(ID_AFR0_EL1),
>         AA32_ID_SANITISED(ID_MMFR0_EL1),
>         AA32_ID_SANITISED(ID_MMFR1_EL1),
> @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc sys_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, },
> +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> +         .access = access_id_reg,
> +         .get_user = get_id_reg,
> +         .set_user = set_id_reg,
> +         .reset = read_sanitised_id_aa64pfr0_el1,
> +         .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
>         ID_SANITISED(ID_AA64PFR1_EL1),
>         ID_UNALLOCATED(4,2),
>         ID_UNALLOCATED(4,3),
> @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc sys_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, },
> +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> +         .access = access_id_reg,
> +         .get_user = get_id_reg,
> +         .set_user = set_id_aa64dfr0_el1,
> +         .reset = read_sanitised_id_aa64dfr0_el1,
> +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
>         ID_SANITISED(ID_AA64DFR1_EL1),
>         ID_UNALLOCATED(5,2),
>         ID_UNALLOCATED(5,3),
> @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
>                 idreg++;
>                 id = reg_to_encoding(idreg);
>         }
> -
> -       /*
> -        * The default is to expose CSV2 == 1 if the HW isn't affected.
> -        * Although this is a per-CPU feature, we make it global because
> -        * asymmetric systems are just a nuisance.
> -        *
> -        * Userspace can override this as long as it doesn't promise
> -        * the impossible.
> -        */
> -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> -
> -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> -       }
> -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> -       }
> -
> -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> -       /*
> -        * Initialise the default PMUver before there is a chance to
> -        * create an actual PMU.
> -        */
> -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> -
> -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> -                         kvm_arm_pmu_get_pmuver_limit());
> -
> -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
>  }
>
>  int __init kvm_sys_reg_table_init(void)
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-02 17:15     ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-02 17:15 UTC (permalink / raw)
  To: KVM, KVMARM, ARMLinux, Marc Zyngier, Oliver Upton, sjitindarsingh
  Cc: Will Deacon, Paolo Bonzini, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Fuad Tabba, Reiji Watanabe,
	Raghavendra Rao Ananta

Hi Suraj,

Let's continue the problem here you raised in the v9.
For the SVE example, the problem would be gone by enabling the
writable for SVE field? Since we are going to enable the writable for
all ID regs one by one later.
The double checking for CSV[2|3] is gone in this series.

Thanks,
Jing

On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com> wrote:
>
> Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> specific to ID register.
>
> Signed-off-by: Jing Zhang <jingzhangos@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      |   2 +-
>  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++---------
>  3 files changed, 203 insertions(+), 91 deletions(-)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 6bf013fb110d..dc769c2eb7a4 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
>         return 8;
>  }
>
> +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
>  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
>
>  extern struct arm64_ftr_override id_aa64mmfr1_override;
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 7d7128c65161..3317a7b6deac 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
>         return reg;
>  }
>
> -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
> +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
>                                 s64 cur)
>  {
>         s64 ret = 0;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 1a534e0fc4ca..50d4e25f42d3 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -41,6 +41,7 @@
>   * 64bit interface.
>   */
>
> +static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
>  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
>  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
>
> @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
> +                                   s64 new, s64 cur)
> +{
> +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> +
> +       /* Some features have different safe value type in KVM than host features */
> +       switch (id) {
> +       case SYS_ID_AA64DFR0_EL1:
> +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> +                       kvm_ftr.type = FTR_LOWER_SAFE;
> +               break;
> +       case SYS_ID_DFR0_EL1:
> +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> +                       kvm_ftr.type = FTR_LOWER_SAFE;
> +               break;
> +       }
> +
> +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> +}
> +
> +/**
> + * arm64_check_features() - Check if a feature register value constitutes
> + * a subset of features indicated by the idreg's KVM sanitised limit.
> + *
> + * This function will check if each feature field of @val is the "safe" value
> + * against idreg's KVM sanitised limit return from reset() callback.
> + * If a field value in @val is the same as the one in limit, it is always
> + * considered the safe value regardless For register fields that are not in
> + * writable, only the value in limit is considered the safe value.
> + *
> + * Return: 0 if all the fields are safe. Otherwise, return negative errno.
> + */
> +static int arm64_check_features(struct kvm_vcpu *vcpu,
> +                               const struct sys_reg_desc *rd,
> +                               u64 val)
> +{
> +       const struct arm64_ftr_reg *ftr_reg;
> +       const struct arm64_ftr_bits *ftrp = NULL;
> +       u32 id = reg_to_encoding(rd);
> +       u64 writable_mask = rd->val;
> +       u64 limit = 0;
> +       u64 mask = 0;
> +
> +       /* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
> +       if (rd->reset) {
> +               limit = rd->reset(vcpu, rd);
> +               ftr_reg = get_arm64_ftr_reg(id);
> +               if (!ftr_reg)
> +                       return -EINVAL;
> +               ftrp = ftr_reg->ftr_bits;
> +       }
> +
> +       for (; ftrp && ftrp->width; ftrp++) {
> +               s64 f_val, f_lim, safe_val;
> +               u64 ftr_mask;
> +
> +               ftr_mask = arm64_ftr_mask(ftrp);
> +               if ((ftr_mask & writable_mask) != ftr_mask)
> +                       continue;
> +
> +               f_val = arm64_ftr_value(ftrp, val);
> +               f_lim = arm64_ftr_value(ftrp, limit);
> +               mask |= ftr_mask;
> +
> +               if (f_val == f_lim)
> +                       safe_val = f_val;
> +               else
> +                       safe_val = kvm_arm64_ftr_safe_value(id, ftrp, f_val, f_lim);
> +
> +               if (safe_val != f_val)
> +                       return -E2BIG;
> +       }
> +
> +       /* For fields that are not writable, values in limit are the safe values. */
> +       if ((val & ~mask) != (limit & ~mask))
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +
>  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
>  {
>         if (kvm_vcpu_has_pmu(vcpu))
> @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
>         }
>  }
>
> -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
> +static int pmuver_update(struct kvm_vcpu *vcpu,
> +                         const struct sys_reg_desc *rd,
> +                         u64 val,
> +                         u8 pmuver,
> +                         bool valid_pmu)
>  {
> -       u64 val;
> +       int ret;
> +
> +       ret = set_id_reg(vcpu, rd, val);
> +       if (ret)
> +               return ret;
>
>         if (valid_pmu) {
>                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool valid_pmu)
>                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu->kvm->arch.flags,
>                            pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
>         }
> +
> +       return 0;
>  }
>
>  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
> @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
>         case SYS_ID_AA64PFR0_EL1:
>                 if (!vcpu_has_sve(vcpu))
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
>                 if (kvm_vgic_global_state.type == VGIC_V3) {
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
>                         val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
>                 break;
>         case SYS_ID_AA64DFR0_EL1:
> -               /* Limit debug to ARMv8.0 */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
>                 /* Set PMUver to the required version */
>                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
>                 val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
>                                   vcpu_pmuver(vcpu));
> -               /* Hide SPE from guests */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
>                 break;
>         case SYS_ID_DFR0_EL1:
>                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              u64 val)
> +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc *rd)
>  {
> -       u64 new_val = val;
> -       u8 csv2, csv3;
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
>
> +       val = read_sanitised_ftr_reg(id);
>         /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> +        * The default is to expose CSV2 == 1 if the HW isn't affected.
> +        * Although this is a per-CPU feature, we make it global because
> +        * asymmetric systems are just a nuisance.
> +        *
> +        * Userspace can override this as long as it doesn't promise
> +        * the impossible.
>          */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> +       }
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> +       }
>
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
>
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> -       if (val)
> -               return -EINVAL;
> +       return val;
> +}
>
> -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> -       return 0;
> +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /* Limit debug to ARMv8.0 */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> +                         kvm_arm_pmu_get_pmuver_limit());
> +       /* Hide SPE from guests */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> +
> +       return val;
>  }
>
>  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
>         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
>                 return -EINVAL;
>
> -       /* We can only differ with PMUver, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -       if (val)
> -               return -EINVAL;
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PMUVer field in @val. The PMUVer would be determined
> +                * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> +                                  IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1));
> +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
> +       }
>
> -       pmuver_update(vcpu, pmuver, valid_pmu);
> -       return 0;
> +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> +}
> +
> +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                                     const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), kvm_arm_pmu_get_pmuver_limit());
> +
> +       return val;
>  }
>
>  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
>         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
>                 return -EINVAL;
>
> -       /* We can only differ with PerfMon, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> -       if (val)
> -               return -EINVAL;
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PerfMon field in @val. The PerfMon would be determined
> +                * by arch flags bit KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> +                                   IDREG(vcpu->kvm, SYS_ID_DFR0_EL1));
> +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, perfmon);
> +       }
>
> -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> -       return 0;
> +       return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
>  }
>
>  /*
> @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
>  static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
>                       u64 val)
>  {
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd))
> -               return -EINVAL;
> +       u32 id = reg_to_encoding(rd);
> +       int ret = 0;
>
> -       return 0;
> +       ret = arm64_check_features(vcpu, rd, val);
> +       if (!ret)
> +               IDREG(vcpu->kvm, id) = val;
> +
> +       return ret;
>  }
>
>  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc sys_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, },
> +       { 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,
> +         .reset = read_sanitised_id_dfr0_el1,
> +         .val = ID_DFR0_EL1_PerfMon_MASK, },
>         ID_HIDDEN(ID_AFR0_EL1),
>         AA32_ID_SANITISED(ID_MMFR0_EL1),
>         AA32_ID_SANITISED(ID_MMFR1_EL1),
> @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc sys_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, },
> +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> +         .access = access_id_reg,
> +         .get_user = get_id_reg,
> +         .set_user = set_id_reg,
> +         .reset = read_sanitised_id_aa64pfr0_el1,
> +         .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
>         ID_SANITISED(ID_AA64PFR1_EL1),
>         ID_UNALLOCATED(4,2),
>         ID_UNALLOCATED(4,3),
> @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc sys_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, },
> +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> +         .access = access_id_reg,
> +         .get_user = get_id_reg,
> +         .set_user = set_id_aa64dfr0_el1,
> +         .reset = read_sanitised_id_aa64dfr0_el1,
> +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
>         ID_SANITISED(ID_AA64DFR1_EL1),
>         ID_UNALLOCATED(5,2),
>         ID_UNALLOCATED(5,3),
> @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
>                 idreg++;
>                 id = reg_to_encoding(idreg);
>         }
> -
> -       /*
> -        * The default is to expose CSV2 == 1 if the HW isn't affected.
> -        * Although this is a per-CPU feature, we make it global because
> -        * asymmetric systems are just a nuisance.
> -        *
> -        * Userspace can override this as long as it doesn't promise
> -        * the impossible.
> -        */
> -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> -
> -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> -       }
> -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> -       }
> -
> -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> -       /*
> -        * Initialise the default PMUver before there is a chance to
> -        * create an actual PMU.
> -        */
> -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> -
> -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> -                         kvm_arm_pmu_get_pmuver_limit());
> -
> -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
>  }
>
>  int __init kvm_sys_reg_table_init(void)
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02  0:51   ` Jing Zhang
@ 2023-06-02 19:21     ` Jitindar Singh, Suraj
  -1 siblings, 0 replies; 40+ messages in thread
From: Jitindar Singh, Suraj @ 2023-06-02 19:21 UTC (permalink / raw)
  To: jingzhangos, kvm, kvmarm, linux-arm-kernel, maz, oupton
  Cc: james.morse, suzuki.poulose, rananta, tabba, pbonzini,
	alexandru.elisei, will, reijiw

On Fri, 2023-06-02 at 00:51 +0000, Jing Zhang wrote:
> Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> specific to ID register.
> 
> Signed-off-by: Jing Zhang <jingzhangos@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      |   2 +-
>  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-------
> --
>  3 files changed, 203 insertions(+), 91 deletions(-)
> 
> 
> +
> +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                                     const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> kvm_arm_pmu_get_pmuver_limit());

Maybe it's never possible, but does this need a:
pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit()) ?

> +
> +       return val;
>  }
>  
>  
Thanks
- Suraj

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-02 19:21     ` Jitindar Singh, Suraj
  0 siblings, 0 replies; 40+ messages in thread
From: Jitindar Singh, Suraj @ 2023-06-02 19:21 UTC (permalink / raw)
  To: jingzhangos, kvm, kvmarm, linux-arm-kernel, maz, oupton
  Cc: james.morse, suzuki.poulose, rananta, tabba, pbonzini,
	alexandru.elisei, will, reijiw

On Fri, 2023-06-02 at 00:51 +0000, Jing Zhang wrote:
> Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> specific to ID register.
> 
> Signed-off-by: Jing Zhang <jingzhangos@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      |   2 +-
>  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-------
> --
>  3 files changed, 203 insertions(+), 91 deletions(-)
> 
> 
> +
> +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                                     const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> kvm_arm_pmu_get_pmuver_limit());

Maybe it's never possible, but does this need a:
pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit()) ?

> +
> +       return val;
>  }
>  
>  
Thanks
- Suraj
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 0/3] RE: Support writable CPU ID registers from userspace [v11]
  2023-06-02  0:51 ` Jing Zhang
@ 2023-06-02 22:14   ` Suraj Jitindar Singh
  -1 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

With the patch set you posted I get some kvm unit tests failures due to
being unable to update register values from userspace for tests using smp.
The first patch addresses this.

The second 2 are optimisations/cleanups.

Based on "Support writable CPU ID registers from userspace" [1]

[1] https://lore.kernel.org/linux-arm-kernel/20230602005118.2899664-1-jingzhangos@google.com/

Suraj Jitindar Singh (3):
  KVM: arm64: Update id_reg limit value based on per vcpu flags
  KVM: arm64: Move non per vcpu flag checks out of
    kvm_arm_update_id_reg()
  KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE

 arch/arm64/include/asm/kvm_host.h |  21 +++--
 arch/arm64/kvm/arm.c              |  11 ++-
 arch/arm64/kvm/sys_regs.c         | 122 +++++++++++++++++++++++++-----
 3 files changed, 121 insertions(+), 33 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 0/3] RE: Support writable CPU ID registers from userspace [v11]
@ 2023-06-02 22:14   ` Suraj Jitindar Singh
  0 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

With the patch set you posted I get some kvm unit tests failures due to
being unable to update register values from userspace for tests using smp.
The first patch addresses this.

The second 2 are optimisations/cleanups.

Based on "Support writable CPU ID registers from userspace" [1]

[1] https://lore.kernel.org/linux-arm-kernel/20230602005118.2899664-1-jingzhangos@google.com/

Suraj Jitindar Singh (3):
  KVM: arm64: Update id_reg limit value based on per vcpu flags
  KVM: arm64: Move non per vcpu flag checks out of
    kvm_arm_update_id_reg()
  KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE

 arch/arm64/include/asm/kvm_host.h |  21 +++--
 arch/arm64/kvm/arm.c              |  11 ++-
 arch/arm64/kvm/sys_regs.c         | 122 +++++++++++++++++++++++++-----
 3 files changed, 121 insertions(+), 33 deletions(-)

-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 1/3] KVM: arm64: Update id_reg limit value based on per vcpu flags
  2023-06-02 22:14   ` Suraj Jitindar Singh
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  -1 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

There are multiple features the availability of which is enabled/disabled
and tracked on a per vcpu level in vcpu->arch.flagset e.g. sve, ptrauth,
and pmu. While the vm wide value of the id regs which represent the
availability of these features is stored in the id_regs kvm struct their
value needs to be manipulated on a per vcpu basis. This is done at read
time in kvm_arm_read_id_reg().

The value of these per vcpu flags needs to be factored in when calculating
the id_reg limit value in check_features() as otherwise we can run into the
following scenario.

[ running on cpu which supports sve ]
1. AA64PFR0.SVE set in id_reg by kvm_arm_init_id_regs() (cpu supports it
   and so is set in value returned from read_sanitised_ftr_reg())
2. vcpus created without sve feature enabled
3. vmm reads AA64PFR0 and attempts to write the same value back
   (writing the same value back is allowed)
4. write fails in check_features() as limit has AA64PFR0.SVE set however it
   is not set in the value being written and although a lower value is
   allowed for this feature it is not in the mask of bits which can be
   modified and so much match exactly.

Thus add a step in check_features() to update the limit returned from
id_reg->reset() with the per vcpu features which may have been
enabled/disabled at vcpu creation time after the id_regs were initialised.
Split this update into a new function named kvm_arm_update_id_reg() so it
can be called from check_features() as well as kvm_arm_read_id_reg() to
dedup code.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/kvm/sys_regs.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 50d4e25f42d3..a4e662bd218b 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -42,6 +42,7 @@
  */
 
 static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
+static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 id, u64 val);
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
@@ -1241,6 +1242,7 @@ static int arm64_check_features(struct kvm_vcpu *vcpu,
 	/* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
 	if (rd->reset) {
 		limit = rd->reset(vcpu, rd);
+		limit = kvm_arm_update_id_reg(vcpu, id, limit);
 		ftr_reg = get_arm64_ftr_reg(id);
 		if (!ftr_reg)
 			return -EINVAL;
@@ -1347,10 +1349,8 @@ static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sy
 	return read_sanitised_ftr_reg(reg_to_encoding(rd));
 }
 
-static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
+static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64 val)
 {
-	u64 val = IDREG(vcpu->kvm, encoding);
-
 	switch (encoding) {
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
@@ -1402,6 +1402,13 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 	return val;
 }
 
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
+{
+	u64 val = IDREG(vcpu->kvm, encoding);
+
+	return kvm_arm_update_id_reg(vcpu, encoding, val);
+}
+
 /* 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)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 1/3] KVM: arm64: Update id_reg limit value based on per vcpu flags
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  0 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

There are multiple features the availability of which is enabled/disabled
and tracked on a per vcpu level in vcpu->arch.flagset e.g. sve, ptrauth,
and pmu. While the vm wide value of the id regs which represent the
availability of these features is stored in the id_regs kvm struct their
value needs to be manipulated on a per vcpu basis. This is done at read
time in kvm_arm_read_id_reg().

The value of these per vcpu flags needs to be factored in when calculating
the id_reg limit value in check_features() as otherwise we can run into the
following scenario.

[ running on cpu which supports sve ]
1. AA64PFR0.SVE set in id_reg by kvm_arm_init_id_regs() (cpu supports it
   and so is set in value returned from read_sanitised_ftr_reg())
2. vcpus created without sve feature enabled
3. vmm reads AA64PFR0 and attempts to write the same value back
   (writing the same value back is allowed)
4. write fails in check_features() as limit has AA64PFR0.SVE set however it
   is not set in the value being written and although a lower value is
   allowed for this feature it is not in the mask of bits which can be
   modified and so much match exactly.

Thus add a step in check_features() to update the limit returned from
id_reg->reset() with the per vcpu features which may have been
enabled/disabled at vcpu creation time after the id_regs were initialised.
Split this update into a new function named kvm_arm_update_id_reg() so it
can be called from check_features() as well as kvm_arm_read_id_reg() to
dedup code.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/kvm/sys_regs.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 50d4e25f42d3..a4e662bd218b 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -42,6 +42,7 @@
  */
 
 static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
+static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 id, u64 val);
 static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
@@ -1241,6 +1242,7 @@ static int arm64_check_features(struct kvm_vcpu *vcpu,
 	/* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
 	if (rd->reset) {
 		limit = rd->reset(vcpu, rd);
+		limit = kvm_arm_update_id_reg(vcpu, id, limit);
 		ftr_reg = get_arm64_ftr_reg(id);
 		if (!ftr_reg)
 			return -EINVAL;
@@ -1347,10 +1349,8 @@ static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sy
 	return read_sanitised_ftr_reg(reg_to_encoding(rd));
 }
 
-static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
+static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64 val)
 {
-	u64 val = IDREG(vcpu->kvm, encoding);
-
 	switch (encoding) {
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
@@ -1402,6 +1402,13 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
 	return val;
 }
 
+static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
+{
+	u64 val = IDREG(vcpu->kvm, encoding);
+
+	return kvm_arm_update_id_reg(vcpu, encoding, val);
+}
+
 /* 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)
 {
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 2/3] KVM: arm64: Move non per vcpu flag checks out of kvm_arm_update_id_reg()
  2023-06-02 22:14   ` Suraj Jitindar Singh
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  -1 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

There are features which are masked in kvm_arm_update_id_reg() which cannot
change throughout the lifecycle of a VM. Thus rather than masking them each
time the register is read, mask them at idreg init time so that the value
in the kvm id_reg correctly reflects the state of support for that feature.

Move masking of AA64PFR0_EL1.GIC and AA64PFR0_EL1.AMU into
read_sanitised_id_aa64pfr0_el1().
Create read_sanitised_id_aa64pfr1_el1() and mask AA64PFR1_EL1.SME.
Create read_sanitised_id_[mmfr4|aa64mmfr2] and mask CCIDX.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/kvm/sys_regs.c | 104 +++++++++++++++++++++++++++++++-------
 1 file changed, 86 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index a4e662bd218b..59f8adda47fa 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1355,16 +1355,10 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
-		if (kvm_vgic_global_state.type == VGIC_V3) {
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
-			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
-		}
 		break;
 	case SYS_ID_AA64PFR1_EL1:
 		if (!kvm_has_mte(vcpu->kvm))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
-
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
 		break;
 	case SYS_ID_AA64ISAR1_EL1:
 		if (!vcpu_has_ptrauth(vcpu))
@@ -1377,8 +1371,6 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 		if (!vcpu_has_ptrauth(vcpu))
 			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) |
 				 ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
-		if (!cpus_have_final_cap(ARM64_HAS_WFXT))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
 		break;
 	case SYS_ID_AA64DFR0_EL1:
 		/* Set PMUver to the required version */
@@ -1391,12 +1383,6 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
 				  pmuver_to_perfmon(vcpu_pmuver(vcpu)));
 		break;
-	case SYS_ID_AA64MMFR2_EL1:
-		val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
-		break;
-	case SYS_ID_MMFR4_EL1:
-		val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
-		break;
 	}
 
 	return val;
@@ -1490,6 +1476,20 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
+static u64 read_sanitised_id_mmfr4_el1(struct kvm_vcpu *vcpu,
+				       const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* CCIDX is not supported */
+	val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
+
+	return val;
+}
+
 static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 					  const struct sys_reg_desc *rd)
 {
@@ -1516,6 +1516,25 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 
 	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 
+	if (kvm_vgic_global_state.type == VGIC_V3) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
+	}
+
+	return val;
+}
+
+static u64 read_sanitised_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* SME is not supported */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
+
 	return val;
 }
 
@@ -1638,6 +1657,34 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
 }
 
+static u64 read_sanitised_id_aa64isar2_el1(struct kvm_vcpu *vcpu,
+					   const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	if (!cpus_have_final_cap(ARM64_HAS_WFXT))
+		val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
+
+	return val;
+}
+
+static u64 read_sanitised_id_aa64mmfr2_el1(struct kvm_vcpu *vcpu,
+					   const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* CCIDX is not supported */
+	val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
+
+	return val;
+}
+
 /*
  * cpufeature ID register user accessors
  *
@@ -2033,7 +2080,13 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	AA32_ID_SANITISED(ID_ISAR3_EL1),
 	AA32_ID_SANITISED(ID_ISAR4_EL1),
 	AA32_ID_SANITISED(ID_ISAR5_EL1),
-	AA32_ID_SANITISED(ID_MMFR4_EL1),
+	{ SYS_DESC(SYS_ID_MMFR4_EL1),
+	  .access     = access_id_reg,
+	  .get_user   = get_id_reg,
+	  .set_user   = set_id_reg,
+	  .visibility = aa32_id_visibility,
+	  .reset      = read_sanitised_id_mmfr4_el1,
+	  .val        = 0, },
 	AA32_ID_SANITISED(ID_ISAR6_EL1),
 
 	/* CRm=3 */
@@ -2054,7 +2107,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	  .set_user = set_id_reg,
 	  .reset = read_sanitised_id_aa64pfr0_el1,
 	  .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
-	ID_SANITISED(ID_AA64PFR1_EL1),
+	{ SYS_DESC(SYS_ID_AA64PFR1_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64pfr1_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
 	ID_SANITISED(ID_AA64ZFR0_EL1),
@@ -2080,7 +2138,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	/* CRm=6 */
 	ID_SANITISED(ID_AA64ISAR0_EL1),
 	ID_SANITISED(ID_AA64ISAR1_EL1),
-	ID_SANITISED(ID_AA64ISAR2_EL1),
+	{ SYS_DESC(SYS_ID_AA64ISAR2_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64isar2_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(6,3),
 	ID_UNALLOCATED(6,4),
 	ID_UNALLOCATED(6,5),
@@ -2090,7 +2153,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	/* CRm=7 */
 	ID_SANITISED(ID_AA64MMFR0_EL1),
 	ID_SANITISED(ID_AA64MMFR1_EL1),
-	ID_SANITISED(ID_AA64MMFR2_EL1),
+	{ SYS_DESC(SYS_ID_AA64MMFR2_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64mmfr2_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(7,3),
 	ID_UNALLOCATED(7,4),
 	ID_UNALLOCATED(7,5),
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 2/3] KVM: arm64: Move non per vcpu flag checks out of kvm_arm_update_id_reg()
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  0 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

There are features which are masked in kvm_arm_update_id_reg() which cannot
change throughout the lifecycle of a VM. Thus rather than masking them each
time the register is read, mask them at idreg init time so that the value
in the kvm id_reg correctly reflects the state of support for that feature.

Move masking of AA64PFR0_EL1.GIC and AA64PFR0_EL1.AMU into
read_sanitised_id_aa64pfr0_el1().
Create read_sanitised_id_aa64pfr1_el1() and mask AA64PFR1_EL1.SME.
Create read_sanitised_id_[mmfr4|aa64mmfr2] and mask CCIDX.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/kvm/sys_regs.c | 104 +++++++++++++++++++++++++++++++-------
 1 file changed, 86 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index a4e662bd218b..59f8adda47fa 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1355,16 +1355,10 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 	case SYS_ID_AA64PFR0_EL1:
 		if (!vcpu_has_sve(vcpu))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
-		if (kvm_vgic_global_state.type == VGIC_V3) {
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
-			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
-		}
 		break;
 	case SYS_ID_AA64PFR1_EL1:
 		if (!kvm_has_mte(vcpu->kvm))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
-
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
 		break;
 	case SYS_ID_AA64ISAR1_EL1:
 		if (!vcpu_has_ptrauth(vcpu))
@@ -1377,8 +1371,6 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 		if (!vcpu_has_ptrauth(vcpu))
 			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) |
 				 ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
-		if (!cpus_have_final_cap(ARM64_HAS_WFXT))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
 		break;
 	case SYS_ID_AA64DFR0_EL1:
 		/* Set PMUver to the required version */
@@ -1391,12 +1383,6 @@ static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64
 		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
 				  pmuver_to_perfmon(vcpu_pmuver(vcpu)));
 		break;
-	case SYS_ID_AA64MMFR2_EL1:
-		val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
-		break;
-	case SYS_ID_MMFR4_EL1:
-		val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
-		break;
 	}
 
 	return val;
@@ -1490,6 +1476,20 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
+static u64 read_sanitised_id_mmfr4_el1(struct kvm_vcpu *vcpu,
+				       const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* CCIDX is not supported */
+	val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
+
+	return val;
+}
+
 static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 					  const struct sys_reg_desc *rd)
 {
@@ -1516,6 +1516,25 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 
 	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
 
+	if (kvm_vgic_global_state.type == VGIC_V3) {
+		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
+		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
+	}
+
+	return val;
+}
+
+static u64 read_sanitised_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+					  const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* SME is not supported */
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
+
 	return val;
 }
 
@@ -1638,6 +1657,34 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	return pmuver_update(vcpu, rd, val, perfmon_to_pmuver(perfmon), valid_pmu);
 }
 
+static u64 read_sanitised_id_aa64isar2_el1(struct kvm_vcpu *vcpu,
+					   const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	if (!cpus_have_final_cap(ARM64_HAS_WFXT))
+		val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
+
+	return val;
+}
+
+static u64 read_sanitised_id_aa64mmfr2_el1(struct kvm_vcpu *vcpu,
+					   const struct sys_reg_desc *rd)
+{
+	u64 val;
+	u32 id = reg_to_encoding(rd);
+
+	val = read_sanitised_ftr_reg(id);
+
+	/* CCIDX is not supported */
+	val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
+
+	return val;
+}
+
 /*
  * cpufeature ID register user accessors
  *
@@ -2033,7 +2080,13 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	AA32_ID_SANITISED(ID_ISAR3_EL1),
 	AA32_ID_SANITISED(ID_ISAR4_EL1),
 	AA32_ID_SANITISED(ID_ISAR5_EL1),
-	AA32_ID_SANITISED(ID_MMFR4_EL1),
+	{ SYS_DESC(SYS_ID_MMFR4_EL1),
+	  .access     = access_id_reg,
+	  .get_user   = get_id_reg,
+	  .set_user   = set_id_reg,
+	  .visibility = aa32_id_visibility,
+	  .reset      = read_sanitised_id_mmfr4_el1,
+	  .val        = 0, },
 	AA32_ID_SANITISED(ID_ISAR6_EL1),
 
 	/* CRm=3 */
@@ -2054,7 +2107,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	  .set_user = set_id_reg,
 	  .reset = read_sanitised_id_aa64pfr0_el1,
 	  .val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
-	ID_SANITISED(ID_AA64PFR1_EL1),
+	{ SYS_DESC(SYS_ID_AA64PFR1_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64pfr1_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
 	ID_SANITISED(ID_AA64ZFR0_EL1),
@@ -2080,7 +2138,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	/* CRm=6 */
 	ID_SANITISED(ID_AA64ISAR0_EL1),
 	ID_SANITISED(ID_AA64ISAR1_EL1),
-	ID_SANITISED(ID_AA64ISAR2_EL1),
+	{ SYS_DESC(SYS_ID_AA64ISAR2_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64isar2_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(6,3),
 	ID_UNALLOCATED(6,4),
 	ID_UNALLOCATED(6,5),
@@ -2090,7 +2153,12 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	/* CRm=7 */
 	ID_SANITISED(ID_AA64MMFR0_EL1),
 	ID_SANITISED(ID_AA64MMFR1_EL1),
-	ID_SANITISED(ID_AA64MMFR2_EL1),
+	{ SYS_DESC(SYS_ID_AA64MMFR2_EL1),
+	  .access   = access_id_reg,
+	  .get_user = get_id_reg,
+	  .set_user = set_id_reg,
+	  .reset    = read_sanitised_id_aa64mmfr2_el1,
+	  .val      = 0, },
 	ID_UNALLOCATED(7,3),
 	ID_UNALLOCATED(7,4),
 	ID_UNALLOCATED(7,5),
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-02 22:14   ` Suraj Jitindar Singh
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  -1 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

With per guest ID registers, MTE settings from userspace can be stored in
its corresponding ID register.

No functional change intended.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
 arch/arm64/kvm/arm.c              | 11 ++++++++++-
 arch/arm64/kvm/sys_regs.c         |  5 +++++
 3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7b0f43373dbe..861997a14ba1 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -226,9 +226,7 @@ struct kvm_arch {
 	 */
 #define KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER	0
 	/* Memory Tagging Extension enabled for the guest */
-#define KVM_ARCH_FLAG_MTE_ENABLED			1
-	/* At least one vCPU has ran in the VM */
-#define KVM_ARCH_FLAG_HAS_RAN_ONCE			2
+#define KVM_ARCH_FLAG_HAS_RAN_ONCE			1
 	/*
 	 * The following two bits are used to indicate the guest's EL1
 	 * register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
@@ -236,22 +234,22 @@ struct kvm_arch {
 	 * Otherwise, the guest's EL1 register width has not yet been
 	 * determined yet.
 	 */
-#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
-#define KVM_ARCH_FLAG_EL1_32BIT				4
+#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		2
+#define KVM_ARCH_FLAG_EL1_32BIT				3
 	/* PSCI SYSTEM_SUSPEND enabled for the guest */
-#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		5
+#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		4
 	/* VM counter offset */
-#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			6
+#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			5
 	/* Timer PPIs made immutable */
-#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
+#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		6
 	/* SMCCC filter initialized for the VM */
-#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
+#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		7
 	/*
 	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
 	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
 	 * userspace for VCPUs without PMU.
 	 */
-#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
+#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		8
 
 	unsigned long flags;
 
@@ -1112,7 +1110,8 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
 
 #define kvm_has_mte(kvm)					\
 	(system_supports_mte() &&				\
-	 test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &(kvm)->arch.flags))
+	 FIELD_GET(ID_AA64PFR1_EL1_MTE_MASK,			\
+		   IDREG(kvm, SYS_ID_AA64PFR1_EL1)))
 
 #define kvm_supports_32bit_el0()				\
 	(system_supports_32bit_el0() &&				\
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index ca18c09ccf82..6fc4190559d1 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		if (!system_supports_mte() || kvm->created_vcpus) {
 			r = -EINVAL;
 		} else {
+			u64 val;
+
+			/* Protects the idregs against modification */
+			mutex_lock(&kvm->arch.config_lock);
+
+			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
+			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
+			IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
+
+			mutex_unlock(&kvm->arch.config_lock);
 			r = 0;
-			set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
 		}
 		mutex_unlock(&kvm->lock);
 		break;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 59f8adda47fa..8cffb82dd10d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3672,6 +3672,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
+
+	/* MTE disabled by default even when supported */
+	val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
+	IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-02 22:14     ` Suraj Jitindar Singh
  0 siblings, 0 replies; 40+ messages in thread
From: Suraj Jitindar Singh @ 2023-06-02 22:14 UTC (permalink / raw)
  To: jingzhangos
  Cc: alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	maz, oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba,
	will, sjitindarsingh, Suraj Jitindar Singh

With per guest ID registers, MTE settings from userspace can be stored in
its corresponding ID register.

No functional change intended.

Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
---
 arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
 arch/arm64/kvm/arm.c              | 11 ++++++++++-
 arch/arm64/kvm/sys_regs.c         |  5 +++++
 3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7b0f43373dbe..861997a14ba1 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -226,9 +226,7 @@ struct kvm_arch {
 	 */
 #define KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER	0
 	/* Memory Tagging Extension enabled for the guest */
-#define KVM_ARCH_FLAG_MTE_ENABLED			1
-	/* At least one vCPU has ran in the VM */
-#define KVM_ARCH_FLAG_HAS_RAN_ONCE			2
+#define KVM_ARCH_FLAG_HAS_RAN_ONCE			1
 	/*
 	 * The following two bits are used to indicate the guest's EL1
 	 * register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
@@ -236,22 +234,22 @@ struct kvm_arch {
 	 * Otherwise, the guest's EL1 register width has not yet been
 	 * determined yet.
 	 */
-#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
-#define KVM_ARCH_FLAG_EL1_32BIT				4
+#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		2
+#define KVM_ARCH_FLAG_EL1_32BIT				3
 	/* PSCI SYSTEM_SUSPEND enabled for the guest */
-#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		5
+#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		4
 	/* VM counter offset */
-#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			6
+#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			5
 	/* Timer PPIs made immutable */
-#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
+#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		6
 	/* SMCCC filter initialized for the VM */
-#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
+#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		7
 	/*
 	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
 	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
 	 * userspace for VCPUs without PMU.
 	 */
-#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
+#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		8
 
 	unsigned long flags;
 
@@ -1112,7 +1110,8 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
 
 #define kvm_has_mte(kvm)					\
 	(system_supports_mte() &&				\
-	 test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &(kvm)->arch.flags))
+	 FIELD_GET(ID_AA64PFR1_EL1_MTE_MASK,			\
+		   IDREG(kvm, SYS_ID_AA64PFR1_EL1)))
 
 #define kvm_supports_32bit_el0()				\
 	(system_supports_32bit_el0() &&				\
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index ca18c09ccf82..6fc4190559d1 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		if (!system_supports_mte() || kvm->created_vcpus) {
 			r = -EINVAL;
 		} else {
+			u64 val;
+
+			/* Protects the idregs against modification */
+			mutex_lock(&kvm->arch.config_lock);
+
+			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
+			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
+			IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
+
+			mutex_unlock(&kvm->arch.config_lock);
 			r = 0;
-			set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
 		}
 		mutex_unlock(&kvm->lock);
 		break;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 59f8adda47fa..8cffb82dd10d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3672,6 +3672,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
 		idreg++;
 		id = reg_to_encoding(idreg);
 	}
+
+	/* MTE disabled by default even when supported */
+	val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
+	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
+	IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
 }
 
 int __init kvm_sys_reg_table_init(void)
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02 17:15     ` Jing Zhang
@ 2023-06-02 22:27       ` Jitindar Singh, Suraj
  -1 siblings, 0 replies; 40+ messages in thread
From: Jitindar Singh, Suraj @ 2023-06-02 22:27 UTC (permalink / raw)
  To: jingzhangos, kvm, kvmarm, linux-arm-kernel, maz, oupton
  Cc: james.morse, suzuki.poulose, rananta, tabba, pbonzini,
	alexandru.elisei, will, reijiw

On Fri, 2023-06-02 at 10:15 -0700, Jing Zhang wrote:
> Hi Suraj,
> 
> Let's continue the problem here you raised in the v9.
> For the SVE example, the problem would be gone by enabling the
> writable for SVE field? Since we are going to enable the writable for
> all ID regs one by one later.
> The double checking for CSV[2|3] is gone in this series.
> 
> Thanks,
> Jing

Hi Jing,

Correct. We need to in theory set the writable bit for all the fields
we're manipulating in kvm_arm_read_id_reg(). I think I also ran into
issues with the GIC bits.

I guess for per vcpu values this raises the question of what it means
to set them at a VM level (although I doubt anyone is setting these
heterogeneously in practise).

Thanks
- Suraj

> 
> On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com>
> wrote:
> > 
> > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > specific to ID register.
> > 
> > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      |   2 +-
> >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-----
> > ----
> >  3 files changed, 203 insertions(+), 91 deletions(-)
> > 
> > diff --git a/arch/arm64/include/asm/cpufeature.h
> > b/arch/arm64/include/asm/cpufeature.h
> > index 6bf013fb110d..dc769c2eb7a4 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64
> > mmfr1)
> >         return 8;
> >  }
> > 
> > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > new, s64 cur);
> >  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
> > 
> >  extern struct arm64_ftr_override id_aa64mmfr1_override;
> > diff --git a/arch/arm64/kernel/cpufeature.c
> > b/arch/arm64/kernel/cpufeature.c
> > index 7d7128c65161..3317a7b6deac 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct
> > arm64_ftr_bits *ftrp, s64 reg,
> >         return reg;
> >  }
> > 
> > -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp,
> > s64 new,
> > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > new,
> >                                 s64 cur)
> >  {
> >         s64 ret = 0;
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 1a534e0fc4ca..50d4e25f42d3 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -41,6 +41,7 @@
> >   * 64bit interface.
> >   */
> > 
> > +static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd, u64 val);
> >  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32
> > encoding);
> >  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
> > 
> > @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct
> > kvm_vcpu *vcpu,
> >         return true;
> >  }
> > 
> > +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct
> > arm64_ftr_bits *ftrp,
> > +                                   s64 new, s64 cur)
> > +{
> > +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> > +
> > +       /* Some features have different safe value type in KVM than
> > host features */
> > +       switch (id) {
> > +       case SYS_ID_AA64DFR0_EL1:
> > +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > +               break;
> > +       case SYS_ID_DFR0_EL1:
> > +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > +               break;
> > +       }
> > +
> > +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> > +}
> > +
> > +/**
> > + * arm64_check_features() - Check if a feature register value
> > constitutes
> > + * a subset of features indicated by the idreg's KVM sanitised
> > limit.
> > + *
> > + * This function will check if each feature field of @val is the
> > "safe" value
> > + * against idreg's KVM sanitised limit return from reset()
> > callback.
> > + * If a field value in @val is the same as the one in limit, it is
> > always
> > + * considered the safe value regardless For register fields that
> > are not in
> > + * writable, only the value in limit is considered the safe value.
> > + *
> > + * Return: 0 if all the fields are safe. Otherwise, return
> > negative errno.
> > + */
> > +static int arm64_check_features(struct kvm_vcpu *vcpu,
> > +                               const struct sys_reg_desc *rd,
> > +                               u64 val)
> > +{
> > +       const struct arm64_ftr_reg *ftr_reg;
> > +       const struct arm64_ftr_bits *ftrp = NULL;
> > +       u32 id = reg_to_encoding(rd);
> > +       u64 writable_mask = rd->val;
> > +       u64 limit = 0;
> > +       u64 mask = 0;
> > +
> > +       /* For hidden and unallocated idregs without reset, only
> > val = 0 is allowed. */
> > +       if (rd->reset) {
> > +               limit = rd->reset(vcpu, rd);
> > +               ftr_reg = get_arm64_ftr_reg(id);
> > +               if (!ftr_reg)
> > +                       return -EINVAL;
> > +               ftrp = ftr_reg->ftr_bits;
> > +       }
> > +
> > +       for (; ftrp && ftrp->width; ftrp++) {
> > +               s64 f_val, f_lim, safe_val;
> > +               u64 ftr_mask;
> > +
> > +               ftr_mask = arm64_ftr_mask(ftrp);
> > +               if ((ftr_mask & writable_mask) != ftr_mask)
> > +                       continue;
> > +
> > +               f_val = arm64_ftr_value(ftrp, val);
> > +               f_lim = arm64_ftr_value(ftrp, limit);
> > +               mask |= ftr_mask;
> > +
> > +               if (f_val == f_lim)
> > +                       safe_val = f_val;
> > +               else
> > +                       safe_val = kvm_arm64_ftr_safe_value(id,
> > ftrp, f_val, f_lim);
> > +
> > +               if (safe_val != f_val)
> > +                       return -E2BIG;
> > +       }
> > +
> > +       /* For fields that are not writable, values in limit are
> > the safe values. */
> > +       if ((val & ~mask) != (limit & ~mask))
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> >  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> >  {
> >         if (kvm_vcpu_has_pmu(vcpu))
> > @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
> >         }
> >  }
> > 
> > -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool
> > valid_pmu)
> > +static int pmuver_update(struct kvm_vcpu *vcpu,
> > +                         const struct sys_reg_desc *rd,
> > +                         u64 val,
> > +                         u8 pmuver,
> > +                         bool valid_pmu)
> >  {
> > -       u64 val;
> > +       int ret;
> > +
> > +       ret = set_id_reg(vcpu, rd, val);
> > +       if (ret)
> > +               return ret;
> > 
> >         if (valid_pmu) {
> >                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> > @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu
> > *vcpu, u8 pmuver, bool valid_pmu)
> >                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > &vcpu->kvm->arch.flags,
> >                            pmuver ==
> > ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> >         }
> > +
> > +       return 0;
> >  }
> > 
> >  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu,
> > const struct sys_reg_desc *rd)
> > @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct
> > kvm_vcpu *vcpu, u32 encoding)
> >         case SYS_ID_AA64PFR0_EL1:
> >                 if (!vcpu_has_sve(vcpu))
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> >                 if (kvm_vgic_global_state.type == VGIC_V3) {
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> >                         val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> > @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct
> > kvm_vcpu *vcpu, u32 encoding)
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> >                 break;
> >         case SYS_ID_AA64DFR0_EL1:
> > -               /* Limit debug to ARMv8.0 */
> > -               val &=
> > ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> >                 /* Set PMUver to the required version */
> >                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> >                 val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> >                                   vcpu_pmuver(vcpu));
> > -               /* Hide SPE from guests */
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> >                 break;
> >         case SYS_ID_DFR0_EL1:
> >                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const
> > struct kvm_vcpu *vcpu,
> >         return REG_HIDDEN;
> >  }
> > 
> > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > -                              const struct sys_reg_desc *rd,
> > -                              u64 val)
> > +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > +                                         const struct sys_reg_desc
> > *rd)
> >  {
> > -       u64 new_val = val;
> > -       u8 csv2, csv3;
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > 
> > +       val = read_sanitised_ftr_reg(id);
> >         /*
> > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long
> > as
> > -        * it doesn't promise more than what is actually provided
> > (the
> > -        * guest could otherwise be covered in ectoplasmic
> > residue).
> > +        * The default is to expose CSV2 == 1 if the HW isn't
> > affected.
> > +        * Although this is a per-CPU feature, we make it global
> > because
> > +        * asymmetric systems are just a nuisance.
> > +        *
> > +        * Userspace can override this as long as it doesn't
> > promise
> > +        * the impossible.
> >          */
> > -       csv2 = cpuid_feature_extract_unsigned_field(val,
> > ID_AA64PFR0_EL1_CSV2_SHIFT);
> > -       if (csv2 > 1 ||
> > -           (csv2 && arm64_get_spectre_v2_state() !=
> > SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > +               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > +       }
> > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > +               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > +       }
> > 
> > -       /* Same thing for CSV3 */
> > -       csv3 = cpuid_feature_extract_unsigned_field(val,
> > ID_AA64PFR0_EL1_CSV3_SHIFT);
> > -       if (csv3 > 1 ||
> > -           (csv3 && arm64_get_meltdown_state() !=
> > SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > 
> > -       /* We can only differ with CSV[23], and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> > -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> > -       if (val)
> > -               return -EINVAL;
> > +       return val;
> > +}
> > 
> > -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> > -       return 0;
> > +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                         const struct sys_reg_desc
> > *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /* Limit debug to ARMv8.0 */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > +       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance
> > to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > +       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > +                         kvm_arm_pmu_get_pmuver_limit());
> > +       /* Hide SPE from guests */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > +
> > +       return val;
> >  }
> > 
> >  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct
> > kvm_vcpu *vcpu,
> >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> >                 return -EINVAL;
> > 
> > -       /* We can only differ with PMUver, and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > -       if (val)
> > -               return -EINVAL;
> > +       if (!valid_pmu) {
> > +               /*
> > +                * Ignore the PMUVer field in @val. The PMUVer
> > would be determined
> > +                * by arch flags bit
> > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > +                */
> > +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> > +                                  IDREG(vcpu->kvm,
> > SYS_ID_AA64DFR0_EL1));
> > +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> > +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> > pmuver);
> > +       }
> > 
> > -       pmuver_update(vcpu, pmuver, valid_pmu);
> > -       return 0;
> > +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> > +}
> > +
> > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                     const struct sys_reg_desc
> > *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance
> > to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > kvm_arm_pmu_get_pmuver_limit());
> > +
> > +       return val;
> >  }
> > 
> >  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu
> > *vcpu,
> >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> >                 return -EINVAL;
> > 
> > -       /* We can only differ with PerfMon, and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > -       if (val)
> > -               return -EINVAL;
> > +       if (!valid_pmu) {
> > +               /*
> > +                * Ignore the PerfMon field in @val. The PerfMon
> > would be determined
> > +                * by arch flags bit
> > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > +                */
> > +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> > +                                   IDREG(vcpu->kvm,
> > SYS_ID_DFR0_EL1));
> > +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> > +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> > perfmon);
> > +       }
> > 
> > -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> > -       return 0;
> > +       return pmuver_update(vcpu, rd, val,
> > perfmon_to_pmuver(perfmon), valid_pmu);
> >  }
> > 
> >  /*
> > @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu
> > *vcpu, const struct sys_reg_desc *rd,
> >  static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd,
> >                       u64 val)
> >  {
> > -       /* This is what we mean by invariant: you can't change it.
> > */
> > -       if (val != read_id_reg(vcpu, rd))
> > -               return -EINVAL;
> > +       u32 id = reg_to_encoding(rd);
> > +       int ret = 0;
> > 
> > -       return 0;
> > +       ret = arm64_check_features(vcpu, rd, val);
> > +       if (!ret)
> > +               IDREG(vcpu->kvm, id) = val;
> > +
> > +       return ret;
> >  }
> > 
> >  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd,
> > @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc
> > sys_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, },
> > +       { 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,
> > +         .reset = read_sanitised_id_dfr0_el1,
> > +         .val = ID_DFR0_EL1_PerfMon_MASK, },
> >         ID_HIDDEN(ID_AFR0_EL1),
> >         AA32_ID_SANITISED(ID_MMFR0_EL1),
> >         AA32_ID_SANITISED(ID_MMFR1_EL1),
> > @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc
> > sys_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,
> > },
> > +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> > +         .access = access_id_reg,
> > +         .get_user = get_id_reg,
> > +         .set_user = set_id_reg,
> > +         .reset = read_sanitised_id_aa64pfr0_el1,
> > +         .val = ID_AA64PFR0_EL1_CSV2_MASK |
> > ID_AA64PFR0_EL1_CSV3_MASK, },
> >         ID_SANITISED(ID_AA64PFR1_EL1),
> >         ID_UNALLOCATED(4,2),
> >         ID_UNALLOCATED(4,3),
> > @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc
> > sys_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,
> > },
> > +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> > +         .access = access_id_reg,
> > +         .get_user = get_id_reg,
> > +         .set_user = set_id_aa64dfr0_el1,
> > +         .reset = read_sanitised_id_aa64dfr0_el1,
> > +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
> >         ID_SANITISED(ID_AA64DFR1_EL1),
> >         ID_UNALLOCATED(5,2),
> >         ID_UNALLOCATED(5,3),
> > @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
> >                 idreg++;
> >                 id = reg_to_encoding(idreg);
> >         }
> > -
> > -       /*
> > -        * The default is to expose CSV2 == 1 if the HW isn't
> > affected.
> > -        * Although this is a per-CPU feature, we make it global
> > because
> > -        * asymmetric systems are just a nuisance.
> > -        *
> > -        * Userspace can override this as long as it doesn't
> > promise
> > -        * the impossible.
> > -        */
> > -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> > -
> > -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > -       }
> > -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > -       }
> > -
> > -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> > -       /*
> > -        * Initialise the default PMUver before there is a chance
> > to
> > -        * create an actual PMU.
> > -        */
> > -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> > -
> > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > -       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > -                         kvm_arm_pmu_get_pmuver_limit());
> > -
> > -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
> >  }
> > 
> >  int __init kvm_sys_reg_table_init(void)
> > --
> > 2.41.0.rc0.172.g3f132b7071-goog
> > 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-02 22:27       ` Jitindar Singh, Suraj
  0 siblings, 0 replies; 40+ messages in thread
From: Jitindar Singh, Suraj @ 2023-06-02 22:27 UTC (permalink / raw)
  To: jingzhangos, kvm, kvmarm, linux-arm-kernel, maz, oupton
  Cc: james.morse, suzuki.poulose, rananta, tabba, pbonzini,
	alexandru.elisei, will, reijiw

On Fri, 2023-06-02 at 10:15 -0700, Jing Zhang wrote:
> Hi Suraj,
> 
> Let's continue the problem here you raised in the v9.
> For the SVE example, the problem would be gone by enabling the
> writable for SVE field? Since we are going to enable the writable for
> all ID regs one by one later.
> The double checking for CSV[2|3] is gone in this series.
> 
> Thanks,
> Jing

Hi Jing,

Correct. We need to in theory set the writable bit for all the fields
we're manipulating in kvm_arm_read_id_reg(). I think I also ran into
issues with the GIC bits.

I guess for per vcpu values this raises the question of what it means
to set them at a VM level (although I doubt anyone is setting these
heterogeneously in practise).

Thanks
- Suraj

> 
> On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com>
> wrote:
> > 
> > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > specific to ID register.
> > 
> > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      |   2 +-
> >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-----
> > ----
> >  3 files changed, 203 insertions(+), 91 deletions(-)
> > 
> > diff --git a/arch/arm64/include/asm/cpufeature.h
> > b/arch/arm64/include/asm/cpufeature.h
> > index 6bf013fb110d..dc769c2eb7a4 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64
> > mmfr1)
> >         return 8;
> >  }
> > 
> > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > new, s64 cur);
> >  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
> > 
> >  extern struct arm64_ftr_override id_aa64mmfr1_override;
> > diff --git a/arch/arm64/kernel/cpufeature.c
> > b/arch/arm64/kernel/cpufeature.c
> > index 7d7128c65161..3317a7b6deac 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct
> > arm64_ftr_bits *ftrp, s64 reg,
> >         return reg;
> >  }
> > 
> > -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp,
> > s64 new,
> > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > new,
> >                                 s64 cur)
> >  {
> >         s64 ret = 0;
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 1a534e0fc4ca..50d4e25f42d3 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -41,6 +41,7 @@
> >   * 64bit interface.
> >   */
> > 
> > +static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd, u64 val);
> >  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32
> > encoding);
> >  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
> > 
> > @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct
> > kvm_vcpu *vcpu,
> >         return true;
> >  }
> > 
> > +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct
> > arm64_ftr_bits *ftrp,
> > +                                   s64 new, s64 cur)
> > +{
> > +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> > +
> > +       /* Some features have different safe value type in KVM than
> > host features */
> > +       switch (id) {
> > +       case SYS_ID_AA64DFR0_EL1:
> > +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > +               break;
> > +       case SYS_ID_DFR0_EL1:
> > +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > +               break;
> > +       }
> > +
> > +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> > +}
> > +
> > +/**
> > + * arm64_check_features() - Check if a feature register value
> > constitutes
> > + * a subset of features indicated by the idreg's KVM sanitised
> > limit.
> > + *
> > + * This function will check if each feature field of @val is the
> > "safe" value
> > + * against idreg's KVM sanitised limit return from reset()
> > callback.
> > + * If a field value in @val is the same as the one in limit, it is
> > always
> > + * considered the safe value regardless For register fields that
> > are not in
> > + * writable, only the value in limit is considered the safe value.
> > + *
> > + * Return: 0 if all the fields are safe. Otherwise, return
> > negative errno.
> > + */
> > +static int arm64_check_features(struct kvm_vcpu *vcpu,
> > +                               const struct sys_reg_desc *rd,
> > +                               u64 val)
> > +{
> > +       const struct arm64_ftr_reg *ftr_reg;
> > +       const struct arm64_ftr_bits *ftrp = NULL;
> > +       u32 id = reg_to_encoding(rd);
> > +       u64 writable_mask = rd->val;
> > +       u64 limit = 0;
> > +       u64 mask = 0;
> > +
> > +       /* For hidden and unallocated idregs without reset, only
> > val = 0 is allowed. */
> > +       if (rd->reset) {
> > +               limit = rd->reset(vcpu, rd);
> > +               ftr_reg = get_arm64_ftr_reg(id);
> > +               if (!ftr_reg)
> > +                       return -EINVAL;
> > +               ftrp = ftr_reg->ftr_bits;
> > +       }
> > +
> > +       for (; ftrp && ftrp->width; ftrp++) {
> > +               s64 f_val, f_lim, safe_val;
> > +               u64 ftr_mask;
> > +
> > +               ftr_mask = arm64_ftr_mask(ftrp);
> > +               if ((ftr_mask & writable_mask) != ftr_mask)
> > +                       continue;
> > +
> > +               f_val = arm64_ftr_value(ftrp, val);
> > +               f_lim = arm64_ftr_value(ftrp, limit);
> > +               mask |= ftr_mask;
> > +
> > +               if (f_val == f_lim)
> > +                       safe_val = f_val;
> > +               else
> > +                       safe_val = kvm_arm64_ftr_safe_value(id,
> > ftrp, f_val, f_lim);
> > +
> > +               if (safe_val != f_val)
> > +                       return -E2BIG;
> > +       }
> > +
> > +       /* For fields that are not writable, values in limit are
> > the safe values. */
> > +       if ((val & ~mask) != (limit & ~mask))
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> >  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> >  {
> >         if (kvm_vcpu_has_pmu(vcpu))
> > @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
> >         }
> >  }
> > 
> > -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool
> > valid_pmu)
> > +static int pmuver_update(struct kvm_vcpu *vcpu,
> > +                         const struct sys_reg_desc *rd,
> > +                         u64 val,
> > +                         u8 pmuver,
> > +                         bool valid_pmu)
> >  {
> > -       u64 val;
> > +       int ret;
> > +
> > +       ret = set_id_reg(vcpu, rd, val);
> > +       if (ret)
> > +               return ret;
> > 
> >         if (valid_pmu) {
> >                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> > @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu
> > *vcpu, u8 pmuver, bool valid_pmu)
> >                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > &vcpu->kvm->arch.flags,
> >                            pmuver ==
> > ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> >         }
> > +
> > +       return 0;
> >  }
> > 
> >  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu,
> > const struct sys_reg_desc *rd)
> > @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct
> > kvm_vcpu *vcpu, u32 encoding)
> >         case SYS_ID_AA64PFR0_EL1:
> >                 if (!vcpu_has_sve(vcpu))
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> >                 if (kvm_vgic_global_state.type == VGIC_V3) {
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> >                         val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> > @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct
> > kvm_vcpu *vcpu, u32 encoding)
> >                         val &=
> > ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> >                 break;
> >         case SYS_ID_AA64DFR0_EL1:
> > -               /* Limit debug to ARMv8.0 */
> > -               val &=
> > ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> >                 /* Set PMUver to the required version */
> >                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> >                 val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> >                                   vcpu_pmuver(vcpu));
> > -               /* Hide SPE from guests */
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> >                 break;
> >         case SYS_ID_DFR0_EL1:
> >                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const
> > struct kvm_vcpu *vcpu,
> >         return REG_HIDDEN;
> >  }
> > 
> > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > -                              const struct sys_reg_desc *rd,
> > -                              u64 val)
> > +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > +                                         const struct sys_reg_desc
> > *rd)
> >  {
> > -       u64 new_val = val;
> > -       u8 csv2, csv3;
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > 
> > +       val = read_sanitised_ftr_reg(id);
> >         /*
> > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long
> > as
> > -        * it doesn't promise more than what is actually provided
> > (the
> > -        * guest could otherwise be covered in ectoplasmic
> > residue).
> > +        * The default is to expose CSV2 == 1 if the HW isn't
> > affected.
> > +        * Although this is a per-CPU feature, we make it global
> > because
> > +        * asymmetric systems are just a nuisance.
> > +        *
> > +        * Userspace can override this as long as it doesn't
> > promise
> > +        * the impossible.
> >          */
> > -       csv2 = cpuid_feature_extract_unsigned_field(val,
> > ID_AA64PFR0_EL1_CSV2_SHIFT);
> > -       if (csv2 > 1 ||
> > -           (csv2 && arm64_get_spectre_v2_state() !=
> > SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > +               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > +       }
> > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > +               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > +       }
> > 
> > -       /* Same thing for CSV3 */
> > -       csv3 = cpuid_feature_extract_unsigned_field(val,
> > ID_AA64PFR0_EL1_CSV3_SHIFT);
> > -       if (csv3 > 1 ||
> > -           (csv3 && arm64_get_meltdown_state() !=
> > SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > 
> > -       /* We can only differ with CSV[23], and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> > -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> > -       if (val)
> > -               return -EINVAL;
> > +       return val;
> > +}
> > 
> > -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> > -       return 0;
> > +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                         const struct sys_reg_desc
> > *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /* Limit debug to ARMv8.0 */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > +       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance
> > to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > +       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > +                         kvm_arm_pmu_get_pmuver_limit());
> > +       /* Hide SPE from guests */
> > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > +
> > +       return val;
> >  }
> > 
> >  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct
> > kvm_vcpu *vcpu,
> >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> >                 return -EINVAL;
> > 
> > -       /* We can only differ with PMUver, and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > -       if (val)
> > -               return -EINVAL;
> > +       if (!valid_pmu) {
> > +               /*
> > +                * Ignore the PMUVer field in @val. The PMUVer
> > would be determined
> > +                * by arch flags bit
> > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > +                */
> > +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> > +                                  IDREG(vcpu->kvm,
> > SYS_ID_AA64DFR0_EL1));
> > +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> > +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> > pmuver);
> > +       }
> > 
> > -       pmuver_update(vcpu, pmuver, valid_pmu);
> > -       return 0;
> > +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> > +}
> > +
> > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                     const struct sys_reg_desc
> > *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance
> > to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > kvm_arm_pmu_get_pmuver_limit());
> > +
> > +       return val;
> >  }
> > 
> >  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu
> > *vcpu,
> >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> >                 return -EINVAL;
> > 
> > -       /* We can only differ with PerfMon, and anything else is an
> > error */
> > -       val ^= read_id_reg(vcpu, rd);
> > -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > -       if (val)
> > -               return -EINVAL;
> > +       if (!valid_pmu) {
> > +               /*
> > +                * Ignore the PerfMon field in @val. The PerfMon
> > would be determined
> > +                * by arch flags bit
> > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > +                */
> > +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> > +                                   IDREG(vcpu->kvm,
> > SYS_ID_DFR0_EL1));
> > +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> > +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> > perfmon);
> > +       }
> > 
> > -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> > -       return 0;
> > +       return pmuver_update(vcpu, rd, val,
> > perfmon_to_pmuver(perfmon), valid_pmu);
> >  }
> > 
> >  /*
> > @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu
> > *vcpu, const struct sys_reg_desc *rd,
> >  static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd,
> >                       u64 val)
> >  {
> > -       /* This is what we mean by invariant: you can't change it.
> > */
> > -       if (val != read_id_reg(vcpu, rd))
> > -               return -EINVAL;
> > +       u32 id = reg_to_encoding(rd);
> > +       int ret = 0;
> > 
> > -       return 0;
> > +       ret = arm64_check_features(vcpu, rd, val);
> > +       if (!ret)
> > +               IDREG(vcpu->kvm, id) = val;
> > +
> > +       return ret;
> >  }
> > 
> >  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct
> > sys_reg_desc *rd,
> > @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc
> > sys_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, },
> > +       { 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,
> > +         .reset = read_sanitised_id_dfr0_el1,
> > +         .val = ID_DFR0_EL1_PerfMon_MASK, },
> >         ID_HIDDEN(ID_AFR0_EL1),
> >         AA32_ID_SANITISED(ID_MMFR0_EL1),
> >         AA32_ID_SANITISED(ID_MMFR1_EL1),
> > @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc
> > sys_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,
> > },
> > +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> > +         .access = access_id_reg,
> > +         .get_user = get_id_reg,
> > +         .set_user = set_id_reg,
> > +         .reset = read_sanitised_id_aa64pfr0_el1,
> > +         .val = ID_AA64PFR0_EL1_CSV2_MASK |
> > ID_AA64PFR0_EL1_CSV3_MASK, },
> >         ID_SANITISED(ID_AA64PFR1_EL1),
> >         ID_UNALLOCATED(4,2),
> >         ID_UNALLOCATED(4,3),
> > @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc
> > sys_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,
> > },
> > +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> > +         .access = access_id_reg,
> > +         .get_user = get_id_reg,
> > +         .set_user = set_id_aa64dfr0_el1,
> > +         .reset = read_sanitised_id_aa64dfr0_el1,
> > +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
> >         ID_SANITISED(ID_AA64DFR1_EL1),
> >         ID_UNALLOCATED(5,2),
> >         ID_UNALLOCATED(5,3),
> > @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
> >                 idreg++;
> >                 id = reg_to_encoding(idreg);
> >         }
> > -
> > -       /*
> > -        * The default is to expose CSV2 == 1 if the HW isn't
> > affected.
> > -        * Although this is a per-CPU feature, we make it global
> > because
> > -        * asymmetric systems are just a nuisance.
> > -        *
> > -        * Userspace can override this as long as it doesn't
> > promise
> > -        * the impossible.
> > -        */
> > -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> > -
> > -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > -       }
> > -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > -               val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > -       }
> > -
> > -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> > -       /*
> > -        * Initialise the default PMUver before there is a chance
> > to
> > -        * create an actual PMU.
> > -        */
> > -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> > -
> > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > -       val |=
> > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > -                         kvm_arm_pmu_get_pmuver_limit());
> > -
> > -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
> >  }
> > 
> >  int __init kvm_sys_reg_table_init(void)
> > --
> > 2.41.0.rc0.172.g3f132b7071-goog
> > 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02 19:21     ` Jitindar Singh, Suraj
@ 2023-06-03  0:03       ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-03  0:03 UTC (permalink / raw)
  To: Jitindar Singh, Suraj
  Cc: kvm, kvmarm, linux-arm-kernel, maz, oupton, james.morse,
	suzuki.poulose, rananta, tabba, pbonzini, alexandru.elisei, will,
	reijiw

On Fri, Jun 2, 2023 at 12:22 PM Jitindar Singh, Suraj
<surajjs@amazon.com> wrote:
>
> On Fri, 2023-06-02 at 00:51 +0000, Jing Zhang wrote:
> > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > specific to ID register.
> >
> > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      |   2 +-
> >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-------
> > --
> >  3 files changed, 203 insertions(+), 91 deletions(-)
> >
> >
> > +
> > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                     const struct sys_reg_desc *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > kvm_arm_pmu_get_pmuver_limit());
>
> Maybe it's never possible, but does this need a:
> pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit()) ?
Yes, will fix it and also update the comment above it.
>
> > +
> > +       return val;
> >  }
> >
> >
> Thanks
> - Suraj
Thanks,
Jing

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-03  0:03       ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-03  0:03 UTC (permalink / raw)
  To: Jitindar Singh, Suraj
  Cc: kvm, kvmarm, linux-arm-kernel, maz, oupton, james.morse,
	suzuki.poulose, rananta, tabba, pbonzini, alexandru.elisei, will,
	reijiw

On Fri, Jun 2, 2023 at 12:22 PM Jitindar Singh, Suraj
<surajjs@amazon.com> wrote:
>
> On Fri, 2023-06-02 at 00:51 +0000, Jing Zhang wrote:
> > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > specific to ID register.
> >
> > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      |   2 +-
> >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-------
> > --
> >  3 files changed, 203 insertions(+), 91 deletions(-)
> >
> >
> > +
> > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > +                                     const struct sys_reg_desc *rd)
> > +{
> > +       u64 val;
> > +       u32 id = reg_to_encoding(rd);
> > +
> > +       val = read_sanitised_ftr_reg(id);
> > +       /*
> > +        * Initialise the default PMUver before there is a chance to
> > +        * create an actual PMU.
> > +        */
> > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > kvm_arm_pmu_get_pmuver_limit());
>
> Maybe it's never possible, but does this need a:
> pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit()) ?
Yes, will fix it and also update the comment above it.
>
> > +
> > +       return val;
> >  }
> >
> >
> Thanks
> - Suraj
Thanks,
Jing

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
  2023-06-02 22:27       ` Jitindar Singh, Suraj
@ 2023-06-03  0:08         ` Jing Zhang
  -1 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-03  0:08 UTC (permalink / raw)
  To: Jitindar Singh, Suraj
  Cc: kvm, kvmarm, linux-arm-kernel, maz, oupton, james.morse,
	suzuki.poulose, rananta, tabba, pbonzini, alexandru.elisei, will,
	reijiw

Hi Suraj,

On Fri, Jun 2, 2023 at 3:28 PM Jitindar Singh, Suraj <surajjs@amazon.com> wrote:
>
> On Fri, 2023-06-02 at 10:15 -0700, Jing Zhang wrote:
> > Hi Suraj,
> >
> > Let's continue the problem here you raised in the v9.
> > For the SVE example, the problem would be gone by enabling the
> > writable for SVE field? Since we are going to enable the writable for
> > all ID regs one by one later.
> > The double checking for CSV[2|3] is gone in this series.
> >
> > Thanks,
> > Jing
>
> Hi Jing,
>
> Correct. We need to in theory set the writable bit for all the fields
> we're manipulating in kvm_arm_read_id_reg(). I think I also ran into
> issues with the GIC bits.
Let's see how people like to fix the issue, by applying your patch or
just enabling writable for those fields.
>
> I guess for per vcpu values this raises the question of what it means
> to set them at a VM level (although I doubt anyone is setting these
> heterogeneously in practise).
For now, per vcpu values are handled on the fly. The VM-scope idregs
would save the latest write from the userspace.
>
> Thanks
> - Suraj
>
Thanks,
Jing
> >
> > On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com>
> > wrote:
> > >
> > > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > > specific to ID register.
> > >
> > > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > > ---
> > >  arch/arm64/include/asm/cpufeature.h |   1 +
> > >  arch/arm64/kernel/cpufeature.c      |   2 +-
> > >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-----
> > > ----
> > >  3 files changed, 203 insertions(+), 91 deletions(-)
> > >
> > > diff --git a/arch/arm64/include/asm/cpufeature.h
> > > b/arch/arm64/include/asm/cpufeature.h
> > > index 6bf013fb110d..dc769c2eb7a4 100644
> > > --- a/arch/arm64/include/asm/cpufeature.h
> > > +++ b/arch/arm64/include/asm/cpufeature.h
> > > @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64
> > > mmfr1)
> > >         return 8;
> > >  }
> > >
> > > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > > new, s64 cur);
> > >  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
> > >
> > >  extern struct arm64_ftr_override id_aa64mmfr1_override;
> > > diff --git a/arch/arm64/kernel/cpufeature.c
> > > b/arch/arm64/kernel/cpufeature.c
> > > index 7d7128c65161..3317a7b6deac 100644
> > > --- a/arch/arm64/kernel/cpufeature.c
> > > +++ b/arch/arm64/kernel/cpufeature.c
> > > @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct
> > > arm64_ftr_bits *ftrp, s64 reg,
> > >         return reg;
> > >  }
> > >
> > > -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp,
> > > s64 new,
> > > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > > new,
> > >                                 s64 cur)
> > >  {
> > >         s64 ret = 0;
> > > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > > index 1a534e0fc4ca..50d4e25f42d3 100644
> > > --- a/arch/arm64/kvm/sys_regs.c
> > > +++ b/arch/arm64/kvm/sys_regs.c
> > > @@ -41,6 +41,7 @@
> > >   * 64bit interface.
> > >   */
> > >
> > > +static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd, u64 val);
> > >  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32
> > > encoding);
> > >  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
> > >
> > > @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct
> > > kvm_vcpu *vcpu,
> > >         return true;
> > >  }
> > >
> > > +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct
> > > arm64_ftr_bits *ftrp,
> > > +                                   s64 new, s64 cur)
> > > +{
> > > +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> > > +
> > > +       /* Some features have different safe value type in KVM than
> > > host features */
> > > +       switch (id) {
> > > +       case SYS_ID_AA64DFR0_EL1:
> > > +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> > > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > > +               break;
> > > +       case SYS_ID_DFR0_EL1:
> > > +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> > > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > > +               break;
> > > +       }
> > > +
> > > +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> > > +}
> > > +
> > > +/**
> > > + * arm64_check_features() - Check if a feature register value
> > > constitutes
> > > + * a subset of features indicated by the idreg's KVM sanitised
> > > limit.
> > > + *
> > > + * This function will check if each feature field of @val is the
> > > "safe" value
> > > + * against idreg's KVM sanitised limit return from reset()
> > > callback.
> > > + * If a field value in @val is the same as the one in limit, it is
> > > always
> > > + * considered the safe value regardless For register fields that
> > > are not in
> > > + * writable, only the value in limit is considered the safe value.
> > > + *
> > > + * Return: 0 if all the fields are safe. Otherwise, return
> > > negative errno.
> > > + */
> > > +static int arm64_check_features(struct kvm_vcpu *vcpu,
> > > +                               const struct sys_reg_desc *rd,
> > > +                               u64 val)
> > > +{
> > > +       const struct arm64_ftr_reg *ftr_reg;
> > > +       const struct arm64_ftr_bits *ftrp = NULL;
> > > +       u32 id = reg_to_encoding(rd);
> > > +       u64 writable_mask = rd->val;
> > > +       u64 limit = 0;
> > > +       u64 mask = 0;
> > > +
> > > +       /* For hidden and unallocated idregs without reset, only
> > > val = 0 is allowed. */
> > > +       if (rd->reset) {
> > > +               limit = rd->reset(vcpu, rd);
> > > +               ftr_reg = get_arm64_ftr_reg(id);
> > > +               if (!ftr_reg)
> > > +                       return -EINVAL;
> > > +               ftrp = ftr_reg->ftr_bits;
> > > +       }
> > > +
> > > +       for (; ftrp && ftrp->width; ftrp++) {
> > > +               s64 f_val, f_lim, safe_val;
> > > +               u64 ftr_mask;
> > > +
> > > +               ftr_mask = arm64_ftr_mask(ftrp);
> > > +               if ((ftr_mask & writable_mask) != ftr_mask)
> > > +                       continue;
> > > +
> > > +               f_val = arm64_ftr_value(ftrp, val);
> > > +               f_lim = arm64_ftr_value(ftrp, limit);
> > > +               mask |= ftr_mask;
> > > +
> > > +               if (f_val == f_lim)
> > > +                       safe_val = f_val;
> > > +               else
> > > +                       safe_val = kvm_arm64_ftr_safe_value(id,
> > > ftrp, f_val, f_lim);
> > > +
> > > +               if (safe_val != f_val)
> > > +                       return -E2BIG;
> > > +       }
> > > +
> > > +       /* For fields that are not writable, values in limit are
> > > the safe values. */
> > > +       if ((val & ~mask) != (limit & ~mask))
> > > +               return -E2BIG;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> > >  {
> > >         if (kvm_vcpu_has_pmu(vcpu))
> > > @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
> > >         }
> > >  }
> > >
> > > -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool
> > > valid_pmu)
> > > +static int pmuver_update(struct kvm_vcpu *vcpu,
> > > +                         const struct sys_reg_desc *rd,
> > > +                         u64 val,
> > > +                         u8 pmuver,
> > > +                         bool valid_pmu)
> > >  {
> > > -       u64 val;
> > > +       int ret;
> > > +
> > > +       ret = set_id_reg(vcpu, rd, val);
> > > +       if (ret)
> > > +               return ret;
> > >
> > >         if (valid_pmu) {
> > >                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> > > @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu
> > > *vcpu, u8 pmuver, bool valid_pmu)
> > >                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > &vcpu->kvm->arch.flags,
> > >                            pmuver ==
> > > ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> > >         }
> > > +
> > > +       return 0;
> > >  }
> > >
> > >  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu,
> > > const struct sys_reg_desc *rd)
> > > @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct
> > > kvm_vcpu *vcpu, u32 encoding)
> > >         case SYS_ID_AA64PFR0_EL1:
> > >                 if (!vcpu_has_sve(vcpu))
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > >                 if (kvm_vgic_global_state.type == VGIC_V3) {
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> > >                         val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> > > @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct
> > > kvm_vcpu *vcpu, u32 encoding)
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> > >                 break;
> > >         case SYS_ID_AA64DFR0_EL1:
> > > -               /* Limit debug to ARMv8.0 */
> > > -               val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > >                 /* Set PMUver to the required version */
> > >                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > >                 val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > >                                   vcpu_pmuver(vcpu));
> > > -               /* Hide SPE from guests */
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > >                 break;
> > >         case SYS_ID_DFR0_EL1:
> > >                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const
> > > struct kvm_vcpu *vcpu,
> > >         return REG_HIDDEN;
> > >  }
> > >
> > > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > > -                              const struct sys_reg_desc *rd,
> > > -                              u64 val)
> > > +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                         const struct sys_reg_desc
> > > *rd)
> > >  {
> > > -       u64 new_val = val;
> > > -       u8 csv2, csv3;
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > >
> > > +       val = read_sanitised_ftr_reg(id);
> > >         /*
> > > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long
> > > as
> > > -        * it doesn't promise more than what is actually provided
> > > (the
> > > -        * guest could otherwise be covered in ectoplasmic
> > > residue).
> > > +        * The default is to expose CSV2 == 1 if the HW isn't
> > > affected.
> > > +        * Although this is a per-CPU feature, we make it global
> > > because
> > > +        * asymmetric systems are just a nuisance.
> > > +        *
> > > +        * Userspace can override this as long as it doesn't
> > > promise
> > > +        * the impossible.
> > >          */
> > > -       csv2 = cpuid_feature_extract_unsigned_field(val,
> > > ID_AA64PFR0_EL1_CSV2_SHIFT);
> > > -       if (csv2 > 1 ||
> > > -           (csv2 && arm64_get_spectre_v2_state() !=
> > > SPECTRE_UNAFFECTED))
> > > -               return -EINVAL;
> > > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > > +               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > > +       }
> > > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > > +               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > > +       }
> > >
> > > -       /* Same thing for CSV3 */
> > > -       csv3 = cpuid_feature_extract_unsigned_field(val,
> > > ID_AA64PFR0_EL1_CSV3_SHIFT);
> > > -       if (csv3 > 1 ||
> > > -           (csv3 && arm64_get_meltdown_state() !=
> > > SPECTRE_UNAFFECTED))
> > > -               return -EINVAL;
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > >
> > > -       /* We can only differ with CSV[23], and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> > > -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       return val;
> > > +}
> > >
> > > -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> > > -       return 0;
> > > +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                         const struct sys_reg_desc
> > > *rd)
> > > +{
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > > +
> > > +       val = read_sanitised_ftr_reg(id);
> > > +       /* Limit debug to ARMv8.0 */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > > +       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > > +       /*
> > > +        * Initialise the default PMUver before there is a chance
> > > to
> > > +        * create an actual PMU.
> > > +        */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > +       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > > +                         kvm_arm_pmu_get_pmuver_limit());
> > > +       /* Hide SPE from guests */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > > +
> > > +       return val;
> > >  }
> > >
> > >  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > > @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct
> > > kvm_vcpu *vcpu,
> > >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> > >                 return -EINVAL;
> > >
> > > -       /* We can only differ with PMUver, and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       if (!valid_pmu) {
> > > +               /*
> > > +                * Ignore the PMUVer field in @val. The PMUVer
> > > would be determined
> > > +                * by arch flags bit
> > > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > +                */
> > > +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> > > +                                  IDREG(vcpu->kvm,
> > > SYS_ID_AA64DFR0_EL1));
> > > +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> > > +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> > > pmuver);
> > > +       }
> > >
> > > -       pmuver_update(vcpu, pmuver, valid_pmu);
> > > -       return 0;
> > > +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> > > +}
> > > +
> > > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                     const struct sys_reg_desc
> > > *rd)
> > > +{
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > > +
> > > +       val = read_sanitised_ftr_reg(id);
> > > +       /*
> > > +        * Initialise the default PMUver before there is a chance
> > > to
> > > +        * create an actual PMU.
> > > +        */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > > kvm_arm_pmu_get_pmuver_limit());
> > > +
> > > +       return val;
> > >  }
> > >
> > >  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > > @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu
> > > *vcpu,
> > >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> > >                 return -EINVAL;
> > >
> > > -       /* We can only differ with PerfMon, and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       if (!valid_pmu) {
> > > +               /*
> > > +                * Ignore the PerfMon field in @val. The PerfMon
> > > would be determined
> > > +                * by arch flags bit
> > > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > +                */
> > > +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> > > +                                   IDREG(vcpu->kvm,
> > > SYS_ID_DFR0_EL1));
> > > +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> > > +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> > > perfmon);
> > > +       }
> > >
> > > -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> > > -       return 0;
> > > +       return pmuver_update(vcpu, rd, val,
> > > perfmon_to_pmuver(perfmon), valid_pmu);
> > >  }
> > >
> > >  /*
> > > @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu
> > > *vcpu, const struct sys_reg_desc *rd,
> > >  static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd,
> > >                       u64 val)
> > >  {
> > > -       /* This is what we mean by invariant: you can't change it.
> > > */
> > > -       if (val != read_id_reg(vcpu, rd))
> > > -               return -EINVAL;
> > > +       u32 id = reg_to_encoding(rd);
> > > +       int ret = 0;
> > >
> > > -       return 0;
> > > +       ret = arm64_check_features(vcpu, rd, val);
> > > +       if (!ret)
> > > +               IDREG(vcpu->kvm, id) = val;
> > > +
> > > +       return ret;
> > >  }
> > >
> > >  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd,
> > > @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc
> > > sys_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, },
> > > +       { 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,
> > > +         .reset = read_sanitised_id_dfr0_el1,
> > > +         .val = ID_DFR0_EL1_PerfMon_MASK, },
> > >         ID_HIDDEN(ID_AFR0_EL1),
> > >         AA32_ID_SANITISED(ID_MMFR0_EL1),
> > >         AA32_ID_SANITISED(ID_MMFR1_EL1),
> > > @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc
> > > sys_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,
> > > },
> > > +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> > > +         .access = access_id_reg,
> > > +         .get_user = get_id_reg,
> > > +         .set_user = set_id_reg,
> > > +         .reset = read_sanitised_id_aa64pfr0_el1,
> > > +         .val = ID_AA64PFR0_EL1_CSV2_MASK |
> > > ID_AA64PFR0_EL1_CSV3_MASK, },
> > >         ID_SANITISED(ID_AA64PFR1_EL1),
> > >         ID_UNALLOCATED(4,2),
> > >         ID_UNALLOCATED(4,3),
> > > @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc
> > > sys_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,
> > > },
> > > +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> > > +         .access = access_id_reg,
> > > +         .get_user = get_id_reg,
> > > +         .set_user = set_id_aa64dfr0_el1,
> > > +         .reset = read_sanitised_id_aa64dfr0_el1,
> > > +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
> > >         ID_SANITISED(ID_AA64DFR1_EL1),
> > >         ID_UNALLOCATED(5,2),
> > >         ID_UNALLOCATED(5,3),
> > > @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
> > >                 idreg++;
> > >                 id = reg_to_encoding(idreg);
> > >         }
> > > -
> > > -       /*
> > > -        * The default is to expose CSV2 == 1 if the HW isn't
> > > affected.
> > > -        * Although this is a per-CPU feature, we make it global
> > > because
> > > -        * asymmetric systems are just a nuisance.
> > > -        *
> > > -        * Userspace can override this as long as it doesn't
> > > promise
> > > -        * the impossible.
> > > -        */
> > > -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> > > -
> > > -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > > -       }
> > > -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > > -       }
> > > -
> > > -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> > > -       /*
> > > -        * Initialise the default PMUver before there is a chance
> > > to
> > > -        * create an actual PMU.
> > > -        */
> > > -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> > > -
> > > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > -       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > > -                         kvm_arm_pmu_get_pmuver_limit());
> > > -
> > > -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
> > >  }
> > >
> > >  int __init kvm_sys_reg_table_init(void)
> > > --
> > > 2.41.0.rc0.172.g3f132b7071-goog
> > >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3
@ 2023-06-03  0:08         ` Jing Zhang
  0 siblings, 0 replies; 40+ messages in thread
From: Jing Zhang @ 2023-06-03  0:08 UTC (permalink / raw)
  To: Jitindar Singh, Suraj
  Cc: kvm, kvmarm, linux-arm-kernel, maz, oupton, james.morse,
	suzuki.poulose, rananta, tabba, pbonzini, alexandru.elisei, will,
	reijiw

Hi Suraj,

On Fri, Jun 2, 2023 at 3:28 PM Jitindar Singh, Suraj <surajjs@amazon.com> wrote:
>
> On Fri, 2023-06-02 at 10:15 -0700, Jing Zhang wrote:
> > Hi Suraj,
> >
> > Let's continue the problem here you raised in the v9.
> > For the SVE example, the problem would be gone by enabling the
> > writable for SVE field? Since we are going to enable the writable for
> > all ID regs one by one later.
> > The double checking for CSV[2|3] is gone in this series.
> >
> > Thanks,
> > Jing
>
> Hi Jing,
>
> Correct. We need to in theory set the writable bit for all the fields
> we're manipulating in kvm_arm_read_id_reg(). I think I also ran into
> issues with the GIC bits.
Let's see how people like to fix the issue, by applying your patch or
just enabling writable for those fields.
>
> I guess for per vcpu values this raises the question of what it means
> to set them at a VM level (although I doubt anyone is setting these
> heterogeneously in practise).
For now, per vcpu values are handled on the fly. The VM-scope idregs
would save the latest write from the userspace.
>
> Thanks
> - Suraj
>
Thanks,
Jing
> >
> > On Thu, Jun 1, 2023 at 5:51 PM Jing Zhang <jingzhangos@google.com>
> > wrote:
> > >
> > > Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> > > ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> > > specific to ID register.
> > >
> > > Signed-off-by: Jing Zhang <jingzhangos@google.com>
> > > ---
> > >  arch/arm64/include/asm/cpufeature.h |   1 +
> > >  arch/arm64/kernel/cpufeature.c      |   2 +-
> > >  arch/arm64/kvm/sys_regs.c           | 291 +++++++++++++++++++-----
> > > ----
> > >  3 files changed, 203 insertions(+), 91 deletions(-)
> > >
> > > diff --git a/arch/arm64/include/asm/cpufeature.h
> > > b/arch/arm64/include/asm/cpufeature.h
> > > index 6bf013fb110d..dc769c2eb7a4 100644
> > > --- a/arch/arm64/include/asm/cpufeature.h
> > > +++ b/arch/arm64/include/asm/cpufeature.h
> > > @@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64
> > > mmfr1)
> > >         return 8;
> > >  }
> > >
> > > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > > new, s64 cur);
> > >  struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
> > >
> > >  extern struct arm64_ftr_override id_aa64mmfr1_override;
> > > diff --git a/arch/arm64/kernel/cpufeature.c
> > > b/arch/arm64/kernel/cpufeature.c
> > > index 7d7128c65161..3317a7b6deac 100644
> > > --- a/arch/arm64/kernel/cpufeature.c
> > > +++ b/arch/arm64/kernel/cpufeature.c
> > > @@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct
> > > arm64_ftr_bits *ftrp, s64 reg,
> > >         return reg;
> > >  }
> > >
> > > -static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp,
> > > s64 new,
> > > +s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64
> > > new,
> > >                                 s64 cur)
> > >  {
> > >         s64 ret = 0;
> > > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > > index 1a534e0fc4ca..50d4e25f42d3 100644
> > > --- a/arch/arm64/kvm/sys_regs.c
> > > +++ b/arch/arm64/kvm/sys_regs.c
> > > @@ -41,6 +41,7 @@
> > >   * 64bit interface.
> > >   */
> > >
> > > +static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd, u64 val);
> > >  static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32
> > > encoding);
> > >  static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
> > >
> > > @@ -1194,6 +1195,86 @@ static bool access_arch_timer(struct
> > > kvm_vcpu *vcpu,
> > >         return true;
> > >  }
> > >
> > > +static s64 kvm_arm64_ftr_safe_value(u32 id, const struct
> > > arm64_ftr_bits *ftrp,
> > > +                                   s64 new, s64 cur)
> > > +{
> > > +       struct arm64_ftr_bits kvm_ftr = *ftrp;
> > > +
> > > +       /* Some features have different safe value type in KVM than
> > > host features */
> > > +       switch (id) {
> > > +       case SYS_ID_AA64DFR0_EL1:
> > > +               if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
> > > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > > +               break;
> > > +       case SYS_ID_DFR0_EL1:
> > > +               if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
> > > +                       kvm_ftr.type = FTR_LOWER_SAFE;
> > > +               break;
> > > +       }
> > > +
> > > +       return arm64_ftr_safe_value(&kvm_ftr, new, cur);
> > > +}
> > > +
> > > +/**
> > > + * arm64_check_features() - Check if a feature register value
> > > constitutes
> > > + * a subset of features indicated by the idreg's KVM sanitised
> > > limit.
> > > + *
> > > + * This function will check if each feature field of @val is the
> > > "safe" value
> > > + * against idreg's KVM sanitised limit return from reset()
> > > callback.
> > > + * If a field value in @val is the same as the one in limit, it is
> > > always
> > > + * considered the safe value regardless For register fields that
> > > are not in
> > > + * writable, only the value in limit is considered the safe value.
> > > + *
> > > + * Return: 0 if all the fields are safe. Otherwise, return
> > > negative errno.
> > > + */
> > > +static int arm64_check_features(struct kvm_vcpu *vcpu,
> > > +                               const struct sys_reg_desc *rd,
> > > +                               u64 val)
> > > +{
> > > +       const struct arm64_ftr_reg *ftr_reg;
> > > +       const struct arm64_ftr_bits *ftrp = NULL;
> > > +       u32 id = reg_to_encoding(rd);
> > > +       u64 writable_mask = rd->val;
> > > +       u64 limit = 0;
> > > +       u64 mask = 0;
> > > +
> > > +       /* For hidden and unallocated idregs without reset, only
> > > val = 0 is allowed. */
> > > +       if (rd->reset) {
> > > +               limit = rd->reset(vcpu, rd);
> > > +               ftr_reg = get_arm64_ftr_reg(id);
> > > +               if (!ftr_reg)
> > > +                       return -EINVAL;
> > > +               ftrp = ftr_reg->ftr_bits;
> > > +       }
> > > +
> > > +       for (; ftrp && ftrp->width; ftrp++) {
> > > +               s64 f_val, f_lim, safe_val;
> > > +               u64 ftr_mask;
> > > +
> > > +               ftr_mask = arm64_ftr_mask(ftrp);
> > > +               if ((ftr_mask & writable_mask) != ftr_mask)
> > > +                       continue;
> > > +
> > > +               f_val = arm64_ftr_value(ftrp, val);
> > > +               f_lim = arm64_ftr_value(ftrp, limit);
> > > +               mask |= ftr_mask;
> > > +
> > > +               if (f_val == f_lim)
> > > +                       safe_val = f_val;
> > > +               else
> > > +                       safe_val = kvm_arm64_ftr_safe_value(id,
> > > ftrp, f_val, f_lim);
> > > +
> > > +               if (safe_val != f_val)
> > > +                       return -E2BIG;
> > > +       }
> > > +
> > > +       /* For fields that are not writable, values in limit are
> > > the safe values. */
> > > +       if ((val & ~mask) != (limit & ~mask))
> > > +               return -E2BIG;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> > >  {
> > >         if (kvm_vcpu_has_pmu(vcpu))
> > > @@ -1231,9 +1312,17 @@ static u8 pmuver_to_perfmon(u8 pmuver)
> > >         }
> > >  }
> > >
> > > -static void pmuver_update(struct kvm_vcpu *vcpu, u8 pmuver, bool
> > > valid_pmu)
> > > +static int pmuver_update(struct kvm_vcpu *vcpu,
> > > +                         const struct sys_reg_desc *rd,
> > > +                         u64 val,
> > > +                         u8 pmuver,
> > > +                         bool valid_pmu)
> > >  {
> > > -       u64 val;
> > > +       int ret;
> > > +
> > > +       ret = set_id_reg(vcpu, rd, val);
> > > +       if (ret)
> > > +               return ret;
> > >
> > >         if (valid_pmu) {
> > >                 val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> > > @@ -1249,6 +1338,8 @@ static void pmuver_update(struct kvm_vcpu
> > > *vcpu, u8 pmuver, bool valid_pmu)
> > >                 assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > &vcpu->kvm->arch.flags,
> > >                            pmuver ==
> > > ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> > >         }
> > > +
> > > +       return 0;
> > >  }
> > >
> > >  static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu,
> > > const struct sys_reg_desc *rd)
> > > @@ -1264,7 +1355,6 @@ static u64 kvm_arm_read_id_reg(const struct
> > > kvm_vcpu *vcpu, u32 encoding)
> > >         case SYS_ID_AA64PFR0_EL1:
> > >                 if (!vcpu_has_sve(vcpu))
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > >                 if (kvm_vgic_global_state.type == VGIC_V3) {
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> > >                         val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> > > @@ -1291,15 +1381,10 @@ static u64 kvm_arm_read_id_reg(const struct
> > > kvm_vcpu *vcpu, u32 encoding)
> > >                         val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> > >                 break;
> > >         case SYS_ID_AA64DFR0_EL1:
> > > -               /* Limit debug to ARMv8.0 */
> > > -               val &=
> > > ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > >                 /* Set PMUver to the required version */
> > >                 val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > >                 val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > >                                   vcpu_pmuver(vcpu));
> > > -               /* Hide SPE from guests */
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > >                 break;
> > >         case SYS_ID_DFR0_EL1:
> > >                 val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > @@ -1398,38 +1483,56 @@ static unsigned int sve_visibility(const
> > > struct kvm_vcpu *vcpu,
> > >         return REG_HIDDEN;
> > >  }
> > >
> > > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > > -                              const struct sys_reg_desc *rd,
> > > -                              u64 val)
> > > +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                         const struct sys_reg_desc
> > > *rd)
> > >  {
> > > -       u64 new_val = val;
> > > -       u8 csv2, csv3;
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > >
> > > +       val = read_sanitised_ftr_reg(id);
> > >         /*
> > > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long
> > > as
> > > -        * it doesn't promise more than what is actually provided
> > > (the
> > > -        * guest could otherwise be covered in ectoplasmic
> > > residue).
> > > +        * The default is to expose CSV2 == 1 if the HW isn't
> > > affected.
> > > +        * Although this is a per-CPU feature, we make it global
> > > because
> > > +        * asymmetric systems are just a nuisance.
> > > +        *
> > > +        * Userspace can override this as long as it doesn't
> > > promise
> > > +        * the impossible.
> > >          */
> > > -       csv2 = cpuid_feature_extract_unsigned_field(val,
> > > ID_AA64PFR0_EL1_CSV2_SHIFT);
> > > -       if (csv2 > 1 ||
> > > -           (csv2 && arm64_get_spectre_v2_state() !=
> > > SPECTRE_UNAFFECTED))
> > > -               return -EINVAL;
> > > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > > +               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > > +       }
> > > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > > +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > > +               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > > +       }
> > >
> > > -       /* Same thing for CSV3 */
> > > -       csv3 = cpuid_feature_extract_unsigned_field(val,
> > > ID_AA64PFR0_EL1_CSV3_SHIFT);
> > > -       if (csv3 > 1 ||
> > > -           (csv3 && arm64_get_meltdown_state() !=
> > > SPECTRE_UNAFFECTED))
> > > -               return -EINVAL;
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> > >
> > > -       /* We can only differ with CSV[23], and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> > > -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       return val;
> > > +}
> > >
> > > -       IDREG(vcpu->kvm, reg_to_encoding(rd)) = new_val;
> > > -       return 0;
> > > +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                         const struct sys_reg_desc
> > > *rd)
> > > +{
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > > +
> > > +       val = read_sanitised_ftr_reg(id);
> > > +       /* Limit debug to ARMv8.0 */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> > > +       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> > > +       /*
> > > +        * Initialise the default PMUver before there is a chance
> > > to
> > > +        * create an actual PMU.
> > > +        */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > +       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > > +                         kvm_arm_pmu_get_pmuver_limit());
> > > +       /* Hide SPE from guests */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> > > +
> > > +       return val;
> > >  }
> > >
> > >  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > > @@ -1457,14 +1560,35 @@ static int set_id_aa64dfr0_el1(struct
> > > kvm_vcpu *vcpu,
> > >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> > >                 return -EINVAL;
> > >
> > > -       /* We can only differ with PMUver, and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       if (!valid_pmu) {
> > > +               /*
> > > +                * Ignore the PMUVer field in @val. The PMUVer
> > > would be determined
> > > +                * by arch flags bit
> > > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > +                */
> > > +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> > > +                                  IDREG(vcpu->kvm,
> > > SYS_ID_AA64DFR0_EL1));
> > > +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> > > +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> > > pmuver);
> > > +       }
> > >
> > > -       pmuver_update(vcpu, pmuver, valid_pmu);
> > > -       return 0;
> > > +       return pmuver_update(vcpu, rd, val, pmuver, valid_pmu);
> > > +}
> > > +
> > > +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > > +                                     const struct sys_reg_desc
> > > *rd)
> > > +{
> > > +       u64 val;
> > > +       u32 id = reg_to_encoding(rd);
> > > +
> > > +       val = read_sanitised_ftr_reg(id);
> > > +       /*
> > > +        * Initialise the default PMUver before there is a chance
> > > to
> > > +        * create an actual PMU.
> > > +        */
> > > +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> > > kvm_arm_pmu_get_pmuver_limit());
> > > +
> > > +       return val;
> > >  }
> > >
> > >  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> > > @@ -1493,14 +1617,18 @@ static int set_id_dfr0_el1(struct kvm_vcpu
> > > *vcpu,
> > >         if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> > >                 return -EINVAL;
> > >
> > > -       /* We can only differ with PerfMon, and anything else is an
> > > error */
> > > -       val ^= read_id_reg(vcpu, rd);
> > > -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> > > -       if (val)
> > > -               return -EINVAL;
> > > +       if (!valid_pmu) {
> > > +               /*
> > > +                * Ignore the PerfMon field in @val. The PerfMon
> > > would be determined
> > > +                * by arch flags bit
> > > KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> > > +                */
> > > +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> > > +                                   IDREG(vcpu->kvm,
> > > SYS_ID_DFR0_EL1));
> > > +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> > > +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> > > perfmon);
> > > +       }
> > >
> > > -       pmuver_update(vcpu, perfmon_to_pmuver(perfmon), valid_pmu);
> > > -       return 0;
> > > +       return pmuver_update(vcpu, rd, val,
> > > perfmon_to_pmuver(perfmon), valid_pmu);
> > >  }
> > >
> > >  /*
> > > @@ -1520,11 +1648,14 @@ static int get_id_reg(struct kvm_vcpu
> > > *vcpu, const struct sys_reg_desc *rd,
> > >  static int set_id_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd,
> > >                       u64 val)
> > >  {
> > > -       /* This is what we mean by invariant: you can't change it.
> > > */
> > > -       if (val != read_id_reg(vcpu, rd))
> > > -               return -EINVAL;
> > > +       u32 id = reg_to_encoding(rd);
> > > +       int ret = 0;
> > >
> > > -       return 0;
> > > +       ret = arm64_check_features(vcpu, rd, val);
> > > +       if (!ret)
> > > +               IDREG(vcpu->kvm, id) = val;
> > > +
> > > +       return ret;
> > >  }
> > >
> > >  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct
> > > sys_reg_desc *rd,
> > > @@ -1875,9 +2006,13 @@ static const struct sys_reg_desc
> > > sys_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, },
> > > +       { 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,
> > > +         .reset = read_sanitised_id_dfr0_el1,
> > > +         .val = ID_DFR0_EL1_PerfMon_MASK, },
> > >         ID_HIDDEN(ID_AFR0_EL1),
> > >         AA32_ID_SANITISED(ID_MMFR0_EL1),
> > >         AA32_ID_SANITISED(ID_MMFR1_EL1),
> > > @@ -1906,8 +2041,12 @@ static const struct sys_reg_desc
> > > sys_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,
> > > },
> > > +       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
> > > +         .access = access_id_reg,
> > > +         .get_user = get_id_reg,
> > > +         .set_user = set_id_reg,
> > > +         .reset = read_sanitised_id_aa64pfr0_el1,
> > > +         .val = ID_AA64PFR0_EL1_CSV2_MASK |
> > > ID_AA64PFR0_EL1_CSV3_MASK, },
> > >         ID_SANITISED(ID_AA64PFR1_EL1),
> > >         ID_UNALLOCATED(4,2),
> > >         ID_UNALLOCATED(4,3),
> > > @@ -1917,8 +2056,12 @@ static const struct sys_reg_desc
> > > sys_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,
> > > },
> > > +       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
> > > +         .access = access_id_reg,
> > > +         .get_user = get_id_reg,
> > > +         .set_user = set_id_aa64dfr0_el1,
> > > +         .reset = read_sanitised_id_aa64dfr0_el1,
> > > +         .val = ID_AA64DFR0_EL1_PMUVer_MASK, },
> > >         ID_SANITISED(ID_AA64DFR1_EL1),
> > >         ID_UNALLOCATED(5,2),
> > >         ID_UNALLOCATED(5,3),
> > > @@ -3454,38 +3597,6 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
> > >                 idreg++;
> > >                 id = reg_to_encoding(idreg);
> > >         }
> > > -
> > > -       /*
> > > -        * The default is to expose CSV2 == 1 if the HW isn't
> > > affected.
> > > -        * Although this is a per-CPU feature, we make it global
> > > because
> > > -        * asymmetric systems are just a nuisance.
> > > -        *
> > > -        * Userspace can override this as long as it doesn't
> > > promise
> > > -        * the impossible.
> > > -        */
> > > -       val = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
> > > -
> > > -       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> > > -       }
> > > -       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> > > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> > > -               val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> > > -       }
> > > -
> > > -       IDREG(kvm, SYS_ID_AA64PFR0_EL1) = val;
> > > -       /*
> > > -        * Initialise the default PMUver before there is a chance
> > > to
> > > -        * create an actual PMU.
> > > -        */
> > > -       val = IDREG(kvm, SYS_ID_AA64DFR0_EL1);
> > > -
> > > -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> > > -       val |=
> > > FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> > > -                         kvm_arm_pmu_get_pmuver_limit());
> > > -
> > > -       IDREG(kvm, SYS_ID_AA64DFR0_EL1) = val;
> > >  }
> > >
> > >  int __init kvm_sys_reg_table_init(void)
> > > --
> > > 2.41.0.rc0.172.g3f132b7071-goog
> > >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-02 22:14     ` Suraj Jitindar Singh
@ 2023-06-03  8:28       ` Marc Zyngier
  -1 siblings, 0 replies; 40+ messages in thread
From: Marc Zyngier @ 2023-06-03  8:28 UTC (permalink / raw)
  To: Suraj Jitindar Singh
  Cc: jingzhangos, alexandru.elisei, james.morse, kvm, kvmarm,
	linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Fri, 02 Jun 2023 23:14:47 +0100,
Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> 
> With per guest ID registers, MTE settings from userspace can be stored in
> its corresponding ID register.
> 
> No functional change intended.
> 
> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>  3 files changed, 25 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 7b0f43373dbe..861997a14ba1 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -226,9 +226,7 @@ struct kvm_arch {
>  	 */
>  #define KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER	0
>  	/* Memory Tagging Extension enabled for the guest */
> -#define KVM_ARCH_FLAG_MTE_ENABLED			1
> -	/* At least one vCPU has ran in the VM */
> -#define KVM_ARCH_FLAG_HAS_RAN_ONCE			2
> +#define KVM_ARCH_FLAG_HAS_RAN_ONCE			1
>  	/*
>  	 * The following two bits are used to indicate the guest's EL1
>  	 * register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
> @@ -236,22 +234,22 @@ struct kvm_arch {
>  	 * Otherwise, the guest's EL1 register width has not yet been
>  	 * determined yet.
>  	 */
> -#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
> -#define KVM_ARCH_FLAG_EL1_32BIT				4
> +#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		2
> +#define KVM_ARCH_FLAG_EL1_32BIT				3
>  	/* PSCI SYSTEM_SUSPEND enabled for the guest */
> -#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		5
> +#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		4
>  	/* VM counter offset */
> -#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			6
> +#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			5
>  	/* Timer PPIs made immutable */
> -#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
> +#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		6
>  	/* SMCCC filter initialized for the VM */
> -#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
> +#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		7
>  	/*
>  	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
>  	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
>  	 * userspace for VCPUs without PMU.
>  	 */
> -#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
> +#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		8
>  
>  	unsigned long flags;
>  
> @@ -1112,7 +1110,8 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
>  
>  #define kvm_has_mte(kvm)					\
>  	(system_supports_mte() &&				\
> -	 test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &(kvm)->arch.flags))
> +	 FIELD_GET(ID_AA64PFR1_EL1_MTE_MASK,			\
> +		   IDREG(kvm, SYS_ID_AA64PFR1_EL1)))
>  
>  #define kvm_supports_32bit_el0()				\
>  	(system_supports_32bit_el0() &&				\
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index ca18c09ccf82..6fc4190559d1 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>  		if (!system_supports_mte() || kvm->created_vcpus) {
>  			r = -EINVAL;
>  		} else {
> +			u64 val;
> +
> +			/* Protects the idregs against modification */
> +			mutex_lock(&kvm->arch.config_lock);
> +
> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);

The architecture specifies 3 versions of MTE in the published ARM ARM,
with a 4th coming up as part of the 2022 extensions. Why are you
actively crippling the MTE version presented to the guest, and
potentially introduce unexpected behaviours?

> +			IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
> +
> +			mutex_unlock(&kvm->arch.config_lock);
>  			r = 0;
> -			set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
>  		}
>  		mutex_unlock(&kvm->lock);
>  		break;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 59f8adda47fa..8cffb82dd10d 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -3672,6 +3672,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
>  		idreg++;
>  		id = reg_to_encoding(idreg);
>  	}
> +
> +	/* MTE disabled by default even when supported */
> +	val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> +	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
> +	IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
>  }
>  
>  int __init kvm_sys_reg_table_init(void)

Overall, I don't really see the point of such a change. It creates
more problems than it solves.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-03  8:28       ` Marc Zyngier
  0 siblings, 0 replies; 40+ messages in thread
From: Marc Zyngier @ 2023-06-03  8:28 UTC (permalink / raw)
  To: Suraj Jitindar Singh
  Cc: jingzhangos, alexandru.elisei, james.morse, kvm, kvmarm,
	linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Fri, 02 Jun 2023 23:14:47 +0100,
Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> 
> With per guest ID registers, MTE settings from userspace can be stored in
> its corresponding ID register.
> 
> No functional change intended.
> 
> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>  3 files changed, 25 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 7b0f43373dbe..861997a14ba1 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -226,9 +226,7 @@ struct kvm_arch {
>  	 */
>  #define KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER	0
>  	/* Memory Tagging Extension enabled for the guest */
> -#define KVM_ARCH_FLAG_MTE_ENABLED			1
> -	/* At least one vCPU has ran in the VM */
> -#define KVM_ARCH_FLAG_HAS_RAN_ONCE			2
> +#define KVM_ARCH_FLAG_HAS_RAN_ONCE			1
>  	/*
>  	 * The following two bits are used to indicate the guest's EL1
>  	 * register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
> @@ -236,22 +234,22 @@ struct kvm_arch {
>  	 * Otherwise, the guest's EL1 register width has not yet been
>  	 * determined yet.
>  	 */
> -#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
> -#define KVM_ARCH_FLAG_EL1_32BIT				4
> +#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		2
> +#define KVM_ARCH_FLAG_EL1_32BIT				3
>  	/* PSCI SYSTEM_SUSPEND enabled for the guest */
> -#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		5
> +#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED		4
>  	/* VM counter offset */
> -#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			6
> +#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			5
>  	/* Timer PPIs made immutable */
> -#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7
> +#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		6
>  	/* SMCCC filter initialized for the VM */
> -#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
> +#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		7
>  	/*
>  	 * AA64DFR0_EL1.PMUver was set as ID_AA64DFR0_EL1_PMUVer_IMP_DEF
>  	 * or DFR0_EL1.PerfMon was set as ID_DFR0_EL1_PerfMon_IMPDEF from
>  	 * userspace for VCPUs without PMU.
>  	 */
> -#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		9
> +#define KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU		8
>  
>  	unsigned long flags;
>  
> @@ -1112,7 +1110,8 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
>  
>  #define kvm_has_mte(kvm)					\
>  	(system_supports_mte() &&				\
> -	 test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &(kvm)->arch.flags))
> +	 FIELD_GET(ID_AA64PFR1_EL1_MTE_MASK,			\
> +		   IDREG(kvm, SYS_ID_AA64PFR1_EL1)))
>  
>  #define kvm_supports_32bit_el0()				\
>  	(system_supports_32bit_el0() &&				\
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index ca18c09ccf82..6fc4190559d1 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>  		if (!system_supports_mte() || kvm->created_vcpus) {
>  			r = -EINVAL;
>  		} else {
> +			u64 val;
> +
> +			/* Protects the idregs against modification */
> +			mutex_lock(&kvm->arch.config_lock);
> +
> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);

The architecture specifies 3 versions of MTE in the published ARM ARM,
with a 4th coming up as part of the 2022 extensions. Why are you
actively crippling the MTE version presented to the guest, and
potentially introduce unexpected behaviours?

> +			IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
> +
> +			mutex_unlock(&kvm->arch.config_lock);
>  			r = 0;
> -			set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
>  		}
>  		mutex_unlock(&kvm->lock);
>  		break;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 59f8adda47fa..8cffb82dd10d 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -3672,6 +3672,11 @@ void kvm_arm_init_id_regs(struct kvm *kvm)
>  		idreg++;
>  		id = reg_to_encoding(idreg);
>  	}
> +
> +	/* MTE disabled by default even when supported */
> +	val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> +	val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
> +	IDREG(kvm, SYS_ID_AA64PFR1_EL1) = val;
>  }
>  
>  int __init kvm_sys_reg_table_init(void)

Overall, I don't really see the point of such a change. It creates
more problems than it solves.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-03  8:28       ` Marc Zyngier
@ 2023-06-05 16:39         ` Cornelia Huck
  -1 siblings, 0 replies; 40+ messages in thread
From: Cornelia Huck @ 2023-06-05 16:39 UTC (permalink / raw)
  To: Marc Zyngier, Suraj Jitindar Singh
  Cc: jingzhangos, alexandru.elisei, james.morse, kvm, kvmarm,
	linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:

> On Fri, 02 Jun 2023 23:14:47 +0100,
> Suraj Jitindar Singh <surajjs@amazon.com> wrote:
>> 
>> With per guest ID registers, MTE settings from userspace can be stored in
>> its corresponding ID register.
>> 
>> No functional change intended.
>> 
>> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
>> ---
>>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>>  3 files changed, 25 insertions(+), 12 deletions(-)
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index ca18c09ccf82..6fc4190559d1 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>>  		if (!system_supports_mte() || kvm->created_vcpus) {
>>  			r = -EINVAL;
>>  		} else {
>> +			u64 val;
>> +
>> +			/* Protects the idregs against modification */
>> +			mutex_lock(&kvm->arch.config_lock);
>> +
>> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
>> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
>
> The architecture specifies 3 versions of MTE in the published ARM ARM,
> with a 4th coming up as part of the 2022 extensions.

Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
AA64PFR2?

> Why are you
> actively crippling the MTE version presented to the guest, and
> potentially introduce unexpected behaviours?

While the code does not look correct here, I think we'll need some way to
control which version of MTE is presented to the guest for compatibility
handling; does it make sense to control this per-cpu, or does it need to
be a vm-wide setting?


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-05 16:39         ` Cornelia Huck
  0 siblings, 0 replies; 40+ messages in thread
From: Cornelia Huck @ 2023-06-05 16:39 UTC (permalink / raw)
  To: Marc Zyngier, Suraj Jitindar Singh
  Cc: jingzhangos, alexandru.elisei, james.morse, kvm, kvmarm,
	linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:

> On Fri, 02 Jun 2023 23:14:47 +0100,
> Suraj Jitindar Singh <surajjs@amazon.com> wrote:
>> 
>> With per guest ID registers, MTE settings from userspace can be stored in
>> its corresponding ID register.
>> 
>> No functional change intended.
>> 
>> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
>> ---
>>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>>  3 files changed, 25 insertions(+), 12 deletions(-)
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index ca18c09ccf82..6fc4190559d1 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>>  		if (!system_supports_mte() || kvm->created_vcpus) {
>>  			r = -EINVAL;
>>  		} else {
>> +			u64 val;
>> +
>> +			/* Protects the idregs against modification */
>> +			mutex_lock(&kvm->arch.config_lock);
>> +
>> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
>> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
>
> The architecture specifies 3 versions of MTE in the published ARM ARM,
> with a 4th coming up as part of the 2022 extensions.

Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
AA64PFR2?

> Why are you
> actively crippling the MTE version presented to the guest, and
> potentially introduce unexpected behaviours?

While the code does not look correct here, I think we'll need some way to
control which version of MTE is presented to the guest for compatibility
handling; does it make sense to control this per-cpu, or does it need to
be a vm-wide setting?


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-05 16:39         ` Cornelia Huck
@ 2023-06-06 16:42           ` Marc Zyngier
  -1 siblings, 0 replies; 40+ messages in thread
From: Marc Zyngier @ 2023-06-06 16:42 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: Suraj Jitindar Singh, jingzhangos, alexandru.elisei, james.morse,
	kvm, kvmarm, linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Mon, 05 Jun 2023 17:39:50 +0100,
Cornelia Huck <cohuck@redhat.com> wrote:
> 
> On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
> 
> > On Fri, 02 Jun 2023 23:14:47 +0100,
> > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> >> 
> >> With per guest ID registers, MTE settings from userspace can be stored in
> >> its corresponding ID register.
> >> 
> >> No functional change intended.
> >> 
> >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> >> ---
> >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
> >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
> >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
> >>  3 files changed, 25 insertions(+), 12 deletions(-)
> >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> >> index ca18c09ccf82..6fc4190559d1 100644
> >> --- a/arch/arm64/kvm/arm.c
> >> +++ b/arch/arm64/kvm/arm.c
> >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
> >>  		if (!system_supports_mte() || kvm->created_vcpus) {
> >>  			r = -EINVAL;
> >>  		} else {
> >> +			u64 val;
> >> +
> >> +			/* Protects the idregs against modification */
> >> +			mutex_lock(&kvm->arch.config_lock);
> >> +
> >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
> >
> > The architecture specifies 3 versions of MTE in the published ARM ARM,
> > with a 4th coming up as part of the 2022 extensions.
> 
> Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
> AA64PFR2?

Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
train wreck, then it probably is one!

> 
> > Why are you
> > actively crippling the MTE version presented to the guest, and
> > potentially introduce unexpected behaviours?
> 
> While the code does not look correct here, I think we'll need some way to
> control which version of MTE is presented to the guest for compatibility
> handling; does it make sense to control this per-cpu, or does it need to
> be a vm-wide setting?

It absolutely needs to be VM-wide. Only having half the vcpus
supporting tags wouldn't make much sense.

But the problem is that the various versions of MTE are not
necessarily compatible, as MTE4 makes MTE3 optional (with a fallback
to MTE2)... There are more subtleties around the what instructions are
available in which mode, and whether the various subfeatures can be
configured or not.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-06 16:42           ` Marc Zyngier
  0 siblings, 0 replies; 40+ messages in thread
From: Marc Zyngier @ 2023-06-06 16:42 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: Suraj Jitindar Singh, jingzhangos, alexandru.elisei, james.morse,
	kvm, kvmarm, linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Mon, 05 Jun 2023 17:39:50 +0100,
Cornelia Huck <cohuck@redhat.com> wrote:
> 
> On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
> 
> > On Fri, 02 Jun 2023 23:14:47 +0100,
> > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> >> 
> >> With per guest ID registers, MTE settings from userspace can be stored in
> >> its corresponding ID register.
> >> 
> >> No functional change intended.
> >> 
> >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> >> ---
> >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
> >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
> >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
> >>  3 files changed, 25 insertions(+), 12 deletions(-)
> >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> >> index ca18c09ccf82..6fc4190559d1 100644
> >> --- a/arch/arm64/kvm/arm.c
> >> +++ b/arch/arm64/kvm/arm.c
> >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
> >>  		if (!system_supports_mte() || kvm->created_vcpus) {
> >>  			r = -EINVAL;
> >>  		} else {
> >> +			u64 val;
> >> +
> >> +			/* Protects the idregs against modification */
> >> +			mutex_lock(&kvm->arch.config_lock);
> >> +
> >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
> >
> > The architecture specifies 3 versions of MTE in the published ARM ARM,
> > with a 4th coming up as part of the 2022 extensions.
> 
> Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
> AA64PFR2?

Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
train wreck, then it probably is one!

> 
> > Why are you
> > actively crippling the MTE version presented to the guest, and
> > potentially introduce unexpected behaviours?
> 
> While the code does not look correct here, I think we'll need some way to
> control which version of MTE is presented to the guest for compatibility
> handling; does it make sense to control this per-cpu, or does it need to
> be a vm-wide setting?

It absolutely needs to be VM-wide. Only having half the vcpus
supporting tags wouldn't make much sense.

But the problem is that the various versions of MTE are not
necessarily compatible, as MTE4 makes MTE3 optional (with a fallback
to MTE2)... There are more subtleties around the what instructions are
available in which mode, and whether the various subfeatures can be
configured or not.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-06 16:42           ` Marc Zyngier
@ 2023-06-07 10:09             ` Cornelia Huck
  -1 siblings, 0 replies; 40+ messages in thread
From: Cornelia Huck @ 2023-06-07 10:09 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Suraj Jitindar Singh, jingzhangos, alexandru.elisei, james.morse,
	kvm, kvmarm, linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Tue, Jun 06 2023, Marc Zyngier <maz@kernel.org> wrote:

> On Mon, 05 Jun 2023 17:39:50 +0100,
> Cornelia Huck <cohuck@redhat.com> wrote:
>> 
>> On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
>> 
>> > On Fri, 02 Jun 2023 23:14:47 +0100,
>> > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
>> >> 
>> >> With per guest ID registers, MTE settings from userspace can be stored in
>> >> its corresponding ID register.
>> >> 
>> >> No functional change intended.
>> >> 
>> >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
>> >> ---
>> >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>> >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>> >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>> >>  3 files changed, 25 insertions(+), 12 deletions(-)
>> >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> >> index ca18c09ccf82..6fc4190559d1 100644
>> >> --- a/arch/arm64/kvm/arm.c
>> >> +++ b/arch/arm64/kvm/arm.c
>> >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>> >>  		if (!system_supports_mte() || kvm->created_vcpus) {
>> >>  			r = -EINVAL;
>> >>  		} else {
>> >> +			u64 val;
>> >> +
>> >> +			/* Protects the idregs against modification */
>> >> +			mutex_lock(&kvm->arch.config_lock);
>> >> +
>> >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
>> >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
>> >
>> > The architecture specifies 3 versions of MTE in the published ARM ARM,
>> > with a 4th coming up as part of the 2022 extensions.
>> 
>> Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
>> AA64PFR2?
>
> Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
> ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
> train wreck, then it probably is one!

With that many features, what could possibly go wrong!

>
>> 
>> > Why are you
>> > actively crippling the MTE version presented to the guest, and
>> > potentially introduce unexpected behaviours?
>> 
>> While the code does not look correct here, I think we'll need some way to
>> control which version of MTE is presented to the guest for compatibility
>> handling; does it make sense to control this per-cpu, or does it need to
>> be a vm-wide setting?
>
> It absolutely needs to be VM-wide. Only having half the vcpus
> supporting tags wouldn't make much sense.
>
> But the problem is that the various versions of MTE are not
> necessarily compatible, as MTE4 makes MTE3 optional (with a fallback
> to MTE2)... There are more subtleties around the what instructions are
> available in which mode, and whether the various subfeatures can be
> configured or not.

So I guess we'll have to expose all of that to userspace, so that it can
actually configure the various configurations that will surely show up
in the wild...


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-07 10:09             ` Cornelia Huck
  0 siblings, 0 replies; 40+ messages in thread
From: Cornelia Huck @ 2023-06-07 10:09 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Suraj Jitindar Singh, jingzhangos, alexandru.elisei, james.morse,
	kvm, kvmarm, linux-arm-kernel, oupton, pbonzini, rananta, reijiw,
	suzuki.poulose, tabba, will, sjitindarsingh

On Tue, Jun 06 2023, Marc Zyngier <maz@kernel.org> wrote:

> On Mon, 05 Jun 2023 17:39:50 +0100,
> Cornelia Huck <cohuck@redhat.com> wrote:
>> 
>> On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
>> 
>> > On Fri, 02 Jun 2023 23:14:47 +0100,
>> > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
>> >> 
>> >> With per guest ID registers, MTE settings from userspace can be stored in
>> >> its corresponding ID register.
>> >> 
>> >> No functional change intended.
>> >> 
>> >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
>> >> ---
>> >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
>> >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
>> >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
>> >>  3 files changed, 25 insertions(+), 12 deletions(-)
>> >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> >> index ca18c09ccf82..6fc4190559d1 100644
>> >> --- a/arch/arm64/kvm/arm.c
>> >> +++ b/arch/arm64/kvm/arm.c
>> >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>> >>  		if (!system_supports_mte() || kvm->created_vcpus) {
>> >>  			r = -EINVAL;
>> >>  		} else {
>> >> +			u64 val;
>> >> +
>> >> +			/* Protects the idregs against modification */
>> >> +			mutex_lock(&kvm->arch.config_lock);
>> >> +
>> >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
>> >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
>> >
>> > The architecture specifies 3 versions of MTE in the published ARM ARM,
>> > with a 4th coming up as part of the 2022 extensions.
>> 
>> Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
>> AA64PFR2?
>
> Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
> ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
> train wreck, then it probably is one!

With that many features, what could possibly go wrong!

>
>> 
>> > Why are you
>> > actively crippling the MTE version presented to the guest, and
>> > potentially introduce unexpected behaviours?
>> 
>> While the code does not look correct here, I think we'll need some way to
>> control which version of MTE is presented to the guest for compatibility
>> handling; does it make sense to control this per-cpu, or does it need to
>> be a vm-wide setting?
>
> It absolutely needs to be VM-wide. Only having half the vcpus
> supporting tags wouldn't make much sense.
>
> But the problem is that the various versions of MTE are not
> necessarily compatible, as MTE4 makes MTE3 optional (with a fallback
> to MTE2)... There are more subtleties around the what instructions are
> available in which mode, and whether the various subfeatures can be
> configured or not.

So I guess we'll have to expose all of that to userspace, so that it can
actually configure the various configurations that will surely show up
in the wild...


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
  2023-06-06 16:42           ` Marc Zyngier
@ 2023-06-08 17:57             ` Catalin Marinas
  -1 siblings, 0 replies; 40+ messages in thread
From: Catalin Marinas @ 2023-06-08 17:57 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Cornelia Huck, Suraj Jitindar Singh, jingzhangos,
	alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba, will,
	sjitindarsingh

On Tue, Jun 06, 2023 at 05:42:24PM +0100, Marc Zyngier wrote:
> On Mon, 05 Jun 2023 17:39:50 +0100,
> Cornelia Huck <cohuck@redhat.com> wrote:
> > On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
> > > On Fri, 02 Jun 2023 23:14:47 +0100,
> > > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> > >> 
> > >> With per guest ID registers, MTE settings from userspace can be stored in
> > >> its corresponding ID register.
> > >> 
> > >> No functional change intended.
> > >> 
> > >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> > >> ---
> > >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
> > >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
> > >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
> > >>  3 files changed, 25 insertions(+), 12 deletions(-)
> > >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > >> index ca18c09ccf82..6fc4190559d1 100644
> > >> --- a/arch/arm64/kvm/arm.c
> > >> +++ b/arch/arm64/kvm/arm.c
> > >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
> > >>  		if (!system_supports_mte() || kvm->created_vcpus) {
> > >>  			r = -EINVAL;
> > >>  		} else {
> > >> +			u64 val;
> > >> +
> > >> +			/* Protects the idregs against modification */
> > >> +			mutex_lock(&kvm->arch.config_lock);
> > >> +
> > >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> > >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
> > >
> > > The architecture specifies 3 versions of MTE in the published ARM ARM,
> > > with a 4th coming up as part of the 2022 extensions.
> > 
> > Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
> > AA64PFR2?
> 
> Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
> ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
> train wreck, then it probably is one!

I stared about an hour at that documentation and I think I got it (well,
for the next couple of hours). The disappearing of MTE_FEAT_ASYNC from
MTE2 is potentially problematic but the worst that can happen is that
async faults are simply not triggered (and TBH, those "faults" were not
that useful anyway). MTE4 without ASYM is defined in a weird way.
Basically there's no such thing as MTE4, just 2 and 3 (the latter
bringing in ASYM) with some extra features like store-only, stage 2
permission, canonical tag checking.

I don't think any of these new MTE extensions add any state that KVM
should care context-switch, so we should be fine. Does KVM limit the
maximum value of the ID field exposed to user? Some future MTE9 may add
new state, so better to be safe (I thought we handled these cases but
can't find it now).

It's also probably safe to disable MTE altogether if there's any
difference between all these fields on different CPUs (I don't think we
currently do, we just go for lower safe while ignoring MTE_frac, MTEX).

Regarding MTEX, I don't think Linux would ever make use of the canonical
tag checking. The enabling bit is unfortunately in TCR_EL1 which we
don't context-switch (and maybe cached in the TLB, I haven't checked the
latest spec).

-- 
Catalin

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE
@ 2023-06-08 17:57             ` Catalin Marinas
  0 siblings, 0 replies; 40+ messages in thread
From: Catalin Marinas @ 2023-06-08 17:57 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Cornelia Huck, Suraj Jitindar Singh, jingzhangos,
	alexandru.elisei, james.morse, kvm, kvmarm, linux-arm-kernel,
	oupton, pbonzini, rananta, reijiw, suzuki.poulose, tabba, will,
	sjitindarsingh

On Tue, Jun 06, 2023 at 05:42:24PM +0100, Marc Zyngier wrote:
> On Mon, 05 Jun 2023 17:39:50 +0100,
> Cornelia Huck <cohuck@redhat.com> wrote:
> > On Sat, Jun 03 2023, Marc Zyngier <maz@kernel.org> wrote:
> > > On Fri, 02 Jun 2023 23:14:47 +0100,
> > > Suraj Jitindar Singh <surajjs@amazon.com> wrote:
> > >> 
> > >> With per guest ID registers, MTE settings from userspace can be stored in
> > >> its corresponding ID register.
> > >> 
> > >> No functional change intended.
> > >> 
> > >> Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
> > >> ---
> > >>  arch/arm64/include/asm/kvm_host.h | 21 ++++++++++-----------
> > >>  arch/arm64/kvm/arm.c              | 11 ++++++++++-
> > >>  arch/arm64/kvm/sys_regs.c         |  5 +++++
> > >>  3 files changed, 25 insertions(+), 12 deletions(-)
> > >> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > >> index ca18c09ccf82..6fc4190559d1 100644
> > >> --- a/arch/arm64/kvm/arm.c
> > >> +++ b/arch/arm64/kvm/arm.c
> > >> @@ -80,8 +80,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
> > >>  		if (!system_supports_mte() || kvm->created_vcpus) {
> > >>  			r = -EINVAL;
> > >>  		} else {
> > >> +			u64 val;
> > >> +
> > >> +			/* Protects the idregs against modification */
> > >> +			mutex_lock(&kvm->arch.config_lock);
> > >> +
> > >> +			val = IDREG(kvm, SYS_ID_AA64PFR1_EL1);
> > >> +			val |= FIELD_PREP(ID_AA64PFR1_EL1_MTE_MASK, 1);
> > >
> > > The architecture specifies 3 versions of MTE in the published ARM ARM,
> > > with a 4th coming up as part of the 2022 extensions.
> > 
> > Is that the one that adds some more MTE<foo> bits in AA64PFR1 and
> > AA64PFR2?
> 
> Yeah, that. You get ID_AA64PFR1_EL1.{MTE,MTE_frac,MTEX}, plus
> ID_AA64PFR2_EL1.{MTEFAR,MTESTOREONLY,MTEPERM}... It this sounds like a
> train wreck, then it probably is one!

I stared about an hour at that documentation and I think I got it (well,
for the next couple of hours). The disappearing of MTE_FEAT_ASYNC from
MTE2 is potentially problematic but the worst that can happen is that
async faults are simply not triggered (and TBH, those "faults" were not
that useful anyway). MTE4 without ASYM is defined in a weird way.
Basically there's no such thing as MTE4, just 2 and 3 (the latter
bringing in ASYM) with some extra features like store-only, stage 2
permission, canonical tag checking.

I don't think any of these new MTE extensions add any state that KVM
should care context-switch, so we should be fine. Does KVM limit the
maximum value of the ID field exposed to user? Some future MTE9 may add
new state, so better to be safe (I thought we handled these cases but
can't find it now).

It's also probably safe to disable MTE altogether if there's any
difference between all these fields on different CPUs (I don't think we
currently do, we just go for lower safe while ignoring MTE_frac, MTEX).

Regarding MTEX, I don't think Linux would ever make use of the canonical
tag checking. The enabling bit is unfortunately in TCR_EL1 which we
don't context-switch (and maybe cached in the TLB, I haven't checked the
latest spec).

-- 
Catalin

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2023-06-08 17:57 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-02  0:51 [PATCH v11 0/5] Support writable CPU ID registers from userspace Jing Zhang
2023-06-02  0:51 ` Jing Zhang
2023-06-02  0:51 ` [PATCH v11 1/5] KVM: arm64: Save ID registers' sanitized value per guest Jing Zhang
2023-06-02  0:51   ` Jing Zhang
2023-06-02  0:51 ` [PATCH v11 2/5] KVM: arm64: Use per guest ID register for ID_AA64PFR0_EL1.[CSV2|CSV3] Jing Zhang
2023-06-02  0:51   ` Jing Zhang
2023-06-02  0:51 ` [PATCH v11 3/5] KVM: arm64: Use per guest ID register for ID_AA64DFR0_EL1.PMUVer Jing Zhang
2023-06-02  0:51   ` Jing Zhang
2023-06-02  0:51 ` [PATCH v11 4/5] KVM: arm64: Reuse fields of sys_reg_desc for idreg Jing Zhang
2023-06-02  0:51   ` Jing Zhang
2023-06-02  0:51 ` [PATCH v11 5/5] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3 Jing Zhang
2023-06-02  0:51   ` Jing Zhang
2023-06-02 17:15   ` Jing Zhang
2023-06-02 17:15     ` Jing Zhang
2023-06-02 22:27     ` Jitindar Singh, Suraj
2023-06-02 22:27       ` Jitindar Singh, Suraj
2023-06-03  0:08       ` Jing Zhang
2023-06-03  0:08         ` Jing Zhang
2023-06-02 19:21   ` Jitindar Singh, Suraj
2023-06-02 19:21     ` Jitindar Singh, Suraj
2023-06-03  0:03     ` Jing Zhang
2023-06-03  0:03       ` Jing Zhang
2023-06-02 22:14 ` [PATCH 0/3] RE: Support writable CPU ID registers from userspace [v11] Suraj Jitindar Singh
2023-06-02 22:14   ` Suraj Jitindar Singh
2023-06-02 22:14   ` [PATCH 1/3] KVM: arm64: Update id_reg limit value based on per vcpu flags Suraj Jitindar Singh
2023-06-02 22:14     ` Suraj Jitindar Singh
2023-06-02 22:14   ` [PATCH 2/3] KVM: arm64: Move non per vcpu flag checks out of kvm_arm_update_id_reg() Suraj Jitindar Singh
2023-06-02 22:14     ` Suraj Jitindar Singh
2023-06-02 22:14   ` [PATCH 3/3] KVM: arm64: Use per guest ID register for ID_AA64PFR1_EL1.MTE Suraj Jitindar Singh
2023-06-02 22:14     ` Suraj Jitindar Singh
2023-06-03  8:28     ` Marc Zyngier
2023-06-03  8:28       ` Marc Zyngier
2023-06-05 16:39       ` Cornelia Huck
2023-06-05 16:39         ` Cornelia Huck
2023-06-06 16:42         ` Marc Zyngier
2023-06-06 16:42           ` Marc Zyngier
2023-06-07 10:09           ` Cornelia Huck
2023-06-07 10:09             ` Cornelia Huck
2023-06-08 17:57           ` Catalin Marinas
2023-06-08 17:57             ` Catalin Marinas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.