diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c index b07d6a3b7031..626d53092635 100644 --- a/sys/amd64/vmm/io/vlapic.c +++ b/sys/amd64/vmm/io/vlapic.c @@ -94,7 +94,6 @@ do { \ #define PRIO(x) ((x) >> 4) #define VLAPIC_VERSION (16) -#define VLAPIC_MAXLVT_ENTRIES (APIC_LVT_CMCI) #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) @@ -212,20 +211,6 @@ vlapic_timer_divisor(uint32_t dcr) } } -static void -vlapic_mask_lvts(struct vlapic *vlapic) -{ - struct LAPIC *lapic = vlapic->apic_page; - - lapic->lvt_cmci |= APIC_LVT_M; - lapic->lvt_timer |= APIC_LVT_M; - lapic->lvt_thermal |= APIC_LVT_M; - lapic->lvt_pcint |= APIC_LVT_M; - lapic->lvt_lint0 |= APIC_LVT_M; - lapic->lvt_lint1 |= APIC_LVT_M; - lapic->lvt_error |= APIC_LVT_M; -} - #if 0 static inline void vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) @@ -304,32 +289,6 @@ vlapic_esr_write_handler(struct vlapic *vlapic) vlapic->esr_pending = 0; } -static void -vlapic_reset(struct vlapic *vlapic) -{ - struct LAPIC *lapic; - - lapic = vlapic->apic_page; - bzero(lapic, sizeof(struct LAPIC)); - - lapic->id = vlapic_get_id(vlapic); - lapic->version = VLAPIC_VERSION; - lapic->version |= (VLAPIC_MAXLVT_ENTRIES << MAXLVTSHIFT); - lapic->dfr = 0xffffffff; - lapic->svr = APIC_SVR_VECTOR; - vlapic_mask_lvts(vlapic); - - lapic->dcr_timer = 0; - vlapic_dcr_write_handler(vlapic); - - if (vlapic->vcpuid == 0) - vlapic->boot_state = BS_RUNNING; /* BSP */ - else - vlapic->boot_state = BS_INIT; /* AP */ - - vlapic->svr_last = lapic->svr; -} - void vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) { @@ -388,24 +347,65 @@ vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) } } +static __inline int +lvt_off_to_idx(uint32_t offset) +{ + int index; + + switch (offset) { + case APIC_OFFSET_CMCI_LVT: + index = APIC_LVT_CMCI; + break; + case APIC_OFFSET_TIMER_LVT: + index = APIC_LVT_TIMER; + break; + case APIC_OFFSET_THERM_LVT: + index = APIC_LVT_THERMAL; + break; + case APIC_OFFSET_PERF_LVT: + index = APIC_LVT_PMC; + break; + case APIC_OFFSET_LINT0_LVT: + index = APIC_LVT_LINT0; + break; + case APIC_OFFSET_LINT1_LVT: + index = APIC_LVT_LINT1; + break; + case APIC_OFFSET_ERROR_LVT: + index = APIC_LVT_ERROR; + break; + default: + index = -1; + break; + } + KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: " + "invalid lvt index %d for offset %#x", index, offset)); + + return (index); +} + static __inline uint32_t vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) { + int idx; + uint32_t val; - return (*vlapic_get_lvtptr(vlapic, offset)); + idx = lvt_off_to_idx(offset); + val = atomic_load_acq_32(&vlapic->lvt_last[idx]); + return (val); } -static void -vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val) +void +vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset) { - uint32_t *lvtptr, mask; + uint32_t *lvtptr, mask, val; struct LAPIC *lapic; + int idx; lapic = vlapic->apic_page; lvtptr = vlapic_get_lvtptr(vlapic, offset); - - if (offset == APIC_OFFSET_TIMER_LVT) - VLAPIC_TIMER_LOCK(vlapic); + val = *lvtptr; + idx = lvt_off_to_idx(offset); if (!(lapic->svr & APIC_SVR_ENABLE)) val |= APIC_LVT_M; @@ -424,10 +424,36 @@ vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val) mask |= APIC_LVT_DM; break; } - *lvtptr = val & mask; + val &= mask; + *lvtptr = val; + atomic_store_rel_32(&vlapic->lvt_last[idx], val); +} - if (offset == APIC_OFFSET_TIMER_LVT) - VLAPIC_TIMER_UNLOCK(vlapic); +static void +vlapic_mask_lvts(struct vlapic *vlapic) +{ + struct LAPIC *lapic = vlapic->apic_page; + + lapic->lvt_cmci |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT); + + lapic->lvt_timer |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT); + + lapic->lvt_thermal |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT); + + lapic->lvt_pcint |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT); + + lapic->lvt_lint0 |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT); + + lapic->lvt_lint1 |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT); + + lapic->lvt_error |= APIC_LVT_M; + vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT); } static int @@ -648,7 +674,7 @@ vlapic_fire_cmci(struct vlapic *vlapic) } } -static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_ENTRIES, +static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1, "lvts triggered"); int @@ -1166,6 +1192,11 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu) case APIC_OFFSET_CMCI_LVT: case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: *data = vlapic_get_lvt(vlapic, offset); +#ifdef INVARIANTS + reg = vlapic_get_lvtptr(vlapic, offset); + KASSERT(*data == *reg, ("inconsistent lvt value at " + "offset %#lx: %#lx/%#x", offset, *data, *reg)); +#endif break; case APIC_OFFSET_TIMER_ICR: *data = lapic->icr_timer; @@ -1190,6 +1221,7 @@ int vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) { struct LAPIC *lapic = vlapic->apic_page; + uint32_t *regptr; int retval; KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE, @@ -1238,7 +1270,9 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) break; case APIC_OFFSET_CMCI_LVT: case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: - vlapic_set_lvt(vlapic, offset, data); + regptr = vlapic_get_lvtptr(vlapic, offset); + *regptr = data; + vlapic_lvt_write_handler(vlapic, offset); break; case APIC_OFFSET_TIMER_ICR: lapic->icr_timer = data; @@ -1269,6 +1303,32 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) return (retval); } +static void +vlapic_reset(struct vlapic *vlapic) +{ + struct LAPIC *lapic; + + lapic = vlapic->apic_page; + bzero(lapic, sizeof(struct LAPIC)); + + lapic->id = vlapic_get_id(vlapic); + lapic->version = VLAPIC_VERSION; + lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT); + lapic->dfr = 0xffffffff; + lapic->svr = APIC_SVR_VECTOR; + vlapic_mask_lvts(vlapic); + + lapic->dcr_timer = 0; + vlapic_dcr_write_handler(vlapic); + + if (vlapic->vcpuid == 0) + vlapic->boot_state = BS_RUNNING; /* BSP */ + else + vlapic->boot_state = BS_INIT; /* AP */ + + vlapic->svr_last = lapic->svr; +} + void vlapic_init(struct vlapic *vlapic) { diff --git a/sys/amd64/vmm/io/vlapic.h b/sys/amd64/vmm/io/vlapic.h index 1daf36d4025b..bcf45597a2c7 100644 --- a/sys/amd64/vmm/io/vlapic.h +++ b/sys/amd64/vmm/io/vlapic.h @@ -80,4 +80,5 @@ void vlapic_esr_write_handler(struct vlapic *vlapic); int vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu); void vlapic_icrtmr_write_handler(struct vlapic *vlapic); void vlapic_dcr_write_handler(struct vlapic *vlapic); +void vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset); #endif /* _VLAPIC_H_ */ diff --git a/sys/amd64/vmm/io/vlapic_priv.h b/sys/amd64/vmm/io/vlapic_priv.h index 719d4a5b3e0c..0480c968f70f 100644 --- a/sys/amd64/vmm/io/vlapic_priv.h +++ b/sys/amd64/vmm/io/vlapic_priv.h @@ -29,6 +29,8 @@ #ifndef _VLAPIC_PRIV_H_ #define _VLAPIC_PRIV_H_ +#include + /* * APIC Register: Offset Description */ @@ -91,6 +93,8 @@ enum boot_state { */ #define ISRVEC_STK_SIZE (16 + 1) +#define VLAPIC_MAXLVT_INDEX APIC_LVT_CMCI + struct vlapic { struct vm *vm; int vcpuid; @@ -111,12 +115,20 @@ struct vlapic { * 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; + uint8_t isrvec_stk[ISRVEC_STK_SIZE]; + int isrvec_stk_top; - uint64_t msr_apicbase; - enum boot_state boot_state; - uint32_t svr_last; + uint64_t msr_apicbase; + enum boot_state boot_state; + + /* + * Copies of some registers in the virtual APIC page. We do this for + * a couple of different reasons: + * - to be able to detect what changed (e.g. svr_last) + * - to maintain a coherent snapshot of the register (e.g. lvt_last) + */ + uint32_t svr_last; + uint32_t lvt_last[VLAPIC_MAXLVT_INDEX + 1]; }; void vlapic_init(struct vlapic *vlapic);