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

