diff options
author | neel <neel@FreeBSD.org> | 2013-12-25 06:46:31 +0000 |
---|---|---|
committer | neel <neel@FreeBSD.org> | 2013-12-25 06:46:31 +0000 |
commit | 0c91ef8145cb2d4090accd731fafb4e98da2a46c (patch) | |
tree | 7c26297e63fee10a7fbabe8f0dba16c34086f60f /sys | |
parent | 258c0be375414a2ecddb9f8db57bb16625ef7ca0 (diff) | |
download | FreeBSD-src-0c91ef8145cb2d4090accd731fafb4e98da2a46c.zip FreeBSD-src-0c91ef8145cb2d4090accd731fafb4e98da2a46c.tar.gz |
vlapic code restructuring to make it easy to support hardware-assist for APIC
emulation.
The vlapic initialization and cleanup is done via processor specific vmm_ops.
This will allow the VT-x/SVM modules to layer any hardware-assist for APIC
emulation or virtual interrupt delivery on top of the vlapic device model.
Add a parameter to 'vcpu_notify_event()' to distinguish between vlapic
interrupts versus other events (e.g. NMI). This provides an opportunity to
use hardware-assists like Posted Interrupts (VT-x) or doorbell MSR (SVM)
to deliver an interrupt to a guest without causing a VM-exit.
Get rid of lapic_pending_intr() and lapic_intr_accepted() and use the
vlapic_xxx() counterparts directly.
Associate an 'Apic Page' with each vcpu and reference it from the 'vlapic'.
The 'Apic Page' is intended to be referenced from the Intel VMCS as the
'virtual APIC page' or from the AMD VMCB as the 'vAPIC backing page'.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/include/vmm.h | 6 | ||||
-rw-r--r-- | sys/amd64/vmm/amd/amdv.c | 16 | ||||
-rw-r--r-- | sys/amd64/vmm/intel/vmx.c | 44 | ||||
-rw-r--r-- | sys/amd64/vmm/intel/vmx.h | 6 | ||||
-rw-r--r-- | sys/amd64/vmm/io/vlapic.c | 147 | ||||
-rw-r--r-- | sys/amd64/vmm/io/vlapic.h | 79 | ||||
-rw-r--r-- | sys/amd64/vmm/io/vlapic_priv.h | 124 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm.c | 26 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_lapic.c | 24 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_lapic.h | 20 |
10 files changed, 293 insertions, 199 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h index d04da26..4f2bf89 100644 --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -69,6 +69,8 @@ typedef int (*vmi_get_cap_t)(void *vmi, int vcpu, int num, int *retval); typedef int (*vmi_set_cap_t)(void *vmi, int vcpu, int num, int val); typedef struct vmspace * (*vmi_vmspace_alloc)(vm_offset_t min, vm_offset_t max); typedef void (*vmi_vmspace_free)(struct vmspace *vmspace); +typedef struct vlapic * (*vmi_vlapic_init)(void *vmi, int vcpu); +typedef void (*vmi_vlapic_cleanup)(void *vmi, struct vlapic *vlapic); struct vmm_ops { vmm_init_func_t init; /* module wide initialization */ @@ -87,6 +89,8 @@ struct vmm_ops { vmi_set_cap_t vmsetcap; vmi_vmspace_alloc vmspace_alloc; vmi_vmspace_free vmspace_free; + vmi_vlapic_init vlapic_init; + vmi_vlapic_cleanup vlapic_cleanup; }; extern struct vmm_ops vmm_ops_intel; @@ -159,7 +163,7 @@ vcpu_is_running(struct vm *vm, int vcpu, int *hostcpu) } void *vcpu_stats(struct vm *vm, int vcpu); -void vcpu_notify_event(struct vm *vm, int vcpuid); +void vcpu_notify_event(struct vm *vm, int vcpuid, bool lapic_intr); struct vmspace *vm_get_vmspace(struct vm *vm); int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func); int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func); diff --git a/sys/amd64/vmm/amd/amdv.c b/sys/amd64/vmm/amd/amdv.c index 6c87901..4d7951a 100644 --- a/sys/amd64/vmm/amd/amdv.c +++ b/sys/amd64/vmm/amd/amdv.c @@ -155,6 +155,20 @@ amdv_vmspace_free(struct vmspace *vmspace) return; } +static struct vlapic * +amdv_vlapic_init(void *arg, int vcpuid) +{ + + panic("amdv_vlapic_init: not implmented"); +} + +static void +amdv_vlapic_cleanup(void *arg, struct vlapic *vlapic) +{ + + panic("amdv_vlapic_cleanup: not implemented"); +} + struct vmm_ops vmm_ops_amd = { amdv_init, amdv_cleanup, @@ -171,6 +185,8 @@ struct vmm_ops vmm_ops_amd = { amdv_setcap, amdv_vmspace_alloc, amdv_vmspace_free, + amdv_vlapic_init, + amdv_vlapic_cleanup, }; static int diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index 567167f..4482828 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -50,10 +50,11 @@ __FBSDID("$FreeBSD$"); #include <machine/vmm.h> #include "vmm_host.h" -#include "vmm_lapic.h" #include "vmm_msr.h" #include "vmm_ktr.h" #include "vmm_stat.h" +#include "vlapic.h" +#include "vlapic_priv.h" #include "vmx_msr.h" #include "ept.h" @@ -112,7 +113,8 @@ __FBSDID("$FreeBSD$"); #define HANDLED 1 #define UNHANDLED 0 -MALLOC_DEFINE(M_VMX, "vmx", "vmx"); +static MALLOC_DEFINE(M_VMX, "vmx", "vmx"); +static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic"); SYSCTL_DECL(_hw_vmm); SYSCTL_NODE(_hw_vmm, OID_AUTO, vmx, CTLFLAG_RW, NULL, NULL); @@ -1033,7 +1035,7 @@ nmiblocked: } static void -vmx_inject_interrupts(struct vmx *vmx, int vcpu) +vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic) { int vector; uint64_t info, rflags, interruptibility; @@ -1059,7 +1061,7 @@ vmx_inject_interrupts(struct vmx *vmx, int vcpu) return; /* Ask the local apic for a vector to inject */ - vector = lapic_pending_intr(vmx->vm, vcpu); + vector = vlapic_pending_intr(vlapic); if (vector < 0) return; @@ -1081,7 +1083,7 @@ vmx_inject_interrupts(struct vmx *vmx, int vcpu) vmcs_write(VMCS_ENTRY_INTR_INFO, info); /* Update the Local APIC ISR */ - lapic_intr_accepted(vmx->vm, vcpu, vector); + vlapic_intr_accepted(vlapic, vector); VCPU_CTR1(vmx->vm, vcpu, "Injecting hwintr at vector %d", vector); @@ -1459,11 +1461,13 @@ vmx_run(void *arg, int vcpu, register_t rip, pmap_t pmap) struct vmxctx *vmxctx; struct vmcs *vmcs; struct vm_exit *vmexit; + struct vlapic *vlapic; vmx = arg; vmcs = &vmx->vmcs[vcpu]; vmxctx = &vmx->ctx[vcpu]; vmxctx->launched = 0; + vlapic = vm_lapic(vmx->vm, vcpu); astpending = 0; vmexit = vm_exitinfo(vmx->vm, vcpu); @@ -1491,7 +1495,7 @@ vmx_run(void *arg, int vcpu, register_t rip, pmap_t pmap) vmx_set_pcpu_defaults(vmx, vcpu); do { - vmx_inject_interrupts(vmx, vcpu); + vmx_inject_interrupts(vmx, vcpu, vlapic); vmx_run_trace(vmx, vcpu); rc = vmx_setjmp(vmxctx); #ifdef SETJMP_TRACE @@ -1963,6 +1967,32 @@ vmx_setcap(void *arg, int vcpu, int type, int val) return (retval); } +static struct vlapic * +vmx_vlapic_init(void *arg, int vcpuid) +{ + struct vmx *vmx; + struct vlapic *vlapic; + + vmx = arg; + + vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO); + vlapic->vm = vmx->vm; + vlapic->vcpuid = vcpuid; + vlapic->apic_page = (struct LAPIC *)&vmx->apic_page[vcpuid]; + + vlapic_init(vlapic); + + return (vlapic); +} + +static void +vmx_vlapic_cleanup(void *arg, struct vlapic *vlapic) +{ + + vlapic_cleanup(vlapic); + free(vlapic, M_VLAPIC); +} + struct vmm_ops vmm_ops_intel = { vmx_init, vmx_cleanup, @@ -1979,4 +2009,6 @@ struct vmm_ops vmm_ops_intel = { vmx_setcap, ept_vmspace_alloc, ept_vmspace_free, + vmx_vlapic_init, + vmx_vlapic_cleanup, }; diff --git a/sys/amd64/vmm/intel/vmx.h b/sys/amd64/vmm/intel/vmx.h index 450bb66..227cc02 100644 --- a/sys/amd64/vmm/intel/vmx.h +++ b/sys/amd64/vmm/intel/vmx.h @@ -92,9 +92,15 @@ struct vmxstate { uint16_t vpid; }; +struct apic_page { + uint32_t reg[PAGE_SIZE / 4]; +}; +CTASSERT(sizeof(struct apic_page) == PAGE_SIZE); + /* virtual machine softc */ struct vmx { struct vmcs vmcs[VM_MAXCPU]; /* one vmcs per virtual cpu */ + struct apic_page apic_page[VM_MAXCPU]; /* one apic page per vcpu */ char msr_bitmap[PAGE_SIZE]; struct msr_entry guest_msrs[VM_MAXCPU][GUEST_MSR_MAX_ENTRIES]; struct vmxctx ctx[VM_MAXCPU]; diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c index 695040d..cfd4c54 100644 --- a/sys/amd64/vmm/io/vlapic.c +++ b/sys/amd64/vmm/io/vlapic.c @@ -37,16 +37,21 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/smp.h> -#include <machine/clock.h> #include <x86/specialreg.h> #include <x86/apicreg.h> +#include <machine/clock.h> +#include <machine/smp.h> + #include <machine/vmm.h> -#include "vmm_stat.h" +#include "vmm_ipi.h" #include "vmm_lapic.h" #include "vmm_ktr.h" +#include "vmm_stat.h" + #include "vlapic.h" +#include "vlapic_priv.h" #include "vioapic.h" #define VLAPIC_CTR0(vlapic, format) \ @@ -60,7 +65,7 @@ __FBSDID("$FreeBSD$"); #define VLAPIC_CTR_IRR(vlapic, msg) \ do { \ - uint32_t *irrptr = &(vlapic)->apic.irr0; \ + uint32_t *irrptr = &(vlapic)->apic_page->irr0; \ irrptr[0] = irrptr[0]; /* silence compiler */ \ VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]); \ VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]); \ @@ -74,7 +79,7 @@ do { \ #define VLAPIC_CTR_ISR(vlapic, msg) \ do { \ - uint32_t *isrptr = &(vlapic)->apic.isr0; \ + uint32_t *isrptr = &(vlapic)->apic_page->isr0; \ isrptr[0] = isrptr[0]; /* silence compiler */ \ VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]); \ VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]); \ @@ -86,8 +91,6 @@ do { \ VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]); \ } while (0) -static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic"); - #define PRIO(x) ((x) >> 4) #define VLAPIC_VERSION (16) @@ -95,40 +98,6 @@ static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic"); #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) -enum boot_state { - BS_INIT, - BS_SIPI, - BS_RUNNING -}; - -struct vlapic { - struct vm *vm; - int vcpuid; - - struct LAPIC apic; - - uint32_t esr_pending; - int esr_firing; - - struct callout callout; /* vlapic timer */ - struct bintime timer_fire_bt; /* callout expiry time */ - struct bintime timer_freq_bt; /* timer frequency */ - struct bintime timer_period_bt; /* timer period */ - struct mtx timer_mtx; - - /* - * The 'isrvec_stk' is a stack of vectors injected by the local apic. - * A vector is popped from the stack when the processor does an EOI. - * The vector on the top of the stack is used to compute the - * Processor Priority in conjunction with the TPR. - */ - uint8_t isrvec_stk[ISRVEC_STK_SIZE]; - int isrvec_stk_top; - - uint64_t msr_apicbase; - enum boot_state boot_state; -}; - /* * The 'vlapic->timer_mtx' is used to provide mutual exclusion between the * vlapic_callout_handler() and vcpu accesses to the following registers: @@ -163,7 +132,7 @@ vlapic_get_ldr(struct vlapic *vlapic) int apicid; uint32_t ldr; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; if (x2apic(vlapic)) { apicid = vlapic_get_id(vlapic); ldr = 1 << (apicid & 0xf); @@ -178,7 +147,7 @@ vlapic_get_dfr(struct vlapic *vlapic) { struct LAPIC *lapic; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; if (x2apic(vlapic)) return (0); else @@ -196,7 +165,7 @@ vlapic_set_dfr(struct vlapic *vlapic, uint32_t data) return; } - lapic = &vlapic->apic; + lapic = vlapic->apic_page; dfr = (lapic->dfr & APIC_DFR_RESERVED) | (data & APIC_DFR_MODEL_MASK); if ((dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT) VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model"); @@ -219,7 +188,7 @@ vlapic_set_ldr(struct vlapic *vlapic, uint32_t data) return; } - lapic = &vlapic->apic; + lapic = vlapic->apic_page; lapic->ldr = data & ~APIC_LDR_RESERVED; VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr); } @@ -277,7 +246,7 @@ vlapic_get_ccr(struct vlapic *vlapic) uint32_t ccr; ccr = 0; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; VLAPIC_TIMER_LOCK(vlapic); if (callout_active(&vlapic->callout)) { @@ -307,7 +276,7 @@ vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr) struct LAPIC *lapic; int divisor; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; VLAPIC_TIMER_LOCK(vlapic); lapic->dcr_timer = dcr; @@ -330,7 +299,9 @@ vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr) static void vlapic_update_errors(struct vlapic *vlapic) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic; + + lapic = vlapic->apic_page; lapic->esr = vlapic->esr_pending; vlapic->esr_pending = 0; } @@ -340,7 +311,7 @@ vlapic_reset(struct vlapic *vlapic) { struct LAPIC *lapic; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; bzero(lapic, sizeof(struct LAPIC)); lapic->version = VLAPIC_VERSION; @@ -360,7 +331,7 @@ vlapic_reset(struct vlapic *vlapic) void vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; uint32_t *irrptr, *tmrptr, mask; int idx; @@ -401,7 +372,7 @@ vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) static __inline uint32_t * vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; int i; switch (offset) { @@ -428,7 +399,7 @@ vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val) uint32_t *lvtptr, mask; struct LAPIC *lapic; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; lvtptr = vlapic_get_lvtptr(vlapic, offset); if (offset == APIC_OFFSET_TIMER_LVT) @@ -475,7 +446,7 @@ vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt) return (0); } vlapic_set_intr_ready(vlapic, vec, false); - vcpu_notify_event(vlapic->vm, vlapic->vcpuid); + vcpu_notify_event(vlapic->vm, vlapic->vcpuid, true); break; case APIC_LVT_DM_NMI: vm_inject_nmi(vlapic->vm, vlapic->vcpuid); @@ -494,7 +465,7 @@ dump_isrvec_stk(struct vlapic *vlapic) int i; uint32_t *isrptr; - isrptr = &vlapic->apic.isr0; + isrptr = &vlapic->apic_page->isr0; for (i = 0; i < 8; i++) printf("ISR%d 0x%08x\n", i, isrptr[i * 4]); @@ -519,7 +490,7 @@ vlapic_update_ppr(struct vlapic *vlapic) * bits is set in the ISRx registers. */ isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top]; - tpr = vlapic->apic.tpr; + tpr = vlapic->apic_page->tpr; #if 1 { @@ -548,7 +519,7 @@ vlapic_update_ppr(struct vlapic *vlapic) * corresponding entry on the isrvec stack. */ i = 1; - isrptr = &vlapic->apic.isr0; + isrptr = &vlapic->apic_page->isr0; for (vector = 0; vector < 256; vector++) { idx = (vector / 32) * 4; if (isrptr[idx] & (1 << (vector % 32))) { @@ -568,14 +539,14 @@ vlapic_update_ppr(struct vlapic *vlapic) else ppr = isrvec & 0xf0; - vlapic->apic.ppr = ppr; + vlapic->apic_page->ppr = ppr; VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr); } static void vlapic_process_eoi(struct vlapic *vlapic) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; uint32_t *isrptr, *tmrptr; int i, idx, bitpos, vector; @@ -735,7 +706,7 @@ vlapic_callout_handler(void *arg) callout_deactivate(&vlapic->callout); - KASSERT(vlapic->apic.icr_timer != 0, ("vlapic timer is disabled")); + KASSERT(vlapic->apic_page->icr_timer != 0, ("timer is disabled")); vlapic_fire_timer(vlapic); @@ -789,7 +760,7 @@ vlapic_set_icr_timer(struct vlapic *vlapic, uint32_t icr_timer) VLAPIC_TIMER_LOCK(vlapic); - lapic = &vlapic->apic; + lapic = vlapic->apic_page; lapic->icr_timer = icr_timer; vlapic->timer_period_bt = vlapic->timer_freq_bt; @@ -1021,7 +992,7 @@ lapic_process_icr(struct vlapic *vlapic, uint64_t icrval, bool *retu) int vlapic_pending_intr(struct vlapic *vlapic) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; int idx, i, bitpos, vector; uint32_t *irrptr, val; @@ -1050,7 +1021,7 @@ vlapic_pending_intr(struct vlapic *vlapic) void vlapic_intr_accepted(struct vlapic *vlapic, int vector) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; uint32_t *irrptr, *isrptr; int idx, stk_top; @@ -1087,7 +1058,7 @@ lapic_set_svr(struct vlapic *vlapic, uint32_t new) struct LAPIC *lapic; uint32_t old, changed; - lapic = &vlapic->apic; + lapic = vlapic->apic_page; old = lapic->svr; changed = old ^ new; if ((changed & APIC_SVR_ENABLE) != 0) { @@ -1115,7 +1086,7 @@ lapic_set_svr(struct vlapic *vlapic, uint32_t new) int vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; uint32_t *reg; int i; @@ -1182,13 +1153,13 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu) case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: *data = vlapic_get_lvt(vlapic, offset); break; - case APIC_OFFSET_ICR: + case APIC_OFFSET_TIMER_ICR: *data = lapic->icr_timer; break; - case APIC_OFFSET_CCR: + case APIC_OFFSET_TIMER_CCR: *data = vlapic_get_ccr(vlapic); break; - case APIC_OFFSET_DCR: + case APIC_OFFSET_TIMER_DCR: *data = lapic->dcr_timer; break; case APIC_OFFSET_RRR: @@ -1204,7 +1175,7 @@ done: int vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; int retval; VLAPIC_CTR2(vlapic, "vlapic write offset %#x, data %#lx", offset, data); @@ -1252,11 +1223,11 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: vlapic_set_lvt(vlapic, offset, data); break; - case APIC_OFFSET_ICR: + case APIC_OFFSET_TIMER_ICR: vlapic_set_icr_timer(vlapic, data); break; - case APIC_OFFSET_DCR: + case APIC_OFFSET_TIMER_DCR: vlapic_set_dcr(vlapic, data); break; @@ -1270,7 +1241,7 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: - case APIC_OFFSET_CCR: + case APIC_OFFSET_TIMER_CCR: default: // Read only. break; @@ -1279,14 +1250,14 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) return (retval); } -struct vlapic * -vlapic_init(struct vm *vm, int vcpuid) +void +vlapic_init(struct vlapic *vlapic) { - struct vlapic *vlapic; - - vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO); - vlapic->vm = vm; - vlapic->vcpuid = vcpuid; + KASSERT(vlapic->vm != NULL, ("vlapic_init: vm is not initialized")); + KASSERT(vlapic->vcpuid >= 0 && vlapic->vcpuid < VM_MAXCPU, + ("vlapic_init: vcpuid is not initialized")); + KASSERT(vlapic->apic_page != NULL, ("vlapic_init: apic_page is not " + "initialized")); /* * If the vlapic is configured in x2apic mode then it will be @@ -1300,12 +1271,10 @@ vlapic_init(struct vm *vm, int vcpuid) vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; - if (vcpuid == 0) + if (vlapic->vcpuid == 0) vlapic->msr_apicbase |= APICBASE_BSP; vlapic_reset(vlapic); - - return (vlapic); } void @@ -1313,7 +1282,6 @@ vlapic_cleanup(struct vlapic *vlapic) { callout_drain(&vlapic->callout); - free(vlapic, M_VLAPIC); } uint64_t @@ -1378,10 +1346,25 @@ vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, } } +void +vlapic_post_intr(struct vlapic *vlapic, int hostcpu) +{ + /* + * Post an interrupt to the vcpu currently running on 'hostcpu'. + * + * This is done by leveraging features like Posted Interrupts (Intel) + * Doorbell MSR (AMD AVIC) that avoid a VM exit. + * + * If neither of these features are available then fallback to + * sending an IPI to 'hostcpu'. + */ + ipi_cpu(hostcpu, vmm_ipinum); +} + bool vlapic_enabled(struct vlapic *vlapic) { - struct LAPIC *lapic = &vlapic->apic; + struct LAPIC *lapic = vlapic->apic_page; if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && (lapic->svr & APIC_SVR_ENABLE) != 0) diff --git a/sys/amd64/vmm/io/vlapic.h b/sys/amd64/vmm/io/vlapic.h index 98f377e..8f34070 100644 --- a/sys/amd64/vmm/io/vlapic.h +++ b/sys/amd64/vmm/io/vlapic.h @@ -30,73 +30,33 @@ #define _VLAPIC_H_ struct vm; - -/* - * Map of APIC Registers: Offset Description Access - */ -#define APIC_OFFSET_ID 0x20 // Local APIC ID R/W -#define APIC_OFFSET_VER 0x30 // Local APIC Version R -#define APIC_OFFSET_TPR 0x80 // Task Priority Register R/W -#define APIC_OFFSET_APR 0x90 // Arbitration Priority Register R -#define APIC_OFFSET_PPR 0xA0 // Processor Priority Register R -#define APIC_OFFSET_EOI 0xB0 // EOI Register W -#define APIC_OFFSET_RRR 0xC0 // Remote read R -#define APIC_OFFSET_LDR 0xD0 // Logical Destination R/W -#define APIC_OFFSET_DFR 0xE0 // Destination Format Register 0..27 R; 28..31 R/W -#define APIC_OFFSET_SVR 0xF0 // Spurious Interrupt Vector Reg. 0..3 R; 4..9 R/W -#define APIC_OFFSET_ISR0 0x100 // ISR 000-031 R -#define APIC_OFFSET_ISR1 0x110 // ISR 032-063 R -#define APIC_OFFSET_ISR2 0x120 // ISR 064-095 R -#define APIC_OFFSET_ISR3 0x130 // ISR 095-128 R -#define APIC_OFFSET_ISR4 0x140 // ISR 128-159 R -#define APIC_OFFSET_ISR5 0x150 // ISR 160-191 R -#define APIC_OFFSET_ISR6 0x160 // ISR 192-223 R -#define APIC_OFFSET_ISR7 0x170 // ISR 224-255 R -#define APIC_OFFSET_TMR0 0x180 // TMR 000-031 R -#define APIC_OFFSET_TMR1 0x190 // TMR 032-063 R -#define APIC_OFFSET_TMR2 0x1A0 // TMR 064-095 R -#define APIC_OFFSET_TMR3 0x1B0 // TMR 095-128 R -#define APIC_OFFSET_TMR4 0x1C0 // TMR 128-159 R -#define APIC_OFFSET_TMR5 0x1D0 // TMR 160-191 R -#define APIC_OFFSET_TMR6 0x1E0 // TMR 192-223 R -#define APIC_OFFSET_TMR7 0x1F0 // TMR 224-255 R -#define APIC_OFFSET_IRR0 0x200 // IRR 000-031 R -#define APIC_OFFSET_IRR1 0x210 // IRR 032-063 R -#define APIC_OFFSET_IRR2 0x220 // IRR 064-095 R -#define APIC_OFFSET_IRR3 0x230 // IRR 095-128 R -#define APIC_OFFSET_IRR4 0x240 // IRR 128-159 R -#define APIC_OFFSET_IRR5 0x250 // IRR 160-191 R -#define APIC_OFFSET_IRR6 0x260 // IRR 192-223 R -#define APIC_OFFSET_IRR7 0x270 // IRR 224-255 R -#define APIC_OFFSET_ESR 0x280 // Error Status Register R -#define APIC_OFFSET_CMCI_LVT 0x2F0 // Local Vector Table (CMCI) R/W -#define APIC_OFFSET_ICR_LOW 0x300 // Interrupt Command Reg. (0-31) R/W -#define APIC_OFFSET_ICR_HI 0x310 // Interrupt Command Reg. (32-63) R/W -#define APIC_OFFSET_TIMER_LVT 0x320 // Local Vector Table (Timer) R/W -#define APIC_OFFSET_THERM_LVT 0x330 // Local Vector Table (Thermal) R/W (PIV+) -#define APIC_OFFSET_PERF_LVT 0x340 // Local Vector Table (Performance) R/W (P6+) -#define APIC_OFFSET_LINT0_LVT 0x350 // Local Vector Table (LINT0) R/W -#define APIC_OFFSET_LINT1_LVT 0x360 // Local Vector Table (LINT1) R/W -#define APIC_OFFSET_ERROR_LVT 0x370 // Local Vector Table (ERROR) R/W -#define APIC_OFFSET_ICR 0x380 // Initial Count Reg. for Timer R/W -#define APIC_OFFSET_CCR 0x390 // Current Count of Timer R -#define APIC_OFFSET_DCR 0x3E0 // Timer Divide Configuration Reg. R/W - -/* - * 16 priority levels with at most one vector injected per level. - */ -#define ISRVEC_STK_SIZE (16 + 1) - enum x2apic_state; -struct vlapic *vlapic_init(struct vm *vm, int vcpuid); -void vlapic_cleanup(struct vlapic *vlapic); int vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu); int vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu); + +/* + * Returns a vector between 32 and 255 if an interrupt is pending in the + * IRR that can be delivered based on the current state of ISR and TPR. + * + * Note that the vector does not automatically transition to the ISR as a + * result of calling this function. + * + * Returns -1 if there is no eligible vector that can be delivered to the + * guest at this time. + */ int vlapic_pending_intr(struct vlapic *vlapic); + +/* + * Transition 'vector' from IRR to ISR. This function is called with the + * vector returned by 'vlapic_pending_intr()' when the guest is able to + * accept this interrupt (i.e. RFLAGS.IF = 1 and no conditions exist that + * block interrupt delivery). + */ void vlapic_intr_accepted(struct vlapic *vlapic, int vector); + void vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level); void vlapic_set_error(struct vlapic *vlapic, uint32_t mask); void vlapic_fire_cmci(struct vlapic *vlapic); @@ -109,4 +69,5 @@ bool vlapic_enabled(struct vlapic *vlapic); void vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, int delmode, int vec); +void vlapic_post_intr(struct vlapic *vlapic, int hostcpu); #endif /* _VLAPIC_H_ */ diff --git a/sys/amd64/vmm/io/vlapic_priv.h b/sys/amd64/vmm/io/vlapic_priv.h new file mode 100644 index 0000000..569b460 --- /dev/null +++ b/sys/amd64/vmm/io/vlapic_priv.h @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VLAPIC_PRIV_H_ +#define _VLAPIC_PRIV_H_ + +/* + * APIC Register: Offset Description + */ +#define APIC_OFFSET_ID 0x20 /* Local APIC ID */ +#define APIC_OFFSET_VER 0x30 /* Local APIC Version */ +#define APIC_OFFSET_TPR 0x80 /* Task Priority Register */ +#define APIC_OFFSET_APR 0x90 /* Arbitration Priority */ +#define APIC_OFFSET_PPR 0xA0 /* Processor Priority Register */ +#define APIC_OFFSET_EOI 0xB0 /* EOI Register */ +#define APIC_OFFSET_RRR 0xC0 /* Remote read */ +#define APIC_OFFSET_LDR 0xD0 /* Logical Destination */ +#define APIC_OFFSET_DFR 0xE0 /* Destination Format Register */ +#define APIC_OFFSET_SVR 0xF0 /* Spurious Vector Register */ +#define APIC_OFFSET_ISR0 0x100 /* In Service Register */ +#define APIC_OFFSET_ISR1 0x110 +#define APIC_OFFSET_ISR2 0x120 +#define APIC_OFFSET_ISR3 0x130 +#define APIC_OFFSET_ISR4 0x140 +#define APIC_OFFSET_ISR5 0x150 +#define APIC_OFFSET_ISR6 0x160 +#define APIC_OFFSET_ISR7 0x170 +#define APIC_OFFSET_TMR0 0x180 /* Trigger Mode Register */ +#define APIC_OFFSET_TMR1 0x190 +#define APIC_OFFSET_TMR2 0x1A0 +#define APIC_OFFSET_TMR3 0x1B0 +#define APIC_OFFSET_TMR4 0x1C0 +#define APIC_OFFSET_TMR5 0x1D0 +#define APIC_OFFSET_TMR6 0x1E0 +#define APIC_OFFSET_TMR7 0x1F0 +#define APIC_OFFSET_IRR0 0x200 /* Interrupt Request Register */ +#define APIC_OFFSET_IRR1 0x210 +#define APIC_OFFSET_IRR2 0x220 +#define APIC_OFFSET_IRR3 0x230 +#define APIC_OFFSET_IRR4 0x240 +#define APIC_OFFSET_IRR5 0x250 +#define APIC_OFFSET_IRR6 0x260 +#define APIC_OFFSET_IRR7 0x270 +#define APIC_OFFSET_ESR 0x280 /* Error Status Register */ +#define APIC_OFFSET_CMCI_LVT 0x2F0 /* Local Vector Table (CMCI) */ +#define APIC_OFFSET_ICR_LOW 0x300 /* Interrupt Command Register */ +#define APIC_OFFSET_ICR_HI 0x310 +#define APIC_OFFSET_TIMER_LVT 0x320 /* Local Vector Table (Timer) */ +#define APIC_OFFSET_THERM_LVT 0x330 /* Local Vector Table (Thermal) */ +#define APIC_OFFSET_PERF_LVT 0x340 /* Local Vector Table (PMC) */ +#define APIC_OFFSET_LINT0_LVT 0x350 /* Local Vector Table (LINT0) */ +#define APIC_OFFSET_LINT1_LVT 0x360 /* Local Vector Table (LINT1) */ +#define APIC_OFFSET_ERROR_LVT 0x370 /* Local Vector Table (ERROR) */ +#define APIC_OFFSET_TIMER_ICR 0x380 /* Timer's Initial Count */ +#define APIC_OFFSET_TIMER_CCR 0x390 /* Timer's Current Count */ +#define APIC_OFFSET_TIMER_DCR 0x3E0 /* Timer's Divide Configuration */ + +enum boot_state { + BS_INIT, + BS_SIPI, + BS_RUNNING +}; + +/* + * 16 priority levels with at most one vector injected per level. + */ +#define ISRVEC_STK_SIZE (16 + 1) + +struct vlapic { + struct vm *vm; + int vcpuid; + struct LAPIC *apic_page; + + uint32_t esr_pending; + int esr_firing; + + struct callout callout; /* vlapic timer */ + struct bintime timer_fire_bt; /* callout expiry time */ + struct bintime timer_freq_bt; /* timer frequency */ + struct bintime timer_period_bt; /* timer period */ + struct mtx timer_mtx; + + /* + * The 'isrvec_stk' is a stack of vectors injected by the local apic. + * A vector is popped from the stack when the processor does an EOI. + * The vector on the top of the stack is used to compute the + * Processor Priority in conjunction with the TPR. + */ + uint8_t isrvec_stk[ISRVEC_STK_SIZE]; + int isrvec_stk_top; + + uint64_t msr_apicbase; + enum boot_state boot_state; +}; + +void vlapic_init(struct vlapic *vlapic); +void vlapic_cleanup(struct vlapic *vlapic); + +#endif /* _VLAPIC_PRIV_H_ */ diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c index ab0f9be..232d48b 100644 --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -156,6 +156,10 @@ static struct vmm_ops *ops; (ops != NULL ? (*ops->vmgetcap)(vmi, vcpu, num, retval) : ENXIO) #define VMSETCAP(vmi, vcpu, num, val) \ (ops != NULL ? (*ops->vmsetcap)(vmi, vcpu, num, val) : ENXIO) +#define VLAPIC_INIT(vmi, vcpu) \ + (ops != NULL ? (*ops->vlapic_init)(vmi, vcpu) : NULL) +#define VLAPIC_CLEANUP(vmi, vlapic) \ + (ops != NULL ? (*ops->vlapic_cleanup)(vmi, vlapic) : NULL) #define fpu_start_emulating() load_cr0(rcr0() | CR0_TS) #define fpu_stop_emulating() clts() @@ -167,9 +171,11 @@ CTASSERT(VMM_MSR_NUM <= 64); /* msr_mask can keep track of up to 64 msrs */ static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime"); static void -vcpu_cleanup(struct vcpu *vcpu) +vcpu_cleanup(struct vm *vm, int i) { - vlapic_cleanup(vcpu->vlapic); + struct vcpu *vcpu = &vm->vcpu[i]; + + VLAPIC_CLEANUP(vm->cookie, vcpu->vlapic); vmm_stat_free(vcpu->stats); fpu_save_area_free(vcpu->guestfpu); } @@ -184,7 +190,7 @@ vcpu_init(struct vm *vm, uint32_t vcpu_id) vcpu_lock_init(vcpu); vcpu->hostcpu = NOCPU; vcpu->vcpuid = vcpu_id; - vcpu->vlapic = vlapic_init(vm, vcpu_id); + vcpu->vlapic = VLAPIC_INIT(vm->cookie, vcpu_id); vm_set_x2apic_state(vm, vcpu_id, X2APIC_ENABLED); vcpu->guestfpu = fpu_save_area_alloc(); fpu_save_area_reset(vcpu->guestfpu); @@ -360,7 +366,7 @@ vm_destroy(struct vm *vm) vm->num_mem_segs = 0; for (i = 0; i < VM_MAXCPU; i++) - vcpu_cleanup(&vm->vcpu[i]); + vcpu_cleanup(vm, i); VMSPACE_FREE(vm->vmspace); @@ -1127,7 +1133,7 @@ vm_inject_nmi(struct vm *vm, int vcpuid) vcpu = &vm->vcpu[vcpuid]; vcpu->nmi_pending = 1; - vcpu_notify_event(vm, vcpuid); + vcpu_notify_event(vm, vcpuid, false); return (0); } @@ -1356,7 +1362,7 @@ vm_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) * to the host_cpu to cause the vcpu to trap into the hypervisor. */ void -vcpu_notify_event(struct vm *vm, int vcpuid) +vcpu_notify_event(struct vm *vm, int vcpuid, bool lapic_intr) { int hostcpu; struct vcpu *vcpu; @@ -1371,8 +1377,12 @@ vcpu_notify_event(struct vm *vm, int vcpuid) } else { if (vcpu->state != VCPU_RUNNING) panic("invalid vcpu state %d", vcpu->state); - if (hostcpu != curcpu) - ipi_cpu(hostcpu, vmm_ipinum); + if (hostcpu != curcpu) { + if (lapic_intr) + vlapic_post_intr(vcpu->vlapic, hostcpu); + else + ipi_cpu(hostcpu, vmm_ipinum); + } } vcpu_unlock(vcpu); } diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c index 8d915cd..cb2c806 100644 --- a/sys/amd64/vmm/vmm_lapic.c +++ b/sys/amd64/vmm/vmm_lapic.c @@ -51,26 +51,6 @@ __FBSDID("$FreeBSD$"); #define MSI_X86_ADDR_LOG 0x00000004 /* Destination Mode */ int -lapic_pending_intr(struct vm *vm, int cpu) -{ - struct vlapic *vlapic; - - vlapic = vm_lapic(vm, cpu); - - return (vlapic_pending_intr(vlapic)); -} - -void -lapic_intr_accepted(struct vm *vm, int cpu, int vector) -{ - struct vlapic *vlapic; - - vlapic = vm_lapic(vm, cpu); - - vlapic_intr_accepted(vlapic, vector); -} - -int lapic_set_intr(struct vm *vm, int cpu, int vector, bool level) { struct vlapic *vlapic; @@ -83,9 +63,7 @@ lapic_set_intr(struct vm *vm, int cpu, int vector, bool level) vlapic = vm_lapic(vm, cpu); vlapic_set_intr_ready(vlapic, vector, level); - - vcpu_notify_event(vm, cpu); - + vcpu_notify_event(vm, cpu, true); return (0); } diff --git a/sys/amd64/vmm/vmm_lapic.h b/sys/amd64/vmm/vmm_lapic.h index c5c95aa..88fa948 100644 --- a/sys/amd64/vmm/vmm_lapic.h +++ b/sys/amd64/vmm/vmm_lapic.h @@ -43,26 +43,6 @@ int lapic_mmio_write(void *vm, int cpu, uint64_t gpa, uint64_t wval, int size, void *arg); /* - * Returns a vector between 32 and 255 if an interrupt is pending in the - * IRR that can be delivered based on the current state of ISR and TPR. - * - * Note that the vector does not automatically transition to the ISR as a - * result of calling this function. - * - * Returns -1 if there is no eligible vector that can be delivered to the - * guest at this time. - */ -int lapic_pending_intr(struct vm *vm, int cpu); - -/* - * Transition 'vector' from IRR to ISR. This function is called with the - * vector returned by 'lapic_pending_intr()' when the guest is able to - * accept this interrupt (i.e. RFLAGS.IF = 1 and no conditions exist that - * block interrupt delivery). - */ -void lapic_intr_accepted(struct vm *vm, int cpu, int vector); - -/* * Signals to the LAPIC that an interrupt at 'vector' needs to be generated * to the 'cpu', the state is recorded in IRR. */ |