intr.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 225 行
C
225 行
/* * intr.c: handling I/O, interrupts related VMX entry/exit * Copyright (c) 2004, Intel Corporation. * Copyright (c) 2004-2007, XenSource Inc. * * 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/errno.h>#include <xen/trace.h>#include <xen/event.h>#include <asm/current.h>#include <asm/cpufeature.h>#include <asm/processor.h>#include <asm/msr.h>#include <asm/hvm/hvm.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/hvm/vmx/vmx.h>#include <asm/hvm/vmx/vmcs.h>#include <asm/hvm/vpic.h>#include <asm/hvm/vlapic.h>#include <public/hvm/ioreq.h>#include <asm/hvm/trace.h>/* * A few notes on virtual NMI and INTR delivery, and interactions with * interruptibility states: * * We can only inject an ExtInt if EFLAGS.IF = 1 and no blocking by * STI nor MOV SS. Otherwise the VM entry fails. The 'virtual interrupt * pending' control causes a VM exit when all these checks succeed. It will * exit immediately after VM entry if the checks succeed at that point. * * We can only inject an NMI if no blocking by MOV SS (also, depending on * implementation, if no blocking by STI). If pin-based 'virtual NMIs' * control is specified then the NMI-blocking interruptibility flag is * also checked. The 'virtual NMI pending' control (available only in * conjunction with 'virtual NMIs') causes a VM exit when all these checks * succeed. It will exit immediately after VM entry if the checks succeed * at that point. * * Because a processor may or may not check blocking-by-STI when injecting * a virtual NMI, it will be necessary to convert that to block-by-MOV-SS * before specifying the 'virtual NMI pending' control. Otherwise we could * enter an infinite loop where we check blocking-by-STI in software and * thus delay delivery of a virtual NMI, but the processor causes immediate * VM exit because it does not check blocking-by-STI. * * Injecting a virtual NMI sets the NMI-blocking interruptibility flag only * if the 'virtual NMIs' control is set. Injecting *any* kind of event clears * the STI- and MOV-SS-blocking interruptibility-state flags. */static void enable_intr_window(struct vcpu *v, struct hvm_intack intack){ u32 *cpu_exec_control = &v->arch.hvm_vmx.exec_control; u32 ctl = CPU_BASED_VIRTUAL_INTR_PENDING; ASSERT(intack.source != hvm_intsrc_none); if ( (intack.source == hvm_intsrc_nmi) && cpu_has_vmx_vnmi ) { /* * We set MOV-SS blocking in lieu of STI blocking when delivering an * NMI. This is because it is processor-specific whether STI-blocking * blocks NMIs. Hence we *must* check for STI-blocking on NMI delivery * (otherwise vmentry will fail on processors that check for STI- * blocking) but if the processor does not check for STI-blocking then * we may immediately vmexit and hance make no progress! * (see SDM 3B 21.3, "Other Causes of VM Exits"). */ u32 intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO); if ( intr_shadow & VMX_INTR_SHADOW_STI ) { /* Having both STI-blocking and MOV-SS-blocking fails vmentry. */ intr_shadow &= ~VMX_INTR_SHADOW_STI; intr_shadow |= VMX_INTR_SHADOW_MOV_SS; __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow); } ctl = CPU_BASED_VIRTUAL_NMI_PENDING; } if ( !(*cpu_exec_control & ctl) ) { *cpu_exec_control |= ctl; __vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control); }}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 vmx_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 vmx_intr_assist(void){ struct hvm_intack intack; struct vcpu *v = current; unsigned int tpr_threshold = 0; enum hvm_intblk intblk; /* Crank the handle on interrupt state. */ pt_update_irq(v); vmx_dirq_assist(v); do { intack = hvm_vcpu_has_pending_irq(v); if ( likely(intack.source == hvm_intsrc_none) ) goto out; intblk = hvm_interrupt_blocked(v, intack); if ( intblk == hvm_intblk_tpr ) { ASSERT(vlapic_enabled(vcpu_vlapic(v))); ASSERT(intack.source == hvm_intsrc_lapic); tpr_threshold = intack.vector >> 4; goto out; } if ( (intblk != hvm_intblk_none) || (__vmread(VM_ENTRY_INTR_INFO) & INTR_INFO_VALID_MASK) ) { enable_intr_window(v, intack); goto out; } intack = hvm_vcpu_ack_pending_irq(v, intack); } while ( intack.source == hvm_intsrc_none ); if ( intack.source == hvm_intsrc_nmi ) { vmx_inject_nmi(v); } else { HVMTRACE_2D(INJ_VIRQ, v, intack.vector, /*fake=*/ 0); vmx_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); out: if ( cpu_has_vmx_tpr_shadow ) __vmwrite(TPR_THRESHOLD, tpr_threshold);}/* * 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 + -
显示快捷键?