mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
hvm: Support injection of virtual NMIs and clean up ExtInt handling in general.
Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
eed63189da
commit
19636447b8
17 files changed
+262
-213
No files matched your search
+34
-27
@@ -285,43 +285,49 @@ void hvm_set_callback_via(struct domain *d, uint64_t via)
|
||||
}
|
||||
}
|
||||
|
||||
int cpu_has_pending_irq(struct vcpu *v)
|
||||
enum hvm_intack hvm_vcpu_has_pending_irq(struct vcpu *v)
|
||||
{
|
||||
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
|
||||
|
||||
/* APIC */
|
||||
if ( unlikely(v->arch.hvm_vcpu.nmi_pending) )
|
||||
return hvm_intack_nmi;
|
||||
|
||||
if ( vlapic_has_interrupt(v) != -1 )
|
||||
return 1;
|
||||
return hvm_intack_lapic;
|
||||
|
||||
/* PIC */
|
||||
if ( !vlapic_accept_pic_intr(v) )
|
||||
return 0;
|
||||
return hvm_intack_none;
|
||||
|
||||
return plat->vpic[0].int_output;
|
||||
return plat->vpic[0].int_output ? hvm_intack_pic : hvm_intack_none;
|
||||
}
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type)
|
||||
int hvm_vcpu_ack_pending_irq(struct vcpu *v, enum hvm_intack type, int *vector)
|
||||
{
|
||||
int vector;
|
||||
switch ( type )
|
||||
{
|
||||
case hvm_intack_nmi:
|
||||
return test_and_clear_bool(v->arch.hvm_vcpu.nmi_pending);
|
||||
case hvm_intack_lapic:
|
||||
return ((*vector = cpu_get_apic_interrupt(v)) != -1);
|
||||
case hvm_intack_pic:
|
||||
ASSERT(v->vcpu_id == 0);
|
||||
return ((*vector = cpu_get_pic_interrupt(v)) != -1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( (vector = cpu_get_apic_interrupt(v, type)) != -1 )
|
||||
return vector;
|
||||
|
||||
if ( (v->vcpu_id == 0) &&
|
||||
((vector = cpu_get_pic_interrupt(v, type)) != -1) )
|
||||
return vector;
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_isa_irq_vector(struct vcpu *v, int isa_irq, int type)
|
||||
int get_isa_irq_vector(struct vcpu *v, int isa_irq, enum hvm_intack src)
|
||||
{
|
||||
unsigned int gsi = hvm_isa_irq_to_gsi(isa_irq);
|
||||
|
||||
if ( type == APIC_DM_EXTINT )
|
||||
if ( src == hvm_intack_pic )
|
||||
return (v->domain->arch.hvm_domain.vpic[isa_irq >> 3].irq_base
|
||||
+ (isa_irq & 7));
|
||||
|
||||
ASSERT(src == hvm_intack_lapic);
|
||||
return domain_vioapic(v->domain)->redirtbl[gsi].fields.vector;
|
||||
}
|
||||
|
||||
@@ -337,19 +343,20 @@ int is_isa_irq_masked(struct vcpu *v, int isa_irq)
|
||||
domain_vioapic(v->domain)->redirtbl[gsi].fields.mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: 1. Should not need special treatment of event-channel events.
|
||||
* 2. Should take notice of interrupt shadows (or clear them).
|
||||
*/
|
||||
int hvm_local_events_need_delivery(struct vcpu *v)
|
||||
{
|
||||
int pending;
|
||||
enum hvm_intack type;
|
||||
|
||||
pending = (vcpu_info(v, evtchn_upcall_pending) || cpu_has_pending_irq(v));
|
||||
if ( unlikely(pending) )
|
||||
pending = hvm_interrupts_enabled(v);
|
||||
/* TODO: Get rid of event-channel special case. */
|
||||
if ( vcpu_info(v, evtchn_upcall_pending) )
|
||||
type = hvm_intack_pic;
|
||||
else
|
||||
type = hvm_vcpu_has_pending_irq(v);
|
||||
|
||||
return pending;
|
||||
if ( likely(type == hvm_intack_none) )
|
||||
return 0;
|
||||
|
||||
return hvm_interrupts_enabled(v, type);
|
||||
}
|
||||
|
||||
#if 0 /* Keep for debugging */
|
||||
|
||||
+82
-64
@@ -15,7 +15,6 @@
|
||||
* 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>
|
||||
@@ -39,100 +38,119 @@
|
||||
#include <xen/domain_page.h>
|
||||
#include <asm/hvm/trace.h>
|
||||
|
||||
/*
|
||||
* Most of this code is copied from vmx_io.c and modified
|
||||
* to be suitable for SVM.
|
||||
*/
|
||||
|
||||
static inline int svm_inject_extint(struct vcpu *v, int trap)
|
||||
static void svm_inject_dummy_vintr(struct vcpu *v)
|
||||
{
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
vintr_t intr = vmcb->vintr;
|
||||
|
||||
/* Update only relevant fields */
|
||||
intr.fields.irq = 1;
|
||||
intr.fields.intr_masking = 1;
|
||||
intr.fields.vector = trap;
|
||||
intr.fields.vector = 0;
|
||||
intr.fields.prio = 0xF;
|
||||
intr.fields.ign_tpr = 1;
|
||||
vmcb->vintr = intr;
|
||||
}
|
||||
|
||||
static void svm_inject_nmi(struct vcpu *v)
|
||||
{
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
eventinj_t event;
|
||||
|
||||
return 0;
|
||||
event.bytes = 0;
|
||||
event.fields.v = 1;
|
||||
event.fields.type = EVENTTYPE_NMI;
|
||||
event.fields.vector = 2;
|
||||
|
||||
ASSERT(vmcb->eventinj.fields.v == 0);
|
||||
vmcb->eventinj = event;
|
||||
}
|
||||
|
||||
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 = EVENTTYPE_INTR;
|
||||
event.fields.vector = vector;
|
||||
|
||||
ASSERT(vmcb->eventinj.fields.v == 0);
|
||||
vmcb->eventinj = event;
|
||||
}
|
||||
|
||||
asmlinkage void svm_intr_assist(void)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
int intr_type = APIC_DM_EXTINT;
|
||||
int intr_vector = -1;
|
||||
enum hvm_intack intr_source;
|
||||
int intr_vector;
|
||||
|
||||
/*
|
||||
* Previous Interrupt delivery caused this intercept?
|
||||
* Previous event delivery caused this intercept?
|
||||
* This will happen if the injection is latched by the processor (hence
|
||||
* clearing vintr.fields.irq) but then subsequently a fault occurs (e.g.,
|
||||
* due to lack of shadow mapping of guest IDT or guest-kernel stack).
|
||||
*
|
||||
* NB. Exceptions that fault during delivery are lost. This needs to be
|
||||
* fixed but we'll usually get away with it since faults are usually
|
||||
* idempotent. But this isn't the case for e.g. software interrupts!
|
||||
* clearing vintr.fields.irq or eventinj.v) but then subsequently a fault
|
||||
* occurs (e.g., due to lack of shadow mapping of guest IDT or guest-kernel
|
||||
* stack).
|
||||
*/
|
||||
if ( vmcb->exitintinfo.fields.v && (vmcb->exitintinfo.fields.type == 0) )
|
||||
if ( vmcb->exitintinfo.fields.v )
|
||||
{
|
||||
intr_vector = vmcb->exitintinfo.fields.vector;
|
||||
vmcb->eventinj = vmcb->exitintinfo;
|
||||
vmcb->exitintinfo.bytes = 0;
|
||||
HVMTRACE_1D(REINJ_VIRQ, v, intr_vector);
|
||||
svm_inject_extint(v, intr_vector);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Previous interrupt still pending? This occurs if we return from VMRUN
|
||||
* very early in the entry-to-guest process. Usually this is because an
|
||||
* external physical interrupt was pending when we executed VMRUN.
|
||||
*/
|
||||
if ( vmcb->vintr.fields.irq )
|
||||
return;
|
||||
|
||||
/* Crank the handle on interrupt state and check for new interrrupts. */
|
||||
/* Crank the handle on interrupt state. */
|
||||
pt_update_irq(v);
|
||||
hvm_set_callback_irq_level();
|
||||
if ( !cpu_has_pending_irq(v) )
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the guest can't take an interrupt right now, create a 'fake'
|
||||
* virtual interrupt on to intercept as soon as the guest _can_ take
|
||||
* interrupts. Do not obtain the next interrupt from the vlapic/pic
|
||||
* if unable to inject.
|
||||
*
|
||||
* Also do this if there is an exception pending. This is because
|
||||
* the delivery of the exception 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.
|
||||
*/
|
||||
if ( irq_masked(vmcb->rflags) || vmcb->interrupt_shadow
|
||||
|| vmcb->eventinj.fields.v )
|
||||
do {
|
||||
intr_source = hvm_vcpu_has_pending_irq(v);
|
||||
if ( likely(intr_source == hvm_intack_none) )
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the guest can't take an interrupt right now, create a 'fake'
|
||||
* virtual interrupt on to intercept as soon as the guest _can_ take
|
||||
* interrupts. Do not obtain the next interrupt from the vlapic/pic
|
||||
* if unable to inject.
|
||||
*
|
||||
* Also do this if there is an injection already pending. This is
|
||||
* because 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
if ( !hvm_interrupts_enabled(v, intr_source) ||
|
||||
vmcb->eventinj.fields.v )
|
||||
{
|
||||
vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;
|
||||
HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1);
|
||||
svm_inject_dummy_vintr(v);
|
||||
return;
|
||||
}
|
||||
} while ( !hvm_vcpu_ack_pending_irq(v, intr_source, &intr_vector) );
|
||||
|
||||
if ( intr_source == hvm_intack_nmi )
|
||||
{
|
||||
vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;
|
||||
HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1);
|
||||
svm_inject_extint(v, 0x0); /* actual vector doesn't matter */
|
||||
return;
|
||||
svm_inject_nmi(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
HVMTRACE_2D(INJ_VIRQ, v, intr_vector, /*fake=*/ 0);
|
||||
svm_inject_extint(v, intr_vector);
|
||||
pt_intr_post(v, intr_vector, intr_source);
|
||||
}
|
||||
|
||||
/* Okay, we can deliver the interrupt: grab it and update PIC state. */
|
||||
intr_vector = cpu_get_interrupt(v, &intr_type);
|
||||
BUG_ON(intr_vector < 0);
|
||||
|
||||
HVMTRACE_2D(INJ_VIRQ, v, intr_vector, /*fake=*/ 0);
|
||||
svm_inject_extint(v, intr_vector);
|
||||
|
||||
pt_intr_post(v, intr_vector, intr_type);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+15
-25
@@ -312,26 +312,8 @@ int svm_vmcb_save(struct vcpu *v, struct hvm_hw_cpu *c)
|
||||
c->sysenter_esp = vmcb->sysenter_esp;
|
||||
c->sysenter_eip = vmcb->sysenter_eip;
|
||||
|
||||
/* Save any event/interrupt that was being injected when we last
|
||||
* exited. Although there are three(!) VMCB fields that can contain
|
||||
* active events, we only need to save at most one: because the
|
||||
* intr_assist logic never delivers an IRQ when any other event is
|
||||
* active, we know that the only possible collision is if we inject
|
||||
* a fault while exitintinfo contains a valid event (the delivery of
|
||||
* which caused the last exit). In that case replaying just the
|
||||
* first event should cause the same behaviour when we restore. */
|
||||
if ( vmcb->vintr.fields.irq
|
||||
&& /* Check it's not a fake interrupt (see svm_intr_assist()) */
|
||||
!(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
|
||||
{
|
||||
c->pending_vector = vmcb->vintr.fields.vector;
|
||||
c->pending_type = 0; /* External interrupt */
|
||||
c->pending_error_valid = 0;
|
||||
c->pending_reserved = 0;
|
||||
c->pending_valid = 1;
|
||||
c->error_code = 0;
|
||||
}
|
||||
else if ( vmcb->exitintinfo.fields.v )
|
||||
/* Save any event/interrupt that was being injected when we last exited. */
|
||||
if ( vmcb->exitintinfo.fields.v )
|
||||
{
|
||||
c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff;
|
||||
c->error_code = vmcb->exitintinfo.fields.errorcode;
|
||||
@@ -569,10 +551,15 @@ static inline void svm_restore_dr(struct vcpu *v)
|
||||
__restore_debug_registers(v);
|
||||
}
|
||||
|
||||
static int svm_interrupts_enabled(struct vcpu *v)
|
||||
static int svm_interrupts_enabled(struct vcpu *v, enum hvm_intack type)
|
||||
{
|
||||
unsigned long eflags = v->arch.hvm_svm.vmcb->rflags;
|
||||
return !irq_masked(eflags);
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
|
||||
if ( type == hvm_intack_nmi )
|
||||
return !vmcb->interrupt_shadow;
|
||||
|
||||
ASSERT((type == hvm_intack_pic) || (type == hvm_intack_lapic));
|
||||
return !irq_masked(vmcb->rflags) && !vmcb->interrupt_shadow;
|
||||
}
|
||||
|
||||
static int svm_guest_x86_mode(struct vcpu *v)
|
||||
@@ -2160,11 +2147,14 @@ static inline void svm_do_msr_access(
|
||||
|
||||
static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb)
|
||||
{
|
||||
enum hvm_intack type = hvm_vcpu_has_pending_irq(current);
|
||||
|
||||
__update_guest_eip(vmcb, 1);
|
||||
|
||||
/* Check for interrupt not handled or new interrupt. */
|
||||
if ( (vmcb->rflags & X86_EFLAGS_IF) &&
|
||||
(vmcb->vintr.fields.irq || cpu_has_pending_irq(current)) ) {
|
||||
if ( vmcb->eventinj.fields.v ||
|
||||
((type != hvm_intack_none) && hvm_interrupts_enabled(current, type)) )
|
||||
{
|
||||
HVMTRACE_1D(HLT, current, /*int pending=*/ 1);
|
||||
return;
|
||||
}
|
||||
|
||||
+19
-15
@@ -254,17 +254,11 @@ static void ioapic_inj_irq(
|
||||
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "irq %d trig %d deliv %d",
|
||||
vector, trig_mode, delivery_mode);
|
||||
|
||||
switch ( delivery_mode )
|
||||
{
|
||||
case dest_Fixed:
|
||||
case dest_LowestPrio:
|
||||
if ( vlapic_set_irq(target, vector, trig_mode) )
|
||||
vcpu_kick(vlapic_vcpu(target));
|
||||
break;
|
||||
default:
|
||||
gdprintk(XENLOG_WARNING, "error delivery mode %d\n", delivery_mode);
|
||||
break;
|
||||
}
|
||||
ASSERT((delivery_mode == dest_Fixed) ||
|
||||
(delivery_mode == dest_LowestPrio));
|
||||
|
||||
if ( vlapic_set_irq(target, vector, trig_mode) )
|
||||
vcpu_kick(vlapic_vcpu(target));
|
||||
}
|
||||
|
||||
static uint32_t ioapic_get_delivery_bitmask(
|
||||
@@ -368,7 +362,6 @@ static void vioapic_deliver(struct hvm_hw_vioapic *vioapic, int irq)
|
||||
}
|
||||
|
||||
case dest_Fixed:
|
||||
case dest_ExtINT:
|
||||
{
|
||||
uint8_t bit;
|
||||
for ( bit = 0; deliver_bitmask != 0; bit++ )
|
||||
@@ -393,10 +386,21 @@ static void vioapic_deliver(struct hvm_hw_vioapic *vioapic, int irq)
|
||||
break;
|
||||
}
|
||||
|
||||
case dest_SMI:
|
||||
case dest_NMI:
|
||||
case dest_INIT:
|
||||
case dest__reserved_2:
|
||||
{
|
||||
uint8_t bit;
|
||||
for ( bit = 0; deliver_bitmask != 0; bit++ )
|
||||
{
|
||||
if ( !(deliver_bitmask & (1 << bit)) )
|
||||
continue;
|
||||
deliver_bitmask &= ~(1 << bit);
|
||||
if ( ((v = vioapic_domain(vioapic)->vcpu[bit]) != NULL) &&
|
||||
!test_and_set_bool(v->arch.hvm_vcpu.nmi_pending) )
|
||||
vcpu_kick(v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n",
|
||||
delivery_mode);
|
||||
|
||||
@@ -294,7 +294,8 @@ static int vlapic_accept_irq(struct vcpu *v, int delivery_mode,
|
||||
break;
|
||||
|
||||
case APIC_DM_NMI:
|
||||
gdprintk(XENLOG_WARNING, "Ignoring guest NMI\n");
|
||||
if ( !test_and_set_bool(v->arch.hvm_vcpu.nmi_pending) )
|
||||
vcpu_kick(v);
|
||||
break;
|
||||
|
||||
case APIC_DM_INIT:
|
||||
@@ -747,7 +748,7 @@ int vlapic_has_interrupt(struct vcpu *v)
|
||||
return highest_irr;
|
||||
}
|
||||
|
||||
int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
|
||||
int cpu_get_apic_interrupt(struct vcpu *v)
|
||||
{
|
||||
int vector = vlapic_has_interrupt(v);
|
||||
struct vlapic *vlapic = vcpu_vlapic(v);
|
||||
@@ -757,8 +758,6 @@ int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
|
||||
|
||||
vlapic_set_vector(vector, &vlapic->regs->data[APIC_ISR]);
|
||||
vlapic_clear_irr(vector, vlapic);
|
||||
|
||||
*mode = APIC_DM_FIXED;
|
||||
return vector;
|
||||
}
|
||||
|
||||
|
||||
+52
-50
@@ -102,8 +102,8 @@ static void update_tpr_threshold(struct vlapic *vlapic)
|
||||
|
||||
asmlinkage void vmx_intr_assist(void)
|
||||
{
|
||||
int has_ext_irq, intr_vector, intr_type = 0;
|
||||
unsigned long eflags, intr_shadow;
|
||||
int intr_vector;
|
||||
enum hvm_intack intr_source;
|
||||
struct vcpu *v = current;
|
||||
unsigned int idtv_info_field;
|
||||
unsigned long inst_len;
|
||||
@@ -114,65 +114,67 @@ asmlinkage void vmx_intr_assist(void)
|
||||
|
||||
update_tpr_threshold(vcpu_vlapic(v));
|
||||
|
||||
has_ext_irq = cpu_has_pending_irq(v);
|
||||
do {
|
||||
intr_source = hvm_vcpu_has_pending_irq(v);
|
||||
|
||||
if ( unlikely(v->arch.hvm_vmx.vector_injected) )
|
||||
{
|
||||
v->arch.hvm_vmx.vector_injected = 0;
|
||||
if ( unlikely(has_ext_irq) )
|
||||
enable_irq_window(v);
|
||||
return;
|
||||
}
|
||||
if ( unlikely(v->arch.hvm_vmx.vector_injected) )
|
||||
{
|
||||
v->arch.hvm_vmx.vector_injected = 0;
|
||||
if ( unlikely(intr_source != hvm_intack_none) )
|
||||
enable_irq_window(v);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This could be moved earlier in the VMX resume sequence. */
|
||||
idtv_info_field = __vmread(IDT_VECTORING_INFO_FIELD);
|
||||
if ( unlikely(idtv_info_field & INTR_INFO_VALID_MASK) )
|
||||
{
|
||||
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
|
||||
/* This could be moved earlier in the VMX resume sequence. */
|
||||
idtv_info_field = __vmread(IDT_VECTORING_INFO_FIELD);
|
||||
if ( unlikely(idtv_info_field & INTR_INFO_VALID_MASK) )
|
||||
{
|
||||
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
|
||||
|
||||
/*
|
||||
* Safe: the length will only be interpreted for software
|
||||
* exceptions and interrupts. If we get here then delivery of some
|
||||
* event caused a fault, and this always results in defined
|
||||
* VM_EXIT_INSTRUCTION_LEN.
|
||||
*/
|
||||
inst_len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe */
|
||||
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
|
||||
|
||||
if ( unlikely(idtv_info_field & 0x800) ) /* valid error code */
|
||||
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
|
||||
__vmread(IDT_VECTORING_ERROR_CODE));
|
||||
if ( unlikely(intr_source != hvm_intack_none) )
|
||||
enable_irq_window(v);
|
||||
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( likely(intr_source == hvm_intack_none) )
|
||||
return;
|
||||
|
||||
/*
|
||||
* Safe: the length will only be interpreted for software exceptions
|
||||
* and interrupts. If we get here then delivery of some event caused a
|
||||
* fault, and this always results in defined VM_EXIT_INSTRUCTION_LEN.
|
||||
* TODO: Better NMI handling. Shouldn't wait for EFLAGS.IF==1, but
|
||||
* should wait for exit from 'NMI blocking' window (NMI injection to
|
||||
* next IRET). This requires us to use the new 'virtual NMI' support.
|
||||
*/
|
||||
inst_len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe */
|
||||
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
|
||||
|
||||
if ( unlikely(idtv_info_field & 0x800) ) /* valid error code */
|
||||
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
|
||||
__vmread(IDT_VECTORING_ERROR_CODE));
|
||||
if ( unlikely(has_ext_irq) )
|
||||
if ( !hvm_interrupts_enabled(v, intr_source) )
|
||||
{
|
||||
enable_irq_window(v);
|
||||
return;
|
||||
}
|
||||
} while ( !hvm_vcpu_ack_pending_irq(v, intr_source, &intr_vector) );
|
||||
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( likely(!has_ext_irq) )
|
||||
return;
|
||||
|
||||
intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
|
||||
if ( unlikely(intr_shadow & (VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS)) )
|
||||
if ( intr_source == hvm_intack_nmi )
|
||||
{
|
||||
enable_irq_window(v);
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility");
|
||||
return;
|
||||
vmx_inject_nmi(v);
|
||||
}
|
||||
|
||||
eflags = __vmread(GUEST_RFLAGS);
|
||||
if ( irq_masked(eflags) )
|
||||
else
|
||||
{
|
||||
enable_irq_window(v);
|
||||
return;
|
||||
HVMTRACE_2D(INJ_VIRQ, v, intr_vector, /*fake=*/ 0);
|
||||
vmx_inject_extint(v, intr_vector);
|
||||
pt_intr_post(v, intr_vector, intr_source);
|
||||
}
|
||||
|
||||
intr_vector = cpu_get_interrupt(v, &intr_type);
|
||||
BUG_ON(intr_vector < 0);
|
||||
|
||||
HVMTRACE_2D(INJ_VIRQ, v, intr_vector, /*fake=*/ 0);
|
||||
vmx_inject_extint(v, intr_vector, VMX_DELIVER_NO_ERROR_CODE);
|
||||
|
||||
pt_intr_post(v, intr_vector, intr_type);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1115,16 +1115,26 @@ static int vmx_nx_enabled(struct vcpu *v)
|
||||
return v->arch.hvm_vmx.efer & EFER_NX;
|
||||
}
|
||||
|
||||
static int vmx_interrupts_enabled(struct vcpu *v)
|
||||
static int vmx_interrupts_enabled(struct vcpu *v, enum hvm_intack type)
|
||||
{
|
||||
unsigned long eflags = __vmread(GUEST_RFLAGS);
|
||||
return !irq_masked(eflags);
|
||||
}
|
||||
unsigned long intr_shadow, eflags;
|
||||
|
||||
ASSERT(v == current);
|
||||
|
||||
intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
|
||||
intr_shadow &= VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS;
|
||||
|
||||
if ( type == hvm_intack_nmi )
|
||||
return !intr_shadow;
|
||||
|
||||
ASSERT((type == hvm_intack_pic) || (type == hvm_intack_lapic));
|
||||
eflags = __vmread(GUEST_RFLAGS);
|
||||
return !irq_masked(eflags) && !intr_shadow;
|
||||
}
|
||||
|
||||
static void vmx_update_host_cr3(struct vcpu *v)
|
||||
{
|
||||
ASSERT( (v == current) || !vcpu_runnable(v) );
|
||||
ASSERT((v == current) || !vcpu_runnable(v));
|
||||
vmx_vmcs_enter(v);
|
||||
__vmwrite(HOST_CR3, v->arch.cr3);
|
||||
vmx_vmcs_exit(v);
|
||||
@@ -1132,7 +1142,7 @@ static void vmx_update_host_cr3(struct vcpu *v)
|
||||
|
||||
static void vmx_update_guest_cr3(struct vcpu *v)
|
||||
{
|
||||
ASSERT( (v == current) || !vcpu_runnable(v) );
|
||||
ASSERT((v == current) || !vcpu_runnable(v));
|
||||
vmx_vmcs_enter(v);
|
||||
__vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr3);
|
||||
vmx_vmcs_exit(v);
|
||||
|
||||
@@ -499,7 +499,7 @@ void vpic_irq_negative_edge(struct domain *d, int irq)
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
int cpu_get_pic_interrupt(struct vcpu *v)
|
||||
{
|
||||
int irq, vector;
|
||||
struct hvm_hw_vpic *vpic = &v->domain->arch.hvm_domain.vpic[0];
|
||||
@@ -512,6 +512,5 @@ int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
return -1;
|
||||
|
||||
vector = vpic[irq >> 3].irq_base + (irq & 7);
|
||||
*type = APIC_DM_EXTINT;
|
||||
return vector;
|
||||
}
|
||||
@@ -155,7 +155,8 @@ void pt_update_irq(struct vcpu *v)
|
||||
}
|
||||
}
|
||||
|
||||
static struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type)
|
||||
static struct periodic_time *is_pt_irq(
|
||||
struct vcpu *v, int vector, enum hvm_intack src)
|
||||
{
|
||||
struct list_head *head = &v->arch.hvm_vcpu.tm_list;
|
||||
struct periodic_time *pt;
|
||||
@@ -174,7 +175,7 @@ static struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type)
|
||||
return pt;
|
||||
}
|
||||
|
||||
vec = get_isa_irq_vector(v, pt->irq, type);
|
||||
vec = get_isa_irq_vector(v, pt->irq, src);
|
||||
|
||||
/* RTC irq need special care */
|
||||
if ( (vector != vec) || (pt->irq == 8 && !is_rtc_periodic_irq(rtc)) )
|
||||
@@ -186,7 +187,7 @@ static struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pt_intr_post(struct vcpu *v, int vector, int type)
|
||||
void pt_intr_post(struct vcpu *v, int vector, enum hvm_intack src)
|
||||
{
|
||||
struct periodic_time *pt;
|
||||
time_cb *cb;
|
||||
@@ -194,7 +195,7 @@ void pt_intr_post(struct vcpu *v, int vector, int type)
|
||||
|
||||
spin_lock(&v->arch.hvm_vcpu.tm_lock);
|
||||
|
||||
pt = is_pt_irq(v, vector, type);
|
||||
pt = is_pt_irq(v, vector, src);
|
||||
if ( pt == NULL )
|
||||
{
|
||||
spin_unlock(&v->arch.hvm_vcpu.tm_lock);
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#define __ASM_EVENT_H__
|
||||
|
||||
#include <xen/shared.h>
|
||||
#include <asm/hvm/irq.h> /* cpu_has_pending_irq() */
|
||||
|
||||
static inline void vcpu_kick(struct vcpu *v)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,14 @@ typedef struct segment_register {
|
||||
u64 base;
|
||||
} __attribute__ ((packed)) segment_register_t;
|
||||
|
||||
/* Interrupt acknowledgement sources. */
|
||||
enum hvm_intack {
|
||||
hvm_intack_none,
|
||||
hvm_intack_pic,
|
||||
hvm_intack_lapic,
|
||||
hvm_intack_nmi
|
||||
};
|
||||
|
||||
/*
|
||||
* The hardware virtual machine (HVM) interface abstracts away from the
|
||||
* x86/x86_64 CPU virtualization assist specifics. Currently this interface
|
||||
@@ -106,7 +114,7 @@ struct hvm_function_table {
|
||||
int (*long_mode_enabled)(struct vcpu *v);
|
||||
int (*pae_enabled)(struct vcpu *v);
|
||||
int (*nx_enabled)(struct vcpu *v);
|
||||
int (*interrupts_enabled)(struct vcpu *v);
|
||||
int (*interrupts_enabled)(struct vcpu *v, enum hvm_intack);
|
||||
int (*guest_x86_mode)(struct vcpu *v);
|
||||
unsigned long (*get_guest_ctrl_reg)(struct vcpu *v, unsigned int num);
|
||||
unsigned long (*get_segment_base)(struct vcpu *v, enum x86_segment seg);
|
||||
@@ -199,16 +207,16 @@ hvm_long_mode_enabled(struct vcpu *v)
|
||||
#define hvm_long_mode_enabled(v) (v,0)
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
static inline int
|
||||
hvm_pae_enabled(struct vcpu *v)
|
||||
{
|
||||
return hvm_funcs.pae_enabled(v);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hvm_interrupts_enabled(struct vcpu *v)
|
||||
hvm_interrupts_enabled(struct vcpu *v, enum hvm_intack type)
|
||||
{
|
||||
return hvm_funcs.interrupts_enabled(v);
|
||||
return hvm_funcs.interrupts_enabled(v, type);
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
#include <xen/types.h>
|
||||
#include <xen/spinlock.h>
|
||||
#include <asm/hvm/hvm.h>
|
||||
#include <asm/hvm/vpic.h>
|
||||
#include <asm/hvm/vioapic.h>
|
||||
#include <public/hvm/save.h>
|
||||
|
||||
|
||||
struct hvm_irq {
|
||||
/*
|
||||
* Virtual interrupt wires for a single PCI bus.
|
||||
@@ -58,7 +58,6 @@ struct hvm_irq {
|
||||
HVMIRQ_callback_gsi,
|
||||
HVMIRQ_callback_pci_intx
|
||||
} callback_via_type;
|
||||
uint32_t pad; /* So the next field will be aligned */
|
||||
};
|
||||
union {
|
||||
uint32_t gsi;
|
||||
@@ -115,9 +114,12 @@ void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
|
||||
void hvm_set_callback_irq_level(void);
|
||||
void hvm_set_callback_via(struct domain *d, uint64_t via);
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type);
|
||||
int cpu_has_pending_irq(struct vcpu *v);
|
||||
int get_isa_irq_vector(struct vcpu *vcpu, int irq, int type);
|
||||
/* Check/Acknowledge next pending interrupt. */
|
||||
enum hvm_intack hvm_vcpu_has_pending_irq(struct vcpu *v);
|
||||
int hvm_vcpu_ack_pending_irq(
|
||||
struct vcpu *v, enum hvm_intack type, int *vector);
|
||||
|
||||
int get_isa_irq_vector(struct vcpu *vcpu, int irq, enum hvm_intack src);
|
||||
int is_isa_irq_masked(struct vcpu *v, int isa_irq);
|
||||
|
||||
#endif /* __ASM_X86_HVM_IRQ_H__ */
|
||||
@@ -30,12 +30,14 @@
|
||||
|
||||
struct hvm_vcpu {
|
||||
unsigned long hw_cr3; /* value we give to HW to use */
|
||||
unsigned long ioflags;
|
||||
struct hvm_io_op io_op;
|
||||
struct vlapic vlapic;
|
||||
s64 cache_tsc_offset;
|
||||
u64 guest_time;
|
||||
|
||||
/* Is an NMI pending for delivery to this VCPU core? */
|
||||
bool_t nmi_pending; /* NB. integrate flag with save/restore */
|
||||
|
||||
/* Lock and list for virtual platform timers. */
|
||||
spinlock_t tm_lock;
|
||||
struct list_head tm_list;
|
||||
|
||||
@@ -76,7 +76,7 @@ int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig);
|
||||
int vlapic_find_highest_irr(struct vlapic *vlapic);
|
||||
|
||||
int vlapic_has_interrupt(struct vcpu *v);
|
||||
int cpu_get_apic_interrupt(struct vcpu *v, int *mode);
|
||||
int cpu_get_apic_interrupt(struct vcpu *v);
|
||||
|
||||
int vlapic_init(struct vcpu *v);
|
||||
void vlapic_destroy(struct vcpu *v);
|
||||
|
||||
@@ -336,9 +336,16 @@ static inline void vmx_inject_sw_exception(
|
||||
instruction_len);
|
||||
}
|
||||
|
||||
static inline void vmx_inject_extint(struct vcpu *v, int trap, int error_code)
|
||||
static inline void vmx_inject_extint(struct vcpu *v, int trap)
|
||||
{
|
||||
__vmx_inject_exception(v, trap, INTR_TYPE_EXT_INTR, error_code, 0);
|
||||
__vmx_inject_exception(v, trap, INTR_TYPE_EXT_INTR,
|
||||
VMX_DELIVER_NO_ERROR_CODE, 0);
|
||||
}
|
||||
|
||||
static inline void vmx_inject_nmi(struct vcpu *v)
|
||||
{
|
||||
__vmx_inject_exception(v, 2, INTR_TYPE_NMI,
|
||||
VMX_DELIVER_NO_ERROR_CODE, 0);
|
||||
}
|
||||
|
||||
#endif /* __ASM_X86_HVM_VMX_VMX_H__ */
|
||||
@@ -32,7 +32,7 @@
|
||||
void vpic_irq_positive_edge(struct domain *d, int irq);
|
||||
void vpic_irq_negative_edge(struct domain *d, int irq);
|
||||
void vpic_init(struct domain *d);
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type);
|
||||
int cpu_get_pic_interrupt(struct vcpu *v);
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VPIC_H__ */
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <xen/timer.h>
|
||||
#include <xen/list.h>
|
||||
#include <asm/hvm/vpic.h>
|
||||
#include <asm/hvm/irq.h>
|
||||
#include <public/hvm/save.h>
|
||||
|
||||
struct HPETState;
|
||||
@@ -119,7 +120,7 @@ struct pl_time { /* platform time */
|
||||
void pt_freeze_time(struct vcpu *v);
|
||||
void pt_thaw_time(struct vcpu *v);
|
||||
void pt_update_irq(struct vcpu *v);
|
||||
void pt_intr_post(struct vcpu *v, int vector, int type);
|
||||
void pt_intr_post(struct vcpu *v, int vector, enum hvm_intack src);
|
||||
void pt_reset(struct vcpu *v);
|
||||
void pt_migrate(struct vcpu *v);
|
||||
void create_periodic_time(
|
||||
|
||||