Subject: [PATCH v1 11/26] KVM: arm64: Move definitions from sys_regs.c to sys_regs.h

From: Steffen Eiden <[email-masked]>
Date: Fri, 29 May 2026 17:55:44 +0200

Make kvm_sanitised_host_ftr_reg() and kvm_read_sanitised_id_reg()
available to enable code sharing with s390. Move some helper and ID
register macro definitions to the header file. No functional changes.

Signed-off-by: Steffen Eiden <[email-masked]>
---
 arch/arm64/kvm/sys_regs.c | 12 ++----
 arch/arm64/kvm/sys_regs.h | 87 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6522f9302967..46b24529ec70 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1852,7 +1852,7 @@ static u8 pmuver_to_perfmon(u8 pmuver)
 /*
  * Sanitise based on the host implementation.
  */
-static u64 kvm_sanitised_host_ftr_reg(u32 id)
+u64 kvm_sanitised_host_ftr_reg(u32 id)
 {
 	u64 val = read_sanitised_ftr_reg(id);
 
@@ -2039,8 +2039,8 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
 	return val;
 }
 
-static u64 kvm_read_sanitised_id_reg(struct kvm_vcpu *vcpu,
-				     const struct sys_reg_desc *r)
+u64 kvm_read_sanitised_id_reg(struct kvm_vcpu *vcpu,
+			      const struct sys_reg_desc *r)
 {
 	return __kvm_read_sanitised_id_reg(vcpu, r);
 }
@@ -2123,12 +2123,6 @@ static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
 	return id_visibility(vcpu, r);
 }
 
-static unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
-				   const struct sys_reg_desc *r)
-{
-	return REG_RAZ;
-}
-
 /* cpufeature ID register access trap handlers */
 
 static bool access_id_reg(struct kvm_vcpu *vcpu,
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index 2a983664220c..75d581050b09 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -108,6 +108,12 @@ struct sys_reg_desc {
 #define REG_RAZ			(1 << 1) /* RAZ from userspace and guest */
 #define REG_USER_WI		(1 << 2) /* WI from userspace only */
 
+static inline unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
+				   const struct sys_reg_desc *r)
+{
+	return REG_RAZ;
+}
+
 static __printf(2, 3)
 inline void print_sys_reg_msg(const struct sys_reg_params *p,
 				       char *fmt, ...)
@@ -237,6 +243,12 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index);
 
 int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu);
 
+u64 kvm_read_sanitised_id_reg(struct kvm_vcpu *vcpu,
+			      const struct sys_reg_desc *r);
+
+/* Implemented by each architecture */
+u64 kvm_sanitised_host_ftr_reg(u32 id);
+
 #define AA32(_x)	.aarch32_map = AA32_##_x
 #define Op0(_x) 	.Op0 = _x
 #define Op1(_x) 	.Op1 = _x
@@ -257,6 +269,81 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu);
 	CRn(sys_reg_CRn(reg)), CRm(sys_reg_CRm(reg)),	\
 	Op2(sys_reg_Op2(reg))
 
+/*
+ * 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.
+ */
+
+#define ID_DESC_DEFAULT_CALLBACKS		\
+	.access	= access_id_reg,		\
+	.get_user = get_id_reg,			\
+	.set_user = set_id_reg,			\
+	.visibility = id_visibility,		\
+	.reset = kvm_read_sanitised_id_reg
+
+#define ID_DESC(name)				\
+	SYS_DESC(SYS_##name),			\
+	ID_DESC_DEFAULT_CALLBACKS
+
+/* sys_reg_desc initialiser for known cpufeature ID registers */
+#define ID_SANITISED(name) {			\
+	ID_DESC(name),				\
+	.val = 0,				\
+}
+
+/* sys_reg_desc initialiser for writable ID registers */
+#define ID_WRITABLE(name, mask) {		\
+	ID_DESC(name),				\
+	.val = mask,				\
+}
+
+/*
+ * 32bit ID regs are fully writable when the guest is 32bit
+ * capable. Nothing in the KVM code should rely on 32bit features
+ * anyway, only 64bit, so let the VMM do its worse.
+ */
+#define AA32_ID_WRITABLE(name) {		\
+	ID_DESC(name),				\
+	.visibility = aa32_id_visibility,	\
+	.val = GENMASK(31, 0),			\
+}
+
+/* sys_reg_desc initialiser for cpufeature ID registers that need filtering */
+#define ID_FILTERED(sysreg, name, mask) {	\
+	ID_DESC(sysreg),				\
+	.set_user = set_##name,				\
+	.val = (mask),					\
+}
+
+/*
+ * sys_reg_desc initialiser for architecturally unallocated cpufeature ID
+ * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
+ * (1 <= crm < 8, 0 <= Op2 < 8).
+ */
+#define ID_UNALLOCATED(crm, op2) {			\
+	.name = "S3_0_0_" #crm "_" #op2,		\
+	Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),	\
+	ID_DESC_DEFAULT_CALLBACKS,			\
+	.visibility = raz_visibility,			\
+	.val = 0,					\
+}
+
+/*
+ * sys_reg_desc initialiser for known ID registers that we hide from guests.
+ * For now, these are exposed just like unallocated ID regs: they appear
+ * RAZ for the guest.
+ */
+#define ID_HIDDEN(name) {			\
+	ID_DESC(name),				\
+	.visibility = raz_visibility,		\
+	.val = 0,				\
+}
 #define ID_REG_LIMIT_FIELD_ENUM(val, reg, field, limit)			       \
 ({									       \
 	u64 __f_val = FIELD_GET(reg##_##field##_MASK, val);		       \
-- 
2.53.0