Introduce an ID feature register specific descriptor to include ID register specific fields and callbacks besides its corresponding general system register descriptor. No functional change intended. Co-developed-by: Reiji Watanabe <reijiw@xxxxxxxxxx> Signed-off-by: Reiji Watanabe <reijiw@xxxxxxxxxx> Signed-off-by: Jing Zhang <jingzhangos@xxxxxxxxxx> --- arch/arm64/kvm/id_regs.c | 233 ++++++++++++++++++++++++++++---------- arch/arm64/kvm/sys_regs.c | 2 +- arch/arm64/kvm/sys_regs.h | 1 + 3 files changed, 178 insertions(+), 58 deletions(-) diff --git a/arch/arm64/kvm/id_regs.c b/arch/arm64/kvm/id_regs.c index e92eacb0ad32..af86001e2686 100644 --- a/arch/arm64/kvm/id_regs.c +++ b/arch/arm64/kvm/id_regs.c @@ -18,6 +18,27 @@ #include "sys_regs.h" +struct id_reg_desc { + const struct sys_reg_desc reg_desc; + /* + * ftr_bits points to the feature bits array defined in cpufeature.c for + * writable CPU ID feature register. + */ + const struct arm64_ftr_bits *ftr_bits; + /* + * 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. + */ + const u64 writable_mask; + /* + * This function returns the KVM sanitised register value. + * The value would be the same as the host kernel sanitised value if + * there is no KVM sanitisation for this id register. + */ + u64 (*read_kvm_sanitised_reg)(const struct id_reg_desc *idr); +}; + static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu) { if (kvm_vcpu_has_pmu(vcpu)) @@ -193,6 +214,11 @@ static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu, return id_visibility(vcpu, r); } +static u64 general_read_kvm_sanitised_reg(const struct id_reg_desc *idr) +{ + return read_sanitised_ftr_reg(reg_to_encoding(&idr->reg_desc)); +} + static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val) @@ -328,21 +354,31 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu, } /* sys_reg_desc initialiser for known cpufeature ID registers */ -#define ID_SANITISED(name) { \ - SYS_DESC(SYS_##name), \ - .access = access_id_reg, \ - .get_user = get_id_reg, \ - .set_user = set_id_reg, \ - .visibility = id_visibility, \ +#define ID_SANITISED(name) { \ + .reg_desc = { \ + SYS_DESC(SYS_##name), \ + .access = access_id_reg, \ + .get_user = get_id_reg, \ + .set_user = set_id_reg, \ + .visibility = id_visibility, \ + }, \ + .ftr_bits = NULL, \ + .writable_mask = 0, \ + .read_kvm_sanitised_reg = general_read_kvm_sanitised_reg, \ } /* sys_reg_desc initialiser for known cpufeature ID registers */ -#define AA32_ID_SANITISED(name) { \ - SYS_DESC(SYS_##name), \ - .access = access_id_reg, \ - .get_user = get_id_reg, \ - .set_user = set_id_reg, \ - .visibility = aa32_id_visibility, \ +#define AA32_ID_SANITISED(name) { \ + .reg_desc = { \ + SYS_DESC(SYS_##name), \ + .access = access_id_reg, \ + .get_user = get_id_reg, \ + .set_user = set_id_reg, \ + .visibility = aa32_id_visibility, \ + }, \ + .ftr_bits = NULL, \ + .writable_mask = 0, \ + .read_kvm_sanitised_reg = general_read_kvm_sanitised_reg, \ } /* @@ -350,12 +386,17 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu, * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2 * (1 <= crm < 8, 0 <= Op2 < 8). */ -#define ID_UNALLOCATED(crm, op2) { \ - Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2), \ - .access = access_id_reg, \ - .get_user = get_id_reg, \ - .set_user = set_id_reg, \ - .visibility = raz_visibility \ +#define ID_UNALLOCATED(crm, op2) { \ + .reg_desc = { \ + Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2), \ + .access = access_id_reg, \ + .get_user = get_id_reg, \ + .set_user = set_id_reg, \ + .visibility = raz_visibility \ + }, \ + .ftr_bits = NULL, \ + .writable_mask = 0, \ + .read_kvm_sanitised_reg = NULL, \ } /* @@ -363,15 +404,20 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu, * For now, these are exposed just like unallocated ID regs: they appear * RAZ for the guest. */ -#define ID_HIDDEN(name) { \ - SYS_DESC(SYS_##name), \ - .access = access_id_reg, \ - .get_user = get_id_reg, \ - .set_user = set_id_reg, \ - .visibility = raz_visibility, \ +#define ID_HIDDEN(name) { \ + .reg_desc = { \ + SYS_DESC(SYS_##name), \ + .access = access_id_reg, \ + .get_user = get_id_reg, \ + .set_user = set_id_reg, \ + .visibility = raz_visibility, \ + }, \ + .ftr_bits = NULL, \ + .writable_mask = 0, \ + .read_kvm_sanitised_reg = NULL, \ } -static const struct sys_reg_desc id_reg_descs[] = { +static const struct id_reg_desc id_reg_descs[KVM_ARM_ID_REG_NUM] = { /* * ID regs: all ID_SANITISED() entries here must have corresponding * entries in arm64_ftr_regs[]. @@ -381,9 +427,13 @@ static const struct sys_reg_desc id_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, }, + { .reg_desc = { + 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, }, + }, ID_HIDDEN(ID_AFR0_EL1), AA32_ID_SANITISED(ID_MMFR0_EL1), AA32_ID_SANITISED(ID_MMFR1_EL1), @@ -412,8 +462,12 @@ static const struct sys_reg_desc id_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, }, + { .reg_desc = { + SYS_DESC(SYS_ID_AA64PFR0_EL1), + .access = access_id_reg, + .get_user = get_id_reg, + .set_user = set_id_aa64pfr0_el1, }, + }, ID_SANITISED(ID_AA64PFR1_EL1), ID_UNALLOCATED(4, 2), ID_UNALLOCATED(4, 3), @@ -423,8 +477,12 @@ static const struct sys_reg_desc id_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, }, + { .reg_desc = { + SYS_DESC(SYS_ID_AA64DFR0_EL1), + .access = access_id_reg, + .get_user = get_id_reg, + .set_user = set_id_aa64dfr0_el1, }, + }, ID_SANITISED(ID_AA64DFR1_EL1), ID_UNALLOCATED(5, 2), ID_UNALLOCATED(5, 3), @@ -454,6 +512,17 @@ static const struct sys_reg_desc id_reg_descs[] = { ID_UNALLOCATED(7, 7), }; +static const struct sys_reg_desc *id_params_to_desc(struct sys_reg_params *params) +{ + u32 id; + + id = reg_to_encoding(params); + if (is_id_reg(id)) + return &id_reg_descs[IDREG_IDX(id)].reg_desc; + + return NULL; +} + /** * emulate_id_reg - Emulate a guest access to an AArch64 CPU ID feature register * @vcpu: The VCPU pointer @@ -465,9 +534,9 @@ int emulate_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params) { const struct sys_reg_desc *r; - r = find_reg(params, id_reg_descs, ARRAY_SIZE(id_reg_descs)); + r = id_params_to_desc(params); - if (likely(r)) { + if (r) { perform_access(vcpu, params, r); } else { print_sys_reg_msg(params, @@ -481,32 +550,91 @@ int emulate_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params) int kvm_arm_get_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - return kvm_sys_reg_get_user(vcpu, reg, - id_reg_descs, ARRAY_SIZE(id_reg_descs)); + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + const struct sys_reg_desc *r; + struct sys_reg_params params; + u64 val; + int ret; + + if (!index_to_params(reg->id, ¶ms)) + return -ENOENT; + + r = id_params_to_desc(¶ms); + if (!r) + return -ENOENT; + + if (r->get_user) { + ret = (r->get_user)(vcpu, r, &val); + } else { + ret = 0; + val = IDREG(vcpu->kvm, reg_to_encoding(r)); + } + + if (!ret) + ret = put_user(val, uaddr); + + return ret; } int kvm_arm_set_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - return kvm_sys_reg_set_user(vcpu, reg, - id_reg_descs, ARRAY_SIZE(id_reg_descs)); + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + const struct sys_reg_desc *r; + struct sys_reg_params params; + u64 val; + int ret; + + if (!index_to_params(reg->id, ¶ms)) + return -ENOENT; + + r = id_params_to_desc(¶ms); + if (!r) + return -ENOENT; + + if (get_user(val, uaddr)) + return -EFAULT; + + if (sysreg_user_write_ignore(vcpu, r)) + return 0; + + if (r->set_user) { + ret = (r->set_user)(vcpu, r, val); + } else { + WARN_ONCE(1, "ID register set_user callback is NULL\n"); + ret = 0; + } + + return ret; } bool kvm_arm_check_idreg_table(void) { - return check_sysreg_table(id_reg_descs, ARRAY_SIZE(id_reg_descs), false); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++) { + const struct sys_reg_desc *r = &id_reg_descs[i].reg_desc; + + if (!is_id_reg(reg_to_encoding(r))) { + kvm_err("id_reg table %pS entry %d not set correctly\n", + &id_reg_descs[i].reg_desc, i); + return false; + } + } + + return true; } int kvm_arm_walk_id_regs(struct kvm_vcpu *vcpu, u64 __user *uind) { - const struct sys_reg_desc *i2, *end2; + const struct id_reg_desc *i2, *end2; unsigned int total = 0; int err; i2 = id_reg_descs; end2 = id_reg_descs + ARRAY_SIZE(id_reg_descs); - while (i2 != end2) { - err = walk_one_sys_reg(vcpu, i2++, &uind, &total); + for (; i2 != end2; i2++) { + err = walk_one_sys_reg(vcpu, &(i2->reg_desc), &uind, &total); if (err) return err; } @@ -524,21 +652,12 @@ void kvm_arm_init_id_regs(struct kvm *kvm) u64 val; for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++) { - id = reg_to_encoding(&id_reg_descs[i]); - if (WARN_ON_ONCE(!is_id_reg(id))) - /* Shouldn't happen */ - continue; - - /* - * 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 (id_reg_descs[i].visibility == raz_visibility) - /* Hidden or reserved ID register */ - continue; - - val = read_sanitised_ftr_reg(id); + id = reg_to_encoding(&id_reg_descs[i].reg_desc); + + val = 0; + if (id_reg_descs[i].read_kvm_sanitised_reg) + val = id_reg_descs[i].read_kvm_sanitised_reg(&id_reg_descs[i]); + IDREG(kvm, id) = val; } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 3af9f85aa976..1d7fb0acf154 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2541,7 +2541,7 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu) * Userspace API *****************************************************************************/ -static bool index_to_params(u64 id, struct sys_reg_params *params) +bool index_to_params(u64 id, struct sys_reg_params *params) { switch (id & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U64: diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index 326ca28c1117..15a0a1e2fe99 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -223,6 +223,7 @@ static inline bool is_id_reg(u32 id) void perform_access(struct kvm_vcpu *vcpu, struct sys_reg_params *params, const struct sys_reg_desc *r); +bool index_to_params(u64 id, struct sys_reg_params *params); const struct sys_reg_desc *get_reg_by_id(u64 id, const struct sys_reg_desc table[], unsigned int num); -- 2.40.0.348.gf938b09366-goog