Refactor sysreg core handling. Before this all core regs are identified with a memory address and reading/writing happened through accessing the data at this address. However, for arm64 on s390 not all core registers have a dedicated memory address. Refactor such that the address function does not return an address but actually does the read/write request. ELR_EL1 and SPSR_EL1 now use vcpu_read_sys_reg/vcpu_write_sys_reg accessor functions, allowing s390 to provide custom implementations. Co-developed-by: Nina Schoetterl-Glausch Signed-off-by: Nina Schoetterl-Glausch Signed-off-by: Steffen Eiden --- virt/kvm/arm64/guest.c | 100 ++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/virt/kvm/arm64/guest.c b/virt/kvm/arm64/guest.c index 35ba03033b4c..11509382d594 100644 --- a/virt/kvm/arm64/guest.c +++ b/virt/kvm/arm64/guest.c @@ -65,69 +65,96 @@ static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off) return size; } -static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +static int core_reg_rw(struct kvm_vcpu *vcpu, u64 reg_id, void *valp, bool read) { - u64 off = core_reg_offset_from_id(reg->id); + u64 off = core_reg_offset_from_id(reg_id); int size = core_reg_size_from_offset(vcpu, off); + void *addr; - if (size < 0) - return NULL; + if (size < 0 || (KVM_REG_SIZE(reg_id) != size)) + return -EINVAL; - if (KVM_REG_SIZE(reg->id) != size) - return NULL; + switch (off) { + case KVM_REG_ARM_CORE_REG(elr_el1): + if (read) + *(u64 *)valp = vcpu_read_sys_reg(vcpu, ELR_EL1); + else + vcpu_write_sys_reg(vcpu, *(u64 *)valp, ELR_EL1); + return 0; + + case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_EL1]): + if (read) + *(u64 *)valp = vcpu_read_sys_reg(vcpu, SPSR_EL1); + else + vcpu_write_sys_reg(vcpu, *(u64 *)valp, SPSR_EL1); + return 0; + } switch (off) { case KVM_REG_ARM_CORE_REG(regs.regs[0]) ... KVM_REG_ARM_CORE_REG(regs.regs[30]): off -= KVM_REG_ARM_CORE_REG(regs.regs[0]); off /= 2; - return &vcpu_gp_regs(vcpu)[off]; + addr = &vcpu_gp_regs(vcpu)[off]; + break; case KVM_REG_ARM_CORE_REG(regs.sp): - return vcpu_sp_el0(vcpu); + addr = vcpu_sp_el0(vcpu); + break; case KVM_REG_ARM_CORE_REG(regs.pc): - return vcpu_pc(vcpu); + addr = vcpu_pc(vcpu); + break; case KVM_REG_ARM_CORE_REG(regs.pstate): - return vcpu_cpsr(vcpu); + addr = vcpu_cpsr(vcpu); + break; case KVM_REG_ARM_CORE_REG(sp_el1): - return kvm_vcpu_get_sp_el1(vcpu); - - case KVM_REG_ARM_CORE_REG(elr_el1): - return __ctxt_sys_reg(&vcpu->arch.ctxt, ELR_EL1); - - case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_EL1]): - return __ctxt_sys_reg(&vcpu->arch.ctxt, SPSR_EL1); + addr = kvm_vcpu_get_sp_el1(vcpu); + break; case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_ABT]): - return &vcpu->arch.ctxt.spsr_abt; + addr = &vcpu->arch.ctxt.spsr_abt; + break; case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_UND]): - return &vcpu->arch.ctxt.spsr_und; + addr = &vcpu->arch.ctxt.spsr_und; + break; case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_IRQ]): - return &vcpu->arch.ctxt.spsr_irq; + addr = &vcpu->arch.ctxt.spsr_irq; + break; case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_FIQ]): - return &vcpu->arch.ctxt.spsr_fiq; + addr = &vcpu->arch.ctxt.spsr_fiq; + break; case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ... KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]): off -= KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]); off /= 4; - return kvm_vcpu_get_vreg(vcpu, off); + addr = kvm_vcpu_get_vreg(vcpu, off); + break; case KVM_REG_ARM_CORE_REG(fp_regs.fpsr): - return kvm_vcpu_get_fpsr(vcpu); + addr = kvm_vcpu_get_fpsr(vcpu); + break; case KVM_REG_ARM_CORE_REG(fp_regs.fpcr): - return kvm_vcpu_get_fpcr(vcpu); + addr = kvm_vcpu_get_fpcr(vcpu); + break; default: - return NULL; + return -EINVAL; } + + if (read) + memcpy(valp, addr, size); + else + memcpy(addr, valp, size); + + return 0; } int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) @@ -140,7 +167,9 @@ int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) */ __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32); - void *addr; + __uint128_t tmp; + void *valp = &tmp; + int ret; u32 off; /* Our ID is an index into the kvm_regs struct. */ @@ -149,11 +178,12 @@ int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs) return -ENOENT; - addr = core_reg_addr(vcpu, reg); - if (!addr) - return -EINVAL; - if (copy_to_user(uaddr, addr, KVM_REG_SIZE(reg->id))) + ret = core_reg_rw(vcpu, reg->id, valp, true); + if (ret) + return ret; + + if (copy_to_user(uaddr, valp, KVM_REG_SIZE(reg->id))) return -EFAULT; return 0; @@ -164,7 +194,7 @@ int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32); __uint128_t tmp; - void *valp = &tmp, *addr; + void *valp = &tmp; u64 off; int err = 0; @@ -174,10 +204,6 @@ int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs) return -ENOENT; - addr = core_reg_addr(vcpu, reg); - if (!addr) - return -EINVAL; - if (KVM_REG_SIZE(reg->id) > sizeof(tmp)) return -EINVAL; @@ -220,7 +246,9 @@ int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) } } - memcpy(addr, valp, KVM_REG_SIZE(reg->id)); + err = core_reg_rw(vcpu, reg->id, valp, false); + if (err) + goto out; if (*vcpu_cpsr(vcpu) & PSR_MODE32_BIT) { int i, nr_reg; -- 2.53.0