diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index fc6ee6c59..c3107506b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -136,6 +136,9 @@ struct kvm_arch { /* Memory Tagging Extension enabled for the guest */ bool mte_enabled; + + /* Exit on WFI/WFE */ + bool exit_on_wfx; }; struct kvm_vcpu_fault_info { diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index f181527f9..6d54dfbae 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -101,6 +101,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, } mutex_unlock(&kvm->lock); break; + case KVM_CAP_EXIT_ON_WFX: + r = 0; + kvm->arch.exit_on_wfx = true; + break; default: r = -EINVAL; break; @@ -215,6 +219,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_SET_GUEST_DEBUG: case KVM_CAP_VCPU_ATTRIBUTES: case KVM_CAP_PTP_KVM: + case KVM_CAP_EXIT_ON_WFX: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -394,8 +399,10 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { struct kvm_s2_mmu *mmu; int *last_ran; + bool exit_on_wfx; mmu = vcpu->arch.hw_mmu; + exit_on_wfx = vcpu->kvm->arch.exit_on_wfx; last_ran = this_cpu_ptr(mmu->last_vcpu_ran); /* @@ -423,7 +430,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (kvm_arm_is_pvtime_enabled(&vcpu->arch)) kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); - if (single_task_running()) + if (single_task_running() && !exit_on_wfx) vcpu_clear_wfx_traps(vcpu); else vcpu_set_wfx_traps(vcpu); diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index a5ab52150..80fa6bdef 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -91,10 +91,21 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu) if (kvm_vcpu_get_esr(vcpu) & ESR_ELx_WFx_ISS_WFE) { trace_kvm_wfx_arm64(*vcpu_pc(vcpu), true); vcpu->stat.wfe_exit_stat++; + if (vcpu->kvm->arch.exit_on_wfx) { + vcpu->run->exit_reason = KVM_EXIT_WFX; + vcpu->run->wfx.esr = kvm_vcpu_get_esr(vcpu); + return 0; + } + kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu)); } else { trace_kvm_wfx_arm64(*vcpu_pc(vcpu), false); vcpu->stat.wfi_exit_stat++; + if (vcpu->kvm->arch.exit_on_wfx) { + vcpu->run->exit_reason = KVM_EXIT_WFX; + vcpu->run->wfx.esr = kvm_vcpu_get_esr(vcpu); + return 0; + } kvm_vcpu_block(vcpu); kvm_clear_request(KVM_REQ_UNHALT, vcpu); } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 0d47e07f4..155dc7eab 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -269,6 +269,7 @@ struct kvm_xen_exit { #define KVM_EXIT_AP_RESET_HOLD 32 #define KVM_EXIT_X86_BUS_LOCK 33 #define KVM_EXIT_XEN 34 +#define KVM_EXIT_WFX 35 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -469,6 +470,11 @@ struct kvm_run { } msr; /* KVM_EXIT_XEN */ struct kvm_xen_exit xen; + /* KVM_EXIT_WFX */ + struct { + __u64 esr; + } wfx; + /* Fix the size of the union. */ char padding[256]; }; @@ -1123,6 +1129,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_XSAVE2 208 #define KVM_CAP_SYS_ATTRIBUTES 209 #define KVM_CAP_S390_MEM_OP_EXTENSION 211 +#define KVM_CAP_EXIT_ON_WFX 222 #ifdef KVM_CAP_IRQ_ROUTING