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 <[email protected]>
---
 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


Reply via email to