Integrate the previously established system register managing infrastructure into the host. Initialize sysregs during vcpu init. Finalize sysregs on PID change, Initialize sysreg table on kvm init, report availability to userspace and let userspace read&modify guest system register. Co-developed-by: Andreas Grapentin Signed-off-by: Andreas Grapentin Signed-off-by: Steffen Eiden --- arch/s390/include/asm/kvm_host_arm64.h | 3 +++ arch/s390/kvm/arm64/arm.c | 29 +++++++++++++++++++++++ arch/s390/kvm/arm64/guest.c | 12 ++++++---- arch/s390/kvm/arm64/handle_exit.c | 1 + arch/s390/kvm/arm64/reset.c | 42 ++++++++++++++++++++++++++++++++++ arch/s390/kvm/arm64/sys_regs.c | 1 + 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/kvm_host_arm64.h b/arch/s390/include/asm/kvm_host_arm64.h index 35b6d8972359..44deae423ace 100644 --- a/arch/s390/include/asm/kvm_host_arm64.h +++ b/arch/s390/include/asm/kvm_host_arm64.h @@ -157,6 +157,9 @@ struct kvm_arch { /* Per-VM ID register storage */ struct kvm_vm_id_regs id_regs; + + /* The EPD as it should be on all VCPUs. */ + u64 epd; }; static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature) diff --git a/arch/s390/kvm/arm64/arm.c b/arch/s390/kvm/arm64/arm.c index ba6235629c7d..62fd71af8968 100644 --- a/arch/s390/kvm/arm64/arm.c +++ b/arch/s390/kvm/arm64/arm.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -50,6 +51,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) break; case KVM_CAP_IOEVENTFD: case KVM_CAP_PRE_FAULT_MEMORY: + case KVM_CAP_ONE_REG: ret = 1; break; default: @@ -136,6 +138,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) goto out_err; kvm->arch.mem_limit = kvm->arch.guest_phys_size; + kvm->arch.epd = ptff_qagto(0); + VM_EVENT(kvm, 3, "vm created with type %lu", type); return 0; @@ -193,6 +197,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) sae_block->mso = 0L; sae_block->msl = vcpu->kvm->arch.mem_limit - 1; sae_block->save_area = virt_to_phys(save_area); + sae_block->gpto = vcpu->kvm->arch.epd; VM_EVENT(vcpu->kvm, 3, "create cpu %d at 0x%p, sae block at 0x%p, satellite at 0x%p", @@ -207,6 +212,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { + kfree(vcpu->arch.ccsidr); kvm_s390_free_mmu_cache(vcpu->arch.mc); VCPU_EVENT(vcpu, 3, "%s", "free cpu"); @@ -218,6 +224,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu->cpu = cpu; lasrm(&vcpu->arch.save_area); + + vcpu->arch.sae_block.gpto = vcpu->kvm->arch.epd; } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -261,6 +269,7 @@ bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) { struct kvm *kvm = vcpu->kvm; + int ret; if (!kvm_vcpu_initialized(vcpu)) return -ENOEXEC; @@ -271,6 +280,10 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) if (likely(READ_ONCE(vcpu->pid))) return 0; + ret = kvm_vcpu_finalize_sys_regs(vcpu); + if (ret) + return ret; + scoped_guard(mutex, &kvm->arch.config_lock) set_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags); @@ -477,9 +490,17 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) trace_kvm_entry(*vcpu_pc(vcpu)); adjust_pc(vcpu); + vcpu_write_host_sys_reg(vcpu, vcpu->arch.hcr_el2, SYS_HCR_EL2); + vcpu_write_host_sys_reg(vcpu, vcpu->arch.hcrx_el2, SYS_HCRX_EL2); + vcpu_write_host_sys_reg(vcpu, vcpu->arch.mpidr, SYS_VMPIDR_EL2); + _vcpu_write_sys_reg(vcpu, vcpu->arch.ctxt.elr_el1, SYS_ELR_EL1); + _vcpu_write_sys_reg(vcpu, vcpu->arch.ctxt.spsr_el1, SYS_SPSR_EL1); arm_vcpu_enter_exit(virt_to_phys(&vcpu->arch.sae_block)); + vcpu->arch.ctxt.elr_el1 = _vcpu_read_sys_reg(vcpu, SYS_ELR_EL1); + vcpu->arch.ctxt.spsr_el1 = _vcpu_read_sys_reg(vcpu, SYS_SPSR_EL1); + vcpu->mode = OUTSIDE_GUEST_MODE; vcpu->stat.exits++; @@ -762,11 +783,19 @@ static bool __init machine_supports_arm64_kvm(void) static int __init kvm_s390_arm64_init(void) { + int ret; + if (!machine_supports_arm64_kvm()) return -ENODEV; kvm_init_qaaf(); + ret = kvm_sys_reg_table_init(); + if (ret) { + kvm_info("Error initializing system register tables\n"); + return ret; + } + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); } diff --git a/arch/s390/kvm/arm64/guest.c b/arch/s390/kvm/arm64/guest.c index 8b5ee9e79a4f..047460975936 100644 --- a/arch/s390/kvm/arm64/guest.c +++ b/arch/s390/kvm/arm64/guest.c @@ -4,6 +4,7 @@ #include #include +#include #define SVE_VQ_MIN __SVE_VQ_MIN #define SVE_NUM_ZREGS KVM_ARM64_SVE_NUM_ZREGS @@ -60,12 +61,15 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) return ret; uindices += ret; - return 0; + return kvm_arm_copy_sys_reg_indices(vcpu, uindices); } unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) { - return num_core_regs(vcpu); + unsigned long num = num_core_regs(vcpu); + + num += kvm_arm_num_sys_reg_descs(vcpu); + return num; } int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) @@ -78,7 +82,7 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) case KVM_REG_ARM_CORE: return get_core_reg(vcpu, reg); default: - return -EINVAL; + return kvm_arm_sys_reg_get_reg(vcpu, reg); } } @@ -92,7 +96,7 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) case KVM_REG_ARM_CORE: return set_core_reg(vcpu, reg); default: - return -EINVAL; + return kvm_arm_sys_reg_set_reg(vcpu, reg); } } diff --git a/arch/s390/kvm/arm64/handle_exit.c b/arch/s390/kvm/arm64/handle_exit.c index e0c7773c2165..1653163ed6d6 100644 --- a/arch/s390/kvm/arm64/handle_exit.c +++ b/arch/s390/kvm/arm64/handle_exit.c @@ -62,6 +62,7 @@ static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu) static exit_handle_fn arm_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = kvm_handle_unknown_ec, [ESR_ELx_EC_HVC64] = handle_hvc, + [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort, [ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort, }; diff --git a/arch/s390/kvm/arm64/reset.c b/arch/s390/kvm/arm64/reset.c index e4cdce1dbf9d..0339901f5fd0 100644 --- a/arch/s390/kvm/arm64/reset.c +++ b/arch/s390/kvm/arm64/reset.c @@ -5,6 +5,10 @@ #include #include +#include +#include + +#include bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu) { @@ -21,6 +25,39 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu) HCR_EL2_TIDCP | HCR_EL2_TLOR; } +static inline void vcpu_reset_hcrx(struct kvm_vcpu *vcpu) +{ + vcpu->arch.hcrx_el2 = 0; + if (kvm_has_feat(vcpu->kvm, ID_AA64ISAR2_EL1, MOPS, IMP)) + vcpu->arch.hcrx_el2 |= HCRX_EL2_MSCEn; +} + +static inline void vcpu_reset_cptr(struct kvm_vcpu *vcpu) +{ + u64 cptr; + + /* we unconditionally have E2H, so disable traps to EL2 for FP and SVE, if enabled */ + cptr = CPACR_EL1_FPEN; + if (vcpu_has_sve(vcpu)) + cptr |= CPACR_EL1_ZEN; + vcpu_write_host_sys_reg(vcpu, cptr, SYS_CPTR_EL2); +} + +static inline void vcpu_reset_cnthctl(struct kvm_vcpu *vcpu) +{ + u64 cnthctl; + + /* we unconditionally have E2H, so disable traps to EL2 for physical timer registers */ + cnthctl = (CNTHCTL_EL1PCEN | CNTHCTL_EL1PCTEN) << 10; + vcpu_write_host_sys_reg(vcpu, cnthctl, SYS_CNTHCTL_EL2); +} + +static inline void vcpu_reset_icc(struct kvm_vcpu *vcpu) +{ + /* ensure ICC_SRE_EL2.Enable = 1 so ICC_SRE_EL1 is handled in hardware */ + vcpu_write_host_sys_reg(vcpu, 0xf, SYS_ICC_SRE_EL2); +} + void kvm_reset_vcpu(struct kvm_vcpu *vcpu) { struct vcpu_reset_state reset_state; @@ -43,9 +80,14 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu) vcpu_put(vcpu); kvm_reset_vcpu_core(vcpu); + kvm_reset_sys_regs(vcpu); /* Reset special registers */ vcpu_reset_hcr(vcpu); + vcpu_reset_hcrx(vcpu); + vcpu_reset_cptr(vcpu); + vcpu_reset_cnthctl(vcpu); + vcpu_reset_icc(vcpu); if (reset_state.reset) { *vcpu_pc(vcpu) = reset_state.pc; diff --git a/arch/s390/kvm/arm64/sys_regs.c b/arch/s390/kvm/arm64/sys_regs.c index b1d4335779c7..417a05ce9015 100644 --- a/arch/s390/kvm/arm64/sys_regs.c +++ b/arch/s390/kvm/arm64/sys_regs.c @@ -441,6 +441,7 @@ static int arch_timer_set_user(struct kvm_vcpu *vcpu, case SYS_CNTPCT_EL0: gpto = ptff_qagto(val); vcpu->arch.sae_block.gpto = gpto; + vcpu->kvm->arch.epd = gpto; return 0; } -- 2.56.0