diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index ffe75fb241b1bcae2d98d1bb9a44d6f16084c3dd..f7d4a114dcf262887076454bab3f1ffe24b3671b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -387,6 +387,8 @@ struct kvm_vcpu_arch { bool pv_unhalted; gpa_t base; } pvsched; + + struct id_registers idregs; }; /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 16c65e844ef773b67f7498c82197815e88dfd28e..e0d15e3b8b4b4402b9904bd23004d72a2c5279af 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -269,6 +269,24 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) return 0; } +static int get_cpu_ftr(u32 id, u64 val, void *argp) +{ + struct id_registers *idregs = argp; + + /* + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), + * where 1<=crm<8, 0<=op2<8. + */ + if (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 && + sys_reg_CRn(id) == 0 && sys_reg_CRm(id) > 0) { + idregs->regs[idregs->num].sys_id = id; + idregs->regs[idregs->num].sys_val = val; + idregs->num++; + } + + return 0; +} + int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) { int err; @@ -296,6 +314,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) if (err) return err; + err = arm64_cpu_ftr_regs_traverse(get_cpu_ftr, &vcpu->arch.idregs); + if (err) + return err; + return create_hyp_mappings(vcpu, vcpu + 1, PAGE_HYP); } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 709fc409b86045973ba696ec26c2065108a68dfd..0e369a25bfe3043270fbcf59bc9216ce297bd854 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1112,13 +1112,35 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu, return true; } +static struct id_reg_info *kvm_id_reg(struct kvm_vcpu *vcpu, u64 id) +{ + int i; + + for (i = 0; i < vcpu->arch.idregs.num; ++i) { + if (vcpu->arch.idregs.regs[i].sys_id == id) + return &vcpu->arch.idregs.regs[i]; + } + return NULL; +} + +static u64 kvm_get_id_reg(struct kvm_vcpu *vcpu, u64 id) +{ + struct id_reg_info *ri = kvm_id_reg(vcpu, id); + + if (!ri) { + WARN_ON(1); + return 0; + } + return ri->sys_val; +} + /* Read a sanitised cpufeature ID register by sys_reg_desc */ -static u64 read_id_reg(const struct kvm_vcpu *vcpu, +static u64 read_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_desc const *r, bool raz) { u32 id = sys_reg((u32)r->Op0, (u32)r->Op1, (u32)r->CRn, (u32)r->CRm, (u32)r->Op2); - u64 val = raz ? 0 : read_sanitised_ftr_reg(id); + u64 val = raz ? 0 : kvm_get_id_reg(vcpu, id); if (id == SYS_ID_AA64PFR0_EL1) { if (!vcpu_has_sve(vcpu)) @@ -1249,7 +1271,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, * are stored, and for set_id_reg() we don't allow the effective value * to be changed. */ -static int __get_id_reg(const struct kvm_vcpu *vcpu, +static int __get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, void __user *uaddr, bool raz) { @@ -1259,7 +1281,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu, return reg_to_user(uaddr, &val, id); } -static int __set_id_reg(const struct kvm_vcpu *vcpu, +static int __set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, void __user *uaddr, bool raz) { diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a5221526cceec135af2bcab5907ef7191bf3f2fd..9976b79da52a6a2212d2c7f41225b1508b209eb2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1298,6 +1298,17 @@ struct kvm_vfio_spapr_tce { __s32 tablefd; }; +#define ID_REG_MAX_NUMS 64 +struct id_reg_info { + __u64 sys_id; + __u64 sys_val; +}; + +struct id_registers { + struct id_reg_info regs[ID_REG_MAX_NUMS]; + __u64 num; +}; + /* * ioctls for VM fds */