intr.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 222 行
C
222 行
/* * intr.c: Interrupt handling for SVM. * Copyright (c) 2005, AMD Inc. * Copyright (c) 2004, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */#include <xen/config.h>#include <xen/init.h>#include <xen/mm.h>#include <xen/lib.h>#include <xen/trace.h>#include <xen/errno.h>#include <asm/cpufeature.h>#include <asm/processor.h>#include <asm/msr.h>#include <asm/paging.h>#include <asm/hvm/hvm.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/hvm/vlapic.h>#include <asm/hvm/svm/svm.h>#include <asm/hvm/svm/intr.h>#include <xen/event.h>#include <xen/kernel.h>#include <public/hvm/ioreq.h>#include <xen/domain_page.h>#include <asm/hvm/trace.h>static void svm_inject_nmi(struct vcpu *v){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; eventinj_t event; event.bytes = 0; event.fields.v = 1; event.fields.type = X86_EVENTTYPE_NMI; event.fields.vector = 2; ASSERT(vmcb->eventinj.fields.v == 0); vmcb->eventinj = event; /* * SVM does not virtualise the NMI mask, so we emulate it by intercepting * the next IRET and blocking NMI injection until the intercept triggers. */ vmcb->general1_intercepts |= GENERAL1_INTERCEPT_IRET;} static void svm_inject_extint(struct vcpu *v, int vector){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; eventinj_t event; event.bytes = 0; event.fields.v = 1; event.fields.type = X86_EVENTTYPE_EXT_INTR; event.fields.vector = vector; ASSERT(vmcb->eventinj.fields.v == 0); vmcb->eventinj = event;} static void enable_intr_window(struct vcpu *v, struct hvm_intack intack){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; vintr_t intr; ASSERT(intack.source != hvm_intsrc_none); HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1); /* * Create a dummy virtual interrupt to intercept as soon as the * guest can accept the real interrupt. * * TODO: Better NMI handling. We need a way to skip a MOV SS interrupt * shadow. This is hard to do without hardware support. We should also * track 'NMI blocking' from NMI injection until IRET. This can be done * quite easily in software by intercepting the unblocking IRET. */ intr = vmcb->vintr; intr.fields.irq = 1; intr.fields.vector = 0; intr.fields.prio = intack.vector >> 4; intr.fields.ign_tpr = (intack.source != hvm_intsrc_lapic); vmcb->vintr = intr; vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;}extern int vmsi_deliver(struct domain *d, int pirq);static int hvm_pci_msi_assert(struct domain *d, int pirq){ return vmsi_deliver(d, pirq);}static void svm_dirq_assist(struct vcpu *v){ unsigned int irq; uint32_t device, intx; struct domain *d = v->domain; struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; struct dev_intx_gsi_link *digl; if ( !iommu_enabled || (v->vcpu_id != 0) || (hvm_irq_dpci == NULL) ) return; for ( irq = find_first_bit(hvm_irq_dpci->dirq_mask, NR_IRQS); irq < NR_IRQS; irq = find_next_bit(hvm_irq_dpci->dirq_mask, NR_IRQS, irq + 1) ) { if ( !test_and_clear_bit(irq, &hvm_irq_dpci->dirq_mask) ) continue; if ( test_bit(_HVM_IRQ_DPCI_MSI, &hvm_irq_dpci->mirq[irq].flags) ) { hvm_pci_msi_assert(d, irq); continue; } stop_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, irq)]); list_for_each_entry ( digl, &hvm_irq_dpci->mirq[irq].digl_list, list ) { device = digl->device; intx = digl->intx; hvm_pci_intx_assert(d, device, intx); spin_lock(&hvm_irq_dpci->dirq_lock); hvm_irq_dpci->mirq[irq].pending++; spin_unlock(&hvm_irq_dpci->dirq_lock); } /* * Set a timer to see if the guest can finish the interrupt or not. For * example, the guest OS may unmask the PIC during boot, before the * guest driver is loaded. hvm_pci_intx_assert() may succeed, but the * guest will never deal with the irq, then the physical interrupt line * will never be deasserted. */ set_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, irq)], NOW() + PT_IRQ_TIME_OUT); }}asmlinkage void svm_intr_assist(void) { struct vcpu *v = current; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; struct hvm_intack intack; /* Crank the handle on interrupt state. */ pt_update_irq(v); svm_dirq_assist(v); do { intack = hvm_vcpu_has_pending_irq(v); if ( likely(intack.source == hvm_intsrc_none) ) return; /* * Pending IRQs must be delayed if: * 1. An event is already pending. This is despite the fact that SVM * provides a VINTR delivery method quite separate from the EVENTINJ * mechanism. The event delivery can arbitrarily delay the injection * of the vintr (for example, if the exception is handled via an * interrupt gate, hence zeroing RFLAGS.IF). In the meantime: * - the vTPR could be modified upwards, so we need to wait until * the exception is delivered before we can safely decide that an * interrupt is deliverable; and * - the guest might look at the APIC/PIC state, so we ought not to * have cleared the interrupt out of the IRR. * 2. The IRQ is masked. */ if ( unlikely(vmcb->eventinj.fields.v) || hvm_interrupt_blocked(v, intack) ) { enable_intr_window(v, intack); return; } intack = hvm_vcpu_ack_pending_irq(v, intack); } while ( intack.source == hvm_intsrc_none ); if ( intack.source == hvm_intsrc_nmi ) { svm_inject_nmi(v); } else { HVMTRACE_2D(INJ_VIRQ, v, intack.vector, /*fake=*/ 0); svm_inject_extint(v, intack.vector); pt_intr_post(v, intack); } /* Is there another IRQ to queue up behind this one? */ intack = hvm_vcpu_has_pending_irq(v); if ( unlikely(intack.source != hvm_intsrc_none) ) enable_intr_window(v, intack);}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?