From patchwork Tue Jun 16 02:23:18 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve Rutherford X-Patchwork-Id: 6612731 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 01A84C0020 for ; Tue, 16 Jun 2015 02:28:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id AD7AE20780 for ; Tue, 16 Jun 2015 02:28:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4840320786 for ; Tue, 16 Jun 2015 02:28:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752688AbbFPC2x (ORCPT ); Mon, 15 Jun 2015 22:28:53 -0400 Received: from mail-ig0-f170.google.com ([209.85.213.170]:37216 "EHLO mail-ig0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752669AbbFPC2s (ORCPT ); Mon, 15 Jun 2015 22:28:48 -0400 Received: by igbsb11 with SMTP id sb11so4817088igb.0 for ; Mon, 15 Jun 2015 19:28:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=mEUFXXuMXuzKuFAg89J96xEtemCGC+V1YN4Fz6T+KI4=; b=Agx0ReUciGgvg6KyZq6qAYfleJgWEKHoTcx/mo8JRx/gHhvLU8+4PsgKAMtSPUNX41 QGO1+V7Z/ARVXDfDHNpS1QQm+Qr7JHP3/wZzXyiZyMoroNpO69gX9UWzhAeh5JlJaEiw 45isa2GqaIwHEgyNrE+lwklfUkfXVPbhLAfbARWUeUQzzB3f3y8uAr3ykq05+a5wqLrE sH0ou0qliJC+O+WC6eH7zDekpP0mAuvwmsu8YGqAwTdM98R52PCJxKHnnzEGm8dQhzo7 /RzLu1QK3GFNfoeoCsKhfyxRHQgQis2rGDOU43Qx5bOEVKkfAbDZ7Tcsqd6HznLsrUU5 Wf4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=mEUFXXuMXuzKuFAg89J96xEtemCGC+V1YN4Fz6T+KI4=; b=E2MsnetUgQufuN1tvShWgt/AMk2fvPyRgVT13ggLPoBSkSwSPrlEtkTApOymWxIAPJ 0ws9HEEJVz6/TuPN+/aqBidOYkr+bOibcfW+KgSI21VCXPlS7Q4HXocmrBhbFyWWMygX /nzbMiyn9aP/Dc3rWsZCY9N4vttYBnLmzlbShvrbMNGvti7G5dMLkusZ8tdL8ElCIRYY /62MjjStbEo/gJjF41MAUktnbNeJmTDuhoo3XmrmjfgR6ZY3VS5KjXFiEWCkUO1YAX7G 8FHnZPBKqBasxqDB/JAZqVrf//53BUW23Au5ojjPCE8WISV186XxNcgr2glyNzCzjMF5 hpJw== X-Gm-Message-State: ALoCoQke6eVINDucT51rItglXUl6NXrGhxdctOL9ZxTx7BsT9jIHg2nHMyWCeUyeF4CORHtcIp+r X-Received: by 10.50.78.170 with SMTP id c10mr607068igx.0.1434421727174; Mon, 15 Jun 2015 19:28:47 -0700 (PDT) Received: from entropic.kir.corp.google.com ([172.31.8.128]) by mx.google.com with ESMTPSA id g3sm223036igi.10.2015.06.15.19.28.46 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 15 Jun 2015 19:28:46 -0700 (PDT) From: Steve Rutherford To: kvm@vger.kernel.org Subject: [PATCH v4 4/4] KVM: x86: Add support for local interrupt requests from userspace Date: Mon, 15 Jun 2015 19:23:18 -0700 Message-Id: <1434421398-6613-4-git-send-email-srutherford@google.com> X-Mailer: git-send-email 2.2.0.rc0.207.ga3a616c In-Reply-To: <1434421398-6613-1-git-send-email-srutherford@google.com> References: <1434421398-6613-1-git-send-email-srutherford@google.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In order to enable userspace PIC support, the userspace PIC needs to be able to inject local interrupt requests. This adds the ioctl KVM_REQUEST_PIC_INJECTION and kvm exit KVM_EXIT_GET_EXTINT. The vm ioctl KVM_REQUEST_PIC_INJECTION makes a KVM_REQ_EVENT request on the BSP, which causes the BSP to exit to userspace to fetch the vector of the underlying external interrupt, which the BSP then injects into the guest. This matches the PIC spec, and is necessary to boot Windows. Compiles for x86. Update: Boots Windows and passes the KVM Unit Tests. Signed-off-by: Steve Rutherford --- Documentation/virtual/kvm/api.txt | 21 +++++++++++++++ arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/irq.c | 22 +++++++++++++-- arch/x86/kvm/lapic.c | 7 +++++ arch/x86/kvm/lapic.h | 2 ++ arch/x86/kvm/x86.c | 57 ++++++++++++++++++++++++++++++++++++--- include/uapi/linux/kvm.h | 11 ++++++++ 7 files changed, 117 insertions(+), 5 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index e5e3e94..b2e48a0 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2979,6 +2979,19 @@ len must be a multiple of sizeof(struct kvm_s390_irq). It must be > 0 and it must not exceed (max_vcpus + 32) * sizeof(struct kvm_s390_irq), which is the maximum number of possibly pending cpu-local interrupts. +4.96 KVM_REQUEST_PIC_INJECTION + +Capability: KVM_CAP_SPLIT_IRQCHIP +Type: VM ioctl +Parameters: struct kvm_pic_injection (in) +Returns: 0 on success, -1 on error. + +Informs the kernel that userspace has a pending external interrupt for +the specified cpu. + +struct kvm_pic_injection { + __u32 cpu; +}; 5. The kvm_run structure ------------------------ @@ -3270,6 +3283,14 @@ the userspace IOAPIC should process the EOI and retrigger the interrupt if it is still asserted. Vector is the LAPIC interrupt vector for which the EOI was received. + /* KVM_EXIT_GET_EXTINT */ + struct { + __u8 vector; + } extint; +Used when a VCPU needs to exit to userspace to fetch an external interrupt +vector from a userspace PIC (which is necessary when KVM_CAP_SPLIT_IRQCHIP +is enabled). The vector should be stored in the exit struct upon reentry. + /* Fix the size of the union. */ char padding[256]; }; diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 4f439ff..0e8b0fc 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -543,6 +543,8 @@ struct kvm_vcpu_arch { u64 eoi_exit_bitmaps[4]; int pending_ioapic_eoi; + bool userspace_extint_available; + int pending_external_vector; }; struct kvm_lpage_info { diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index a1ec6a50..e9ecade 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -38,12 +38,25 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) EXPORT_SYMBOL(kvm_cpu_has_pending_timer); /* + * check if there is a pending userspace external interrupt + */ +static int pending_userspace_extint(struct kvm_vcpu *v) +{ + return v->arch.userspace_extint_available || + v->arch.pending_external_vector != -1; +} + +/* * check if there is pending interrupt from * non-APIC source without intack. */ static int kvm_cpu_has_extint(struct kvm_vcpu *v) { - if (kvm_apic_accept_pic_intr(v)) + u8 accept = kvm_apic_accept_pic_intr(v); + + if (accept && irqchip_split(v->kvm)) + return pending_userspace_extint(v); + else if (accept) return pic_irqchip(v->kvm)->output; /* PIC */ else return 0; @@ -91,7 +104,12 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt); */ static int kvm_cpu_get_extint(struct kvm_vcpu *v) { - if (kvm_cpu_has_extint(v)) + if (irqchip_split(v->kvm) && kvm_cpu_has_extint(v)) { + int vector = v->arch.pending_external_vector; + + v->arch.pending_external_vector = -1; + return vector; + } else if (kvm_cpu_has_extint(v)) return kvm_pic_read_irq(v->kvm); /* PIC */ return -1; } diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 45be02b..932ab94 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2094,3 +2094,10 @@ void kvm_lapic_init(void) jump_label_rate_limit(&apic_hw_disabled, HZ); jump_label_rate_limit(&apic_sw_disabled, HZ); } + +void kvm_request_pic_injection(struct kvm_vcpu *vcpu) +{ + vcpu->arch.userspace_extint_available = true; + kvm_make_request(KVM_REQ_EVENT, vcpu); + kvm_vcpu_kick(vcpu); +} diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 71b150c..7831e4d 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -63,6 +63,8 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq, unsigned long *dest_map); int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type); +void kvm_request_pic_injection(struct kvm_vcpu *vcpu); + bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, struct kvm_lapic_irq *irq, int *r, unsigned long *dest_map); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a1a1c4a..0bacd82 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -65,6 +65,8 @@ #include #include +#define GET_VECTOR_FROM_USERSPACE 1 + #define MAX_IO_MSRS 256 #define KVM_MAX_MCE_BANKS 32 #define KVM_MCE_CAP_SUPPORTED (MCG_CTL_P | MCG_SER_P) @@ -4217,6 +4219,28 @@ long kvm_arch_vm_ioctl(struct file *filp, r = kvm_vm_ioctl_enable_cap(kvm, &cap); break; } + case KVM_REQUEST_PIC_INJECTION: { + struct kvm_vcpu *vcpu; + struct kvm_pic_injection pic_injection; + + r = -EFAULT; + if (copy_from_user(&pic_injection, argp, sizeof(pic_injection))) + goto out; + r = -EEXIST; + if (!irqchip_split(kvm)) + goto interrupt_unlock; + + mutex_lock(&kvm->lock); + r = -EINVAL; + if (atomic_read(&kvm->online_vcpus) < pic_injection.cpu) + goto interrupt_unlock; + vcpu = kvm->vcpus[pic_injection.cpu]; + kvm_request_pic_injection(vcpu); + r = 0; +interrupt_unlock: + mutex_unlock(&kvm->lock); + break; + } default: r = kvm_vm_ioctl_assigned_device(kvm, ioctl, arg); @@ -6194,6 +6218,17 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu) kvm_x86_ops->update_cr8_intercept(vcpu, tpr, max_irr); } +static int kvm_accept_request_pic_injection(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.userspace_extint_available && + kvm_apic_accept_pic_intr(vcpu)) { + vcpu->arch.userspace_extint_available = false; + return true; + } else + return false; + +} + static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win) { int r; @@ -6258,7 +6293,12 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win) return r; } if (kvm_x86_ops->interrupt_allowed(vcpu)) { - kvm_queue_interrupt(vcpu, kvm_cpu_get_interrupt(vcpu), + if (irqchip_split(vcpu->kvm) && + kvm_accept_request_pic_injection(vcpu)) { + return GET_VECTOR_FROM_USERSPACE; + } + kvm_queue_interrupt(vcpu, + kvm_cpu_get_interrupt(vcpu), false); kvm_x86_ops->set_irq(vcpu); } @@ -6419,13 +6459,19 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) } if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) { + int event; kvm_apic_accept_events(vcpu); if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) { r = 1; goto out; } - - if (inject_pending_event(vcpu, req_int_win) != 0) + event = inject_pending_event(vcpu, req_int_win); + if (event == GET_VECTOR_FROM_USERSPACE) { + vcpu->run->exit_reason = KVM_EXIT_GET_EXTINT; + kvm_make_request(KVM_REQ_EVENT, vcpu); + r = 0; + goto out; + } else if (event != 0) req_immediate_exit = true; /* enable NMI/IRQ window open exits if needed */ else if (vcpu->arch.nmi_pending) @@ -6742,6 +6788,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) if (vcpu->sigset_active) sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); + if (vcpu->run->exit_reason == KVM_EXIT_GET_EXTINT) + vcpu->arch.pending_external_vector = vcpu->run->extint.vector; + if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) { kvm_vcpu_block(vcpu); kvm_apic_accept_events(vcpu); @@ -7531,6 +7580,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) kvm_async_pf_hash_reset(vcpu); kvm_pmu_init(vcpu); + vcpu->arch.pending_external_vector = -1; + return 0; fail_free_wbinvd_dirty_mask: free_cpumask_var(vcpu->arch.wbinvd_dirty_mask); diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 826a08d..9887cfd 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -184,6 +184,7 @@ struct kvm_s390_skeys { #define KVM_EXIT_SYSTEM_EVENT 24 #define KVM_EXIT_S390_STSI 25 #define KVM_EXIT_IOAPIC_EOI 26 +#define KVM_EXIT_GET_EXTINT 27 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -334,6 +335,10 @@ struct kvm_run { struct { __u8 vector; } eoi; + /* KVM_EXIT_GET_EXTINT */ + struct { + __u8 vector; + } extint; /* Fix the size of the union. */ char padding[256]; }; @@ -1003,6 +1008,10 @@ struct kvm_device_attr { __u64 addr; /* userspace address of attr data */ }; +struct kvm_pic_injection { + __u32 cpu; +}; + #define KVM_DEV_VFIO_GROUP 1 #define KVM_DEV_VFIO_GROUP_ADD 1 #define KVM_DEV_VFIO_GROUP_DEL 2 @@ -1206,6 +1215,8 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_S390_IRQ_STATE */ #define KVM_S390_SET_IRQ_STATE _IOW(KVMIO, 0xb5, struct kvm_s390_irq_state) #define KVM_S390_GET_IRQ_STATE _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state) +/* Available with KVM_CAP_SPLIT_IRQCHIP */ +#define KVM_REQUEST_PIC_INJECTION _IOW(KVMIO, 0xb7, struct kvm_pic_injection) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)