[RFC PATCH 03/46] KVM: arm64: Split kvm_sys_reg_{get,set}_user from kernel accessors
From: Pasha Tatashin
Date: Sun Sep 20 2026 - 15:39:48 EST
Factor __kvm_sys_reg_get() and __kvm_sys_reg_set() out of the userspace
ioctl handlers and introduce struct sys_reg_index_sink so in-kernel
callers can enumerate, read, and write architectural system registers
via kvm_arm_get_sys_reg_indices(), kvm_arm_sys_reg_read(), and
kvm_arm_sys_reg_write().
Signed-off-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
---
arch/arm64/kvm/sys_regs.c | 199 ++++++++++++++++++++++++++++++--------
arch/arm64/kvm/sys_regs.h | 4 +
2 files changed, 160 insertions(+), 43 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index b873ccf1ab14..0049e460e5ad 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -5569,38 +5569,43 @@ static int demux_c15_set(struct kvm_vcpu *vcpu, u64 id, void __user *uaddr)
return demux_c15_set_val(vcpu, id, newval);
}
-static u64 kvm_one_reg_to_id(const struct kvm_one_reg *reg)
+static u64 kvm_one_reg_to_id(u64 reg_id)
{
- switch(reg->id) {
+ switch (reg_id) {
case KVM_REG_ARM_TIMER_CVAL:
return TO_ARM64_SYS_REG(CNTV_CVAL_EL0);
case KVM_REG_ARM_TIMER_CNT:
return TO_ARM64_SYS_REG(CNTVCT_EL0);
default:
- return reg->id;
+ return reg_id;
}
}
-int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
- const struct sys_reg_desc table[], unsigned int num)
+static int __kvm_sys_reg_get(struct kvm_vcpu *vcpu, u64 reg_id, u64 *val,
+ const struct sys_reg_desc table[], unsigned int num)
{
- u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
const struct sys_reg_desc *r;
- u64 id = kvm_one_reg_to_id(reg);
- u64 val;
- int ret;
+ u64 id = kvm_one_reg_to_id(reg_id);
r = id_to_sys_reg_desc(vcpu, id, table, num);
if (!r || sysreg_hidden(vcpu, r))
return -ENOENT;
- if (r->get_user) {
- ret = (r->get_user)(vcpu, r, &val);
- } else {
- val = __vcpu_sys_reg(vcpu, r->reg);
- ret = 0;
- }
+ if (r->get_user)
+ return (r->get_user)(vcpu, r, val);
+
+ *val = __vcpu_sys_reg(vcpu, r->reg);
+ return 0;
+}
+
+int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
+ const struct sys_reg_desc table[], unsigned int num)
+{
+ u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
+ u64 val;
+ int ret;
+ ret = __kvm_sys_reg_get(vcpu, reg->id, &val, table, num);
if (!ret)
ret = put_user(val, uaddr);
@@ -5618,17 +5623,11 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
}
-int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
- const struct sys_reg_desc table[], unsigned int num)
+static int __kvm_sys_reg_set(struct kvm_vcpu *vcpu, u64 reg_id, u64 val,
+ const struct sys_reg_desc table[], unsigned int num)
{
- u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
const struct sys_reg_desc *r;
- u64 id = kvm_one_reg_to_id(reg);
- u64 val;
- int ret;
-
- if (get_user(val, uaddr))
- return -EFAULT;
+ u64 id = kvm_one_reg_to_id(reg_id);
r = id_to_sys_reg_desc(vcpu, id, table, num);
if (!r || sysreg_hidden(vcpu, r))
@@ -5637,14 +5636,23 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
if (sysreg_user_write_ignore(vcpu, r))
return 0;
- if (r->set_user) {
- ret = (r->set_user)(vcpu, r, val);
- } else {
- __vcpu_assign_sys_reg(vcpu, r->reg, val);
- ret = 0;
- }
+ if (r->set_user)
+ return (r->set_user)(vcpu, r, val);
- return ret;
+ __vcpu_assign_sys_reg(vcpu, r->reg, val);
+ return 0;
+}
+
+int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
+ const struct sys_reg_desc table[], unsigned int num)
+{
+ u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
+ u64 val;
+
+ if (get_user(val, uaddr))
+ return -EFAULT;
+
+ return __kvm_sys_reg_set(vcpu, reg->id, val, table, num);
}
int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
@@ -5688,25 +5696,24 @@ static u64 sys_reg_to_index(const struct sys_reg_desc *reg)
(reg->Op2 << KVM_REG_ARM64_SYSREG_OP2_SHIFT));
}
-static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
+static u64 reg_to_user_idx(const struct sys_reg_desc *reg)
{
- u64 idx;
-
- if (!*uind)
- return true;
-
switch (reg_to_encoding(reg)) {
case SYS_CNTV_CVAL_EL0:
- idx = KVM_REG_ARM_TIMER_CVAL;
- break;
+ return KVM_REG_ARM_TIMER_CVAL;
case SYS_CNTVCT_EL0:
- idx = KVM_REG_ARM_TIMER_CNT;
- break;
+ return KVM_REG_ARM_TIMER_CNT;
default:
- idx = sys_reg_to_index(reg);
+ return sys_reg_to_index(reg);
}
+}
- if (put_user(idx, *uind))
+static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
+{
+ if (!*uind)
+ return true;
+
+ if (put_user(reg_to_user_idx(reg), *uind))
return false;
(*uind)++;
@@ -5986,3 +5993,109 @@ int __init kvm_sys_reg_table_init(void)
return ret;
}
+
+/* The GICv3 CPU interface sysregs, or NULL if the in-kernel GICv3 is unused. */
+static const struct sys_reg_desc *vcpu_gic_sysreg_table(struct kvm_vcpu *vcpu,
+ unsigned int *num)
+{
+ if (!irqchip_in_kernel(vcpu->kvm) ||
+ vcpu->kvm->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
+ return NULL;
+
+ return vgic_v3_get_sysreg_table(num);
+}
+
+static void collect_table_indices(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *table,
+ unsigned int num, u64 **out, int *count)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ const struct sys_reg_desc *rd = &table[i];
+
+ if (!(rd->reg || rd->get_user) || sysreg_hidden(vcpu, rd))
+ continue;
+ if (*out)
+ *(*out)++ = reg_to_user_idx(rd);
+ (*count)++;
+ }
+}
+
+/*
+ * Enumerate every system register that kvm_arm_sys_reg_read() can return for
+ * @vcpu. This is a superset of KVM_GET_REG_LIST: it also covers the GICv3 CPU
+ * interface table, which has no uAPI representation. Pass a NULL @indices to
+ * query the number of entries the array needs to hold.
+ */
+int kvm_arm_get_sys_reg_indices(struct kvm_vcpu *vcpu, u64 *indices)
+{
+ const struct sys_reg_desc *gic_regs;
+ unsigned int sz = 0, i;
+ int count = 0;
+
+ collect_table_indices(vcpu, sys_reg_descs, ARRAY_SIZE(sys_reg_descs),
+ &indices, &count);
+
+ gic_regs = vcpu_gic_sysreg_table(vcpu, &sz);
+ if (gic_regs)
+ collect_table_indices(vcpu, gic_regs, sz, &indices, &count);
+
+ for (i = 0; i < CSSELR_MAX; i++) {
+ if (indices)
+ *indices++ = KVM_REG_ARM64 | KVM_REG_SIZE_U32 |
+ KVM_REG_ARM_DEMUX |
+ KVM_REG_ARM_DEMUX_ID_CCSIDR | i;
+ count++;
+ }
+
+ return count;
+}
+
+int kvm_arm_sys_reg_read(struct kvm_vcpu *vcpu, u64 reg_id, u64 *val)
+{
+ const struct sys_reg_desc *gic_regs;
+ unsigned int sz = 0;
+ int ret;
+
+ if ((reg_id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) {
+ u32 uval;
+
+ ret = demux_c15_get_val(vcpu, reg_id, &uval);
+ if (!ret)
+ *val = uval;
+ return ret;
+ }
+
+ ret = __kvm_sys_reg_get(vcpu, reg_id, val, sys_reg_descs,
+ ARRAY_SIZE(sys_reg_descs));
+ if (ret != -ENOENT)
+ return ret;
+
+ gic_regs = vcpu_gic_sysreg_table(vcpu, &sz);
+ if (!gic_regs)
+ return ret;
+
+ return __kvm_sys_reg_get(vcpu, reg_id, val, gic_regs, sz);
+}
+
+int kvm_arm_sys_reg_write(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
+{
+ const struct sys_reg_desc *gic_regs;
+ unsigned int sz = 0;
+ int ret;
+
+ if ((reg_id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
+ return demux_c15_set_val(vcpu, reg_id, (u32)val);
+
+ ret = __kvm_sys_reg_set(vcpu, reg_id, val, sys_reg_descs,
+ ARRAY_SIZE(sys_reg_descs));
+ if (ret != -ENOENT)
+ return ret;
+
+ gic_regs = vcpu_gic_sysreg_table(vcpu, &sz);
+ if (!gic_regs)
+ return ret;
+
+ return __kvm_sys_reg_set(vcpu, reg_id, val, gic_regs, sz);
+}
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index bd56a45abbf9..9837db99909b 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -292,4 +292,8 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu);
sys_reg_CRm(SYS_ ## r), \
sys_reg_Op2(SYS_ ## r))
+int kvm_arm_get_sys_reg_indices(struct kvm_vcpu *vcpu, u64 *indices);
+int kvm_arm_sys_reg_read(struct kvm_vcpu *vcpu, u64 reg_id, u64 *val);
+int kvm_arm_sys_reg_write(struct kvm_vcpu *vcpu, u64 reg_id, u64 val);
+
#endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */
--
2.55.0.1082.g2b9226bbc0-goog