From patchwork Mon Oct 8 18:29:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jim Mattson X-Patchwork-Id: 10631265 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4CC9A174A for ; Mon, 8 Oct 2018 18:30:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 407CE29733 for ; Mon, 8 Oct 2018 18:30:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 34CE52977A; Mon, 8 Oct 2018 18:30:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 92B6229733 for ; Mon, 8 Oct 2018 18:30:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726671AbeJIBn2 (ORCPT ); Mon, 8 Oct 2018 21:43:28 -0400 Received: from mail-pf1-f202.google.com ([209.85.210.202]:34487 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726663AbeJIBn2 (ORCPT ); Mon, 8 Oct 2018 21:43:28 -0400 Received: by mail-pf1-f202.google.com with SMTP id i81-v6so17631005pfj.1 for ; Mon, 08 Oct 2018 11:30:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=puRYz/9AJg6z0LS02SzHvthqkFy4mQVK/U0r5o/Vlyk=; b=BPVZiv0ATkMKCj7n5NDvO63l+nDRgialDdD9RLW4TE46yWoXUGj9Uq0EOG6tDSfqYH oyJ1CbWnpkfG9WmdXHHCC2YttQjFzP5keCR637u99/e9FMa9AVHO07NPrTI+Fyr4Bogb baYC0g6JyeVilTzCRWpSebW+AuPAbRxmBg5pGrn73HzM8K7rp4Y6sWsRBbLJKeneKaBq +bbZHabbHDgK9CoqDDBd1Rdtj/C7GdCkeWsUdRdd+6PbQ7eQPH2orVLHXCDm93BsoEJy TIffv6V78i1qaJLFOjoXH6GWJoCRUV5RVPKjLWCR5AO7i3b8Hdp9tjfGaLaJLZbCzlNG yILg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=puRYz/9AJg6z0LS02SzHvthqkFy4mQVK/U0r5o/Vlyk=; b=FY70+39Mz9XSgG/QSS5sC+eonZkvtj6fZozmxXv2z7vs+bkmJ3dfQYCXAzMppV3EPW P2L9G1qGcLlMED2YR7j6Z4U6BIlsYbdxLz5M4IBQcE/XAurb7z1XG9BE259vGDq4jx7O Vm8hhHL+bQb9aRE+ZFzcp0K/+Jkoc5p/6wqJxEWqMl31ZGrVQcnhLgaPFtQLaj1DXrir lVGLOBcMu6tf6d6jWrc2MLWag76K6C5C7ESpqII0XHLOpZ0WsYFAfQe7otiP2MaQWQVT 7kJeGplSUgZfb/ZP9UTY0TLt/FuEugU1/E8V7dArCcWPBpu5gVClc3WJQ3luDeMTprLp hf0w== X-Gm-Message-State: ABuFfoj+d5ndH05asToeOPg6+/en6ocjbGU+7lrdJEdhae8RjSuZzPZv xRW0HDHgXEfbbHK5O9aln7bM5j1Y/bRY4RflBourtCicpUpCya6my+yBUsLuyBqOM5Yhuv8Ug3f bRjD0JbQcghUrgiViFyEjwtWgofcKbHLs+Xa2bFTPukNpWerfqW85CI2oqMTZtLQ= X-Google-Smtp-Source: ACcGV60i4HXDEjKBDi9nZ5Bp87kLlGCITub17Obcl4O6HGzAATHAIUkGAisofGrZN7aABuZMyZTfHH6DN49XbQ== X-Received: by 2002:a62:bd4:: with SMTP id 81-v6mr11564916pfl.49.1539023426147; Mon, 08 Oct 2018 11:30:26 -0700 (PDT) Date: Mon, 8 Oct 2018 11:29:43 -0700 In-Reply-To: <20181008182945.79957-1-jmattson@google.com> Message-Id: <20181008182945.79957-3-jmattson@google.com> Mime-Version: 1.0 References: <20181008182945.79957-1-jmattson@google.com> X-Mailer: git-send-email 2.19.0.605.g01d371f741-goog Subject: [PATCH 3/5] kvm: x86: Defer setting of CR2 until #PF delivery From: Jim Mattson To: kvm@vger.kernel.org, Paolo Bonzini , Peter Shier Cc: Jim Mattson Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When exception payloads are enabled by userspace (which is not yet possible) and a #PF is raised in L2, defer the setting of CR2 until the #PF is delivered. This allows the L1 hypervisor to intercept the fault before CR2 is modified. For backwards compatibility, when exception payloads are not enabled by userspace, kvm_multiple_exception modifies CR2 when the #PF exception is raised. Reported-by: Jim Mattson Suggested-by: Paolo Bonzini Signed-off-by: Jim Mattson Reviewed-by: Peter Shier Reviewed-by: Liran Alon --- arch/x86/kvm/svm.c | 13 ++++++------- arch/x86/kvm/vmx.c | 19 +++++++++---------- arch/x86/kvm/x86.c | 40 ++++++++++++++++++++++++++++++++++++---- arch/x86/kvm/x86.h | 2 ++ 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index d96092b35936..8b05a84cf8f2 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -805,6 +805,8 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu) nested_svm_check_exception(svm, nr, has_error_code, error_code)) return; + kvm_deliver_exception_payload(&svm->vcpu); + if (nr == BP_VECTOR && !static_cpu_has(X86_FEATURE_NRIPS)) { unsigned long rip, old_rip = kvm_rip_read(&svm->vcpu); @@ -2965,16 +2967,13 @@ static int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr, svm->vmcb->control.exit_info_1 = error_code; /* - * FIXME: we should not write CR2 when L1 intercepts an L2 #PF exception. - * The fix is to add the ancillary datum (CR2 or DR6) to structs - * kvm_queued_exception and kvm_vcpu_events, so that CR2 and DR6 can be - * written only when inject_pending_event runs (DR6 would written here - * too). This should be conditional on a new capability---if the - * capability is disabled, kvm_multiple_exception would write the - * ancillary information to CR2 or DR6, for backwards ABI-compatibility. + * EXITINFO2 is undefined for all exception intercepts other + * than #PF. */ if (svm->vcpu.arch.exception.nested_apf) svm->vmcb->control.exit_info_2 = svm->vcpu.arch.apf.nested_apf_token; + else if (svm->vcpu.arch.exception.has_payload) + svm->vmcb->control.exit_info_2 = svm->vcpu.arch.exception.payload; else svm->vmcb->control.exit_info_2 = svm->vcpu.arch.cr2; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 06412ba46aa3..fc3f2d27b580 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3275,27 +3275,24 @@ static int nested_vmx_check_exception(struct kvm_vcpu *vcpu, unsigned long *exit { struct vmcs12 *vmcs12 = get_vmcs12(vcpu); unsigned int nr = vcpu->arch.exception.nr; + bool has_payload = vcpu->arch.exception.has_payload; + unsigned long payload = vcpu->arch.exception.payload; if (nr == PF_VECTOR) { if (vcpu->arch.exception.nested_apf) { *exit_qual = vcpu->arch.apf.nested_apf_token; return 1; } - /* - * FIXME: we must not write CR2 when L1 intercepts an L2 #PF exception. - * The fix is to add the ancillary datum (CR2 or DR6) to structs - * kvm_queued_exception and kvm_vcpu_events, so that CR2 and DR6 - * can be written only when inject_pending_event runs. This should be - * conditional on a new capability---if the capability is disabled, - * kvm_multiple_exception would write the ancillary information to - * CR2 or DR6, for backwards ABI-compatibility. - */ if (nested_vmx_is_page_fault_vmexit(vmcs12, vcpu->arch.exception.error_code)) { - *exit_qual = vcpu->arch.cr2; + *exit_qual = has_payload ? payload : vcpu->arch.cr2; return 1; } } else { + /* + * FIXME: we must not write DR6 when L1 intercepts an + * L2 #DB exception. + */ if (vmcs12->exception_bitmap & (1u << nr)) { if (nr == DB_VECTOR) *exit_qual = vcpu->arch.dr6; @@ -3329,6 +3326,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu) u32 error_code = vcpu->arch.exception.error_code; u32 intr_info = nr | INTR_INFO_VALID_MASK; + kvm_deliver_exception_payload(vcpu); + if (has_error_code) { vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code); intr_info |= INTR_INFO_DELIVER_CODE_MASK; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7c4e6845fe80..974f0784ac99 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -400,6 +400,26 @@ static int exception_type(int vector) return EXCPT_FAULT; } +void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu) +{ + unsigned nr = vcpu->arch.exception.nr; + unsigned long has_payload = vcpu->arch.exception.has_payload; + unsigned long payload = vcpu->arch.exception.payload; + + if (!has_payload) + return; + + switch (nr) { + case PF_VECTOR: + vcpu->arch.cr2 = payload; + break; + } + + vcpu->arch.exception.has_payload = false; + vcpu->arch.exception.payload = 0; +} +EXPORT_SYMBOL_GPL(kvm_deliver_exception_payload); + static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned nr, bool has_error, u32 error_code, bool has_payload, unsigned long payload, bool reinject) @@ -433,6 +453,9 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, vcpu->arch.exception.error_code = error_code; vcpu->arch.exception.has_payload = has_payload; vcpu->arch.exception.payload = payload; + if (!vcpu->kvm->arch.exception_payload_enabled || + !is_guest_mode(vcpu)) + kvm_deliver_exception_payload(vcpu); return; } @@ -478,6 +501,13 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned nr) } EXPORT_SYMBOL_GPL(kvm_requeue_exception); +static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, + u32 error_code, unsigned long payload) +{ + kvm_multiple_exception(vcpu, nr, true, error_code, + true, payload, false); +} + int kvm_complete_insn_gp(struct kvm_vcpu *vcpu, int err) { if (err) @@ -494,11 +524,13 @@ void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault) ++vcpu->stat.pf_guest; vcpu->arch.exception.nested_apf = is_guest_mode(vcpu) && fault->async_page_fault; - if (vcpu->arch.exception.nested_apf) + if (vcpu->arch.exception.nested_apf) { vcpu->arch.apf.nested_apf_token = fault->address; - else - vcpu->arch.cr2 = fault->address; - kvm_queue_exception_e(vcpu, PF_VECTOR, fault->error_code); + kvm_queue_exception_e(vcpu, PF_VECTOR, fault->error_code); + } else { + kvm_queue_exception_e_p(vcpu, PF_VECTOR, fault->error_code, + fault->address); + } } EXPORT_SYMBOL_GPL(kvm_inject_page_fault); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 67b9568613f3..224cd0a47568 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -266,6 +266,8 @@ int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, int handle_ud(struct kvm_vcpu *vcpu); +void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu); + void kvm_vcpu_mtrr_init(struct kvm_vcpu *vcpu); u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn); bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data);