Support level triggered interrupts with VT-x virtual interrupt delivery.
The VMCS field EOI_bitmap[] is an array of 256 bits - one for each vector. If a bit is set to '1' in the EOI_bitmap[] then the processor will trigger an EOI-induced VM-exit when it is doing EOI virtualization. The EOI-induced VM-exit results in the EOI being forwarded to the vioapic so that level triggered interrupts can be properly handled. Tested by: Anish Gupta (akgupt3@gmail.com)
This commit is contained in:
parent
3dbdfe820b
commit
30b94db8c0
@ -298,6 +298,7 @@ enum vm_exitcode {
|
||||
VM_EXITCODE_SPINUP_AP,
|
||||
VM_EXITCODE_SPINDOWN_CPU,
|
||||
VM_EXITCODE_RENDEZVOUS,
|
||||
VM_EXITCODE_IOAPIC_EOI,
|
||||
VM_EXITCODE_MAX
|
||||
};
|
||||
|
||||
@ -354,6 +355,9 @@ struct vm_exit {
|
||||
struct {
|
||||
uint64_t rflags;
|
||||
} hlt;
|
||||
struct {
|
||||
int vector;
|
||||
} ioapic_eoi;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
@ -136,6 +136,7 @@ vmcs_write(uint32_t encoding, uint64_t val)
|
||||
#define VMCS_EOI_EXIT1 0x0000201E
|
||||
#define VMCS_EOI_EXIT2 0x00002020
|
||||
#define VMCS_EOI_EXIT3 0x00002022
|
||||
#define VMCS_EOI_EXIT(vector) (VMCS_EOI_EXIT0 + ((vector) / 64) * 2)
|
||||
|
||||
/* 64-bit read-only fields */
|
||||
#define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400
|
||||
@ -318,6 +319,7 @@ vmcs_write(uint32_t encoding, uint64_t val)
|
||||
#define EXIT_REASON_MCE 41
|
||||
#define EXIT_REASON_TPR 43
|
||||
#define EXIT_REASON_APIC_ACCESS 44
|
||||
#define EXIT_REASON_VIRTUALIZED_EOI 45
|
||||
#define EXIT_REASON_GDTR_IDTR 46
|
||||
#define EXIT_REASON_LDTR_TR 47
|
||||
#define EXIT_REASON_EPT_FAULT 48
|
||||
|
@ -1726,6 +1726,11 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
(qual & EXIT_QUAL_NMIUDTI) != 0)
|
||||
vmx_restore_nmi_blocking(vmx, vcpu);
|
||||
break;
|
||||
case EXIT_REASON_VIRTUALIZED_EOI:
|
||||
vmexit->exitcode = VM_EXITCODE_IOAPIC_EOI;
|
||||
vmexit->u.ioapic_eoi.vector = qual & 0xFF;
|
||||
vmexit->inst_length = 0; /* trap-like */
|
||||
break;
|
||||
case EXIT_REASON_APIC_ACCESS:
|
||||
handled = vmx_handle_apic_access(vmx, vcpu, vmexit);
|
||||
break;
|
||||
@ -2320,6 +2325,7 @@ vmx_setcap(void *arg, int vcpu, int type, int val)
|
||||
struct vlapic_vtx {
|
||||
struct vlapic vlapic;
|
||||
struct pir_desc *pir_desc;
|
||||
struct vmx *vmx;
|
||||
};
|
||||
|
||||
#define VMX_CTR_PIR(vm, vcpuid, pir_desc, notify, vector, level, msg) \
|
||||
@ -2345,9 +2351,6 @@ vmx_set_intr_ready(struct vlapic *vlapic, int vector, bool level)
|
||||
uint64_t mask;
|
||||
int idx, notify;
|
||||
|
||||
/*
|
||||
* XXX need to deal with level triggered interrupts
|
||||
*/
|
||||
vlapic_vtx = (struct vlapic_vtx *)vlapic;
|
||||
pir_desc = vlapic_vtx->pir_desc;
|
||||
|
||||
@ -2421,6 +2424,33 @@ vmx_intr_accepted(struct vlapic *vlapic, int vector)
|
||||
panic("vmx_intr_accepted: not expected to be called");
|
||||
}
|
||||
|
||||
static void
|
||||
vmx_set_tmr(struct vlapic *vlapic, int vector, bool level)
|
||||
{
|
||||
struct vlapic_vtx *vlapic_vtx;
|
||||
struct vmx *vmx;
|
||||
struct vmcs *vmcs;
|
||||
uint64_t mask, val;
|
||||
|
||||
KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector));
|
||||
KASSERT(!vcpu_is_running(vlapic->vm, vlapic->vcpuid, NULL),
|
||||
("vmx_set_tmr: vcpu cannot be running"));
|
||||
|
||||
vlapic_vtx = (struct vlapic_vtx *)vlapic;
|
||||
vmx = vlapic_vtx->vmx;
|
||||
vmcs = &vmx->vmcs[vlapic->vcpuid];
|
||||
mask = 1UL << (vector % 64);
|
||||
|
||||
VMPTRLD(vmcs);
|
||||
val = vmcs_read(VMCS_EOI_EXIT(vector));
|
||||
if (level)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
vmcs_write(VMCS_EOI_EXIT(vector), val);
|
||||
VMCLEAR(vmcs);
|
||||
}
|
||||
|
||||
static void
|
||||
vmx_post_intr(struct vlapic *vlapic, int hostcpu)
|
||||
{
|
||||
@ -2519,11 +2549,13 @@ vmx_vlapic_init(void *arg, int vcpuid)
|
||||
|
||||
vlapic_vtx = (struct vlapic_vtx *)vlapic;
|
||||
vlapic_vtx->pir_desc = &vmx->pir_desc[vcpuid];
|
||||
vlapic_vtx->vmx = vmx;
|
||||
|
||||
if (virtual_interrupt_delivery) {
|
||||
vlapic->ops.set_intr_ready = vmx_set_intr_ready;
|
||||
vlapic->ops.pending_intr = vmx_pending_intr;
|
||||
vlapic->ops.intr_accepted = vmx_intr_accepted;
|
||||
vlapic->ops.set_tmr = vmx_set_tmr;
|
||||
}
|
||||
|
||||
if (posted_interrupts)
|
||||
|
@ -1300,6 +1300,7 @@ vlapic_reset(struct vlapic *vlapic)
|
||||
lapic->dfr = 0xffffffff;
|
||||
lapic->svr = APIC_SVR_VECTOR;
|
||||
vlapic_mask_lvts(vlapic);
|
||||
vlapic_reset_tmr(vlapic);
|
||||
|
||||
lapic->dcr_timer = 0;
|
||||
vlapic_dcr_write_handler(vlapic);
|
||||
@ -1457,32 +1458,42 @@ vlapic_enabled(struct vlapic *vlapic)
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void
|
||||
vlapic_set_tmr(struct vlapic *vlapic, int vector, bool level)
|
||||
{
|
||||
struct LAPIC *lapic;
|
||||
uint32_t *tmrptr, mask;
|
||||
int idx;
|
||||
|
||||
lapic = vlapic->apic_page;
|
||||
tmrptr = &lapic->tmr0;
|
||||
idx = (vector / 32) * 4;
|
||||
mask = 1 << (vector % 32);
|
||||
if (level)
|
||||
tmrptr[idx] |= mask;
|
||||
else
|
||||
tmrptr[idx] &= ~mask;
|
||||
|
||||
if (vlapic->ops.set_tmr != NULL)
|
||||
(*vlapic->ops.set_tmr)(vlapic, vector, level);
|
||||
}
|
||||
|
||||
void
|
||||
vlapic_reset_tmr(struct vlapic *vlapic)
|
||||
{
|
||||
struct LAPIC *lapic;
|
||||
int vector;
|
||||
|
||||
VLAPIC_CTR0(vlapic, "vlapic resetting all vectors to edge-triggered");
|
||||
|
||||
lapic = vlapic->apic_page;
|
||||
lapic->tmr0 = 0;
|
||||
lapic->tmr1 = 0;
|
||||
lapic->tmr2 = 0;
|
||||
lapic->tmr3 = 0;
|
||||
lapic->tmr4 = 0;
|
||||
lapic->tmr5 = 0;
|
||||
lapic->tmr6 = 0;
|
||||
lapic->tmr7 = 0;
|
||||
for (vector = 0; vector <= 255; vector++)
|
||||
vlapic_set_tmr(vlapic, vector, false);
|
||||
}
|
||||
|
||||
void
|
||||
vlapic_set_tmr_level(struct vlapic *vlapic, uint32_t dest, bool phys,
|
||||
int delmode, int vector)
|
||||
{
|
||||
struct LAPIC *lapic;
|
||||
uint32_t *tmrptr, mask;
|
||||
cpuset_t dmask;
|
||||
int idx;
|
||||
bool lowprio;
|
||||
|
||||
KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector));
|
||||
@ -1502,11 +1513,6 @@ vlapic_set_tmr_level(struct vlapic *vlapic, uint32_t dest, bool phys,
|
||||
if (!CPU_ISSET(vlapic->vcpuid, &dmask))
|
||||
return;
|
||||
|
||||
lapic = vlapic->apic_page;
|
||||
tmrptr = &lapic->tmr0;
|
||||
idx = (vector / 32) * 4;
|
||||
mask = 1 << (vector % 32);
|
||||
tmrptr[idx] |= mask;
|
||||
|
||||
VLAPIC_CTR1(vlapic, "vector %d set to level-triggered", vector);
|
||||
vlapic_set_tmr(vlapic, vector, true);
|
||||
}
|
||||
|
@ -139,6 +139,7 @@ struct vlapic_ops {
|
||||
int (*pending_intr)(struct vlapic *vlapic, int *vecptr);
|
||||
void (*intr_accepted)(struct vlapic *vlapic, int vector);
|
||||
void (*post_intr)(struct vlapic *vlapic, int hostcpu);
|
||||
void (*set_tmr)(struct vlapic *vlapic, int vector, bool level);
|
||||
};
|
||||
|
||||
struct vlapic {
|
||||
|
@ -1150,6 +1150,10 @@ vm_run(struct vm *vm, struct vm_run *vmrun)
|
||||
if (error == 0) {
|
||||
retu = false;
|
||||
switch (vme->exitcode) {
|
||||
case VM_EXITCODE_IOAPIC_EOI:
|
||||
vioapic_process_eoi(vm, vcpuid,
|
||||
vme->u.ioapic_eoi.vector);
|
||||
break;
|
||||
case VM_EXITCODE_RENDEZVOUS:
|
||||
vm_handle_rendezvous(vm, vcpuid);
|
||||
error = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user