In AArch64, guest will read the same values of the ID regsiters with host. Both of them read the values from arm64_ftr_regs. This patch series add support to emulate and configure ID registers so that we can control the value of ID registers that guest read.
Peng Liang (4): arm64: add a helper function to traverse arm64_ftr_regs kvm: arm64: emulate the ID registers kvm: arm64: make ID registers configurable kvm: arm64: add KVM_CAP_ARM_CPU_FEATURE extension
arch/arm64/include/asm/cpufeature.h | 2 + arch/arm64/include/asm/kvm_host.h | 2 + arch/arm64/kernel/cpufeature.c | 13 ++++++ arch/arm64/kvm/sys_regs.c | 71 ++++++++++++++++++++++------- include/uapi/linux/kvm.h | 13 ++++++ virt/kvm/arm/arm.c | 23 ++++++++++ 6 files changed, 107 insertions(+), 17 deletions(-)
From: Peng Liang liangpeng10@huawei.com
hulk inclusion category: feature bugzilla: NA CVE: NA
If we want to emulate ID registers, we need to initialize ID registers firstly. This commit is to add a helper function to traverse arm64_ftr_regs so that we can initialize ID registers from arm64_ftr_regs.
Signed-off-by: zhanghailiang zhang.zhanghailiang@huawei.com Signed-off-by: Peng Liang liangpeng10@huawei.com Acked-by: Hanjun Guo guohanjun@huawei.com Reviewed-by: Xiangyou Xie xiexiangyou@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/include/asm/cpufeature.h | 2 ++ arch/arm64/kernel/cpufeature.c | 13 +++++++++++++ 2 files changed, 15 insertions(+)
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index b4779d91eff59..f776f82396990 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -89,6 +89,8 @@ struct arm64_ftr_reg {
extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0;
+int arm64_cpu_ftr_regs_traverse(int (*op)(u32, u64, void *), void *argp); + /* * CPU capabilities: * diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index beea9be91cd1a..9bc92d8deb773 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -763,6 +763,19 @@ u64 read_sanitised_ftr_reg(u32 id) return regp->sys_val; }
+int arm64_cpu_ftr_regs_traverse(int (*op)(u32, u64, void *), void *argp) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(arm64_ftr_regs); i++) { + ret = (*op)(arm64_ftr_regs[i].sys_id, + arm64_ftr_regs[i].reg->sys_val, argp); + if (ret < 0) + return ret; + } + return 0; +} + #define read_sysreg_case(r) \ case r: return read_sysreg_s(r)
From: Peng Liang liangpeng10@huawei.com
hulk inclusion category: feature bugzilla: NA CVE: NA
To emulate the ID registers, we need a place to storage the values of the ID regsiters. Maybe putting in kvm_arch_vcpu is a good idea.
This commit has no functional changes but only code refactor. When initializing a vcpu, get the values of the ID registers from arm64_ftr_regs and storage them in kvm_arch_vcpu. And we just read the value from kvm_arch_vcpu when getting/setting the value of the ID regs.
Signed-off-by: zhanghailiang zhang.zhanghailiang@huawei.com Signed-off-by: Peng Liang liangpeng10@huawei.com Reviewed-by: Xiangyou Xie xiexiangyou@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++-------- include/uapi/linux/kvm.h | 11 ++++++++ virt/kvm/arm/arm.c | 22 +++++++++++++++ 4 files changed, 71 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 354bd5b9f1792..f3c1a2263623b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -330,6 +330,8 @@ struct kvm_vcpu_arch { bool pv_unhalted; gpa_t base; } pvsched; + + struct id_registers idregs; };
/* vcpu_arch flags field values: */ diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index c557eb95cb8e7..ccb868dc0efa7 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1031,12 +1031,35 @@ static bool access_cntp_cval(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(struct sys_reg_desc const *r, bool raz) +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 (val & (0xfUL << ID_AA64PFR0_SVE_SHIFT)) @@ -1063,7 +1086,7 @@ static bool __access_id_reg(struct kvm_vcpu *vcpu, if (p->is_write) return write_to_read_only(vcpu, p, r);
- p->regval = read_id_reg(r, raz); + p->regval = read_id_reg(vcpu, r, raz); return true; }
@@ -1092,16 +1115,18 @@ static u64 sys_reg_to_index(const struct sys_reg_desc *reg); * are stored, and for set_id_reg() we don't allow the effective value * to be changed. */ -static int __get_id_reg(const struct sys_reg_desc *rd, void __user *uaddr, +static int __get_id_reg(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, void __user *uaddr, bool raz) { const u64 id = sys_reg_to_index(rd); - const u64 val = read_id_reg(rd, raz); + const u64 val = read_id_reg(vcpu, rd, raz);
return reg_to_user(uaddr, &val, id); }
-static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr, +static int __set_id_reg(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, void __user *uaddr, bool raz) { const u64 id = sys_reg_to_index(rd); @@ -1113,7 +1138,7 @@ static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr, return err;
/* This is what we mean by invariant: you can't change it. */ - if (val != read_id_reg(rd, raz)) + if (val != read_id_reg(vcpu, rd, raz)) return -EINVAL;
return 0; @@ -1122,25 +1147,25 @@ static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr, static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, const struct kvm_one_reg *reg, void __user *uaddr) { - return __get_id_reg(rd, uaddr, false); + return __get_id_reg(vcpu, rd, uaddr, false); }
static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, const struct kvm_one_reg *reg, void __user *uaddr) { - return __set_id_reg(rd, uaddr, false); + return __set_id_reg(vcpu, rd, uaddr, false); }
static int get_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, const struct kvm_one_reg *reg, void __user *uaddr) { - return __get_id_reg(rd, uaddr, true); + return __get_id_reg(vcpu, rd, uaddr, true); }
static int set_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, const struct kvm_one_reg *reg, void __user *uaddr) { - return __set_id_reg(rd, uaddr, true); + return __set_id_reg(vcpu, rd, uaddr, true); }
/* sys_reg_desc initialiser for known cpufeature ID registers */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 211888d0c4e14..4735689dc8eb2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1197,6 +1197,17 @@ struct kvm_vfio_spapr_tce { __s32 tablefd; };
+#define ID_REG_MAX_NUMS 64 +struct id_reg_info { + uint64_t sys_id; + uint64_t sys_val; +}; + +struct id_registers { + struct id_reg_info regs[ID_REG_MAX_NUMS]; + uint64_t num; +}; + /* * ioctls for VM fds */ diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 3c484da5aa3e8..cd6710630b52d 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -277,6 +277,24 @@ void kvm_arch_free_vm(struct kvm *kvm) vfree(kvm); }
+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; +} + struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) { int err; @@ -302,6 +320,10 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) if (err) goto free_vcpu;
+ err = arm64_cpu_ftr_regs_traverse(get_cpu_ftr, &vcpu->arch.idregs); + if (err) + goto free_vcpu; + err = create_hyp_mappings(vcpu, vcpu + 1, PAGE_HYP); if (err) goto vcpu_uninit;
From: Peng Liang liangpeng10@huawei.com
hulk inclusion category: feature bugzilla: NA CVE: NA
It's time to make ID registers configurable. When userspace (but not guest) want to set the values of ID registers, save the value in kvm_arch_vcpu so that guest can read the modified values.
Signed-off-by: zhanghailiang zhang.zhanghailiang@huawei.com Signed-off-by: Peng Liang liangpeng10@huawei.com Reviewed-by: Xiangyou Xie xiexiangyou@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/kvm/sys_regs.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index ccb868dc0efa7..29496548c56b9 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1053,6 +1053,17 @@ static u64 kvm_get_id_reg(struct kvm_vcpu *vcpu, u64 id) return ri->sys_val; }
+static void kvm_set_id_reg(struct kvm_vcpu *vcpu, u64 id, u64 value) +{ + struct id_reg_info *ri = kvm_id_reg(vcpu, id); + + if (!ri) { + WARN_ON(1); + return; + } + ri->sys_val = value; +} + /* Read a sanitised cpufeature ID register by sys_reg_desc */ static u64 read_id_reg(struct kvm_vcpu *vcpu, struct sys_reg_desc const *r, bool raz) @@ -1110,10 +1121,6 @@ static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
/* * cpufeature ID register user accessors - * - * For now, these registers are immutable for userspace, so no values - * are stored, and for set_id_reg() we don't allow the effective value - * to be changed. */ static int __get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, void __user *uaddr, @@ -1137,9 +1144,14 @@ static int __set_id_reg(struct kvm_vcpu *vcpu, if (err) return err;
- /* This is what we mean by invariant: you can't change it. */ - if (val != read_id_reg(vcpu, rd, raz)) - return -EINVAL; + if (raz) { + if (val != read_id_reg(vcpu, rd, raz)) + return -EINVAL; + } else { + u32 reg_id = sys_reg((u32)rd->Op0, (u32)rd->Op1, (u32)rd->CRn, + (u32)rd->CRm, (u32)rd->Op2); + kvm_set_id_reg(vcpu, reg_id, val); + }
return 0; }
From: Peng Liang liangpeng10@huawei.com
hulk inclusion category: feature bugzilla: NA CVE: NA
Add KVM_CAP_ARM_CPU_FEATURE extension for userpace to check whether KVM supports to set CPU features in AArch64.
Signed-off-by: zhanghailiang zhang.zhanghailiang@huawei.com Signed-off-by: Peng Liang liangpeng10@huawei.com Reviewed-by: Xiangyou Xie xiexiangyou@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- include/uapi/linux/kvm.h | 2 ++ virt/kvm/arm/arm.c | 1 + 2 files changed, 3 insertions(+)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 4735689dc8eb2..4bd8e8bcc78e1 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -967,6 +967,8 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_VM_IPA_SIZE 165 /* returns maximum IPA bits for a VM */ #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
+#define KVM_CAP_ARM_CPU_FEATURE 555 + #ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing_irqchip { diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index cd6710630b52d..50fe77632d194 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -218,6 +218,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_MP_STATE: case KVM_CAP_IMMEDIATE_EXIT: case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: + case KVM_CAP_ARM_CPU_FEATURE: r = 1; break; case KVM_CAP_ARM_SET_DEVICE_ADDR: