1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 33*fb03ca4eSNeel Natu #include <sys/lock.h> 34366f6083SPeter Grehan #include <sys/kernel.h> 35366f6083SPeter Grehan #include <sys/malloc.h> 36*fb03ca4eSNeel Natu #include <sys/mutex.h> 37366f6083SPeter Grehan #include <sys/systm.h> 38a5615c90SPeter Grehan #include <sys/smp.h> 39366f6083SPeter Grehan 40366f6083SPeter Grehan #include <machine/clock.h> 412d3a73edSNeel Natu #include <x86/specialreg.h> 4234a6b2d6SJohn Baldwin #include <x86/apicreg.h> 43366f6083SPeter Grehan 44366f6083SPeter Grehan #include <machine/vmm.h> 45366f6083SPeter Grehan 4677d8fd9bSNeel Natu #include "vmm_stat.h" 47366f6083SPeter Grehan #include "vmm_lapic.h" 48366f6083SPeter Grehan #include "vmm_ktr.h" 49366f6083SPeter Grehan #include "vlapic.h" 50b5b28fc9SNeel Natu #include "vioapic.h" 51366f6083SPeter Grehan 52366f6083SPeter Grehan #define VLAPIC_CTR0(vlapic, format) \ 53513c8d33SNeel Natu VCPU_CTR0((vlapic)->vm, (vlapic)->vcpuid, format) 54366f6083SPeter Grehan 55366f6083SPeter Grehan #define VLAPIC_CTR1(vlapic, format, p1) \ 56513c8d33SNeel Natu VCPU_CTR1((vlapic)->vm, (vlapic)->vcpuid, format, p1) 57366f6083SPeter Grehan 581c052192SNeel Natu #define VLAPIC_CTR2(vlapic, format, p1, p2) \ 591c052192SNeel Natu VCPU_CTR2((vlapic)->vm, (vlapic)->vcpuid, format, p1, p2) 601c052192SNeel Natu 61366f6083SPeter Grehan #define VLAPIC_CTR_IRR(vlapic, msg) \ 62366f6083SPeter Grehan do { \ 63366f6083SPeter Grehan uint32_t *irrptr = &(vlapic)->apic.irr0; \ 64366f6083SPeter Grehan irrptr[0] = irrptr[0]; /* silence compiler */ \ 65366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]); \ 66366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]); \ 67366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr2 0x%08x", irrptr[2 << 2]); \ 68366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr3 0x%08x", irrptr[3 << 2]); \ 69366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr4 0x%08x", irrptr[4 << 2]); \ 70366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr5 0x%08x", irrptr[5 << 2]); \ 71366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr6 0x%08x", irrptr[6 << 2]); \ 72366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr7 0x%08x", irrptr[7 << 2]); \ 73366f6083SPeter Grehan } while (0) 74366f6083SPeter Grehan 75366f6083SPeter Grehan #define VLAPIC_CTR_ISR(vlapic, msg) \ 76366f6083SPeter Grehan do { \ 77366f6083SPeter Grehan uint32_t *isrptr = &(vlapic)->apic.isr0; \ 78366f6083SPeter Grehan isrptr[0] = isrptr[0]; /* silence compiler */ \ 79366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]); \ 80366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]); \ 81366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr2 0x%08x", isrptr[2 << 2]); \ 82366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr3 0x%08x", isrptr[3 << 2]); \ 83366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr4 0x%08x", isrptr[4 << 2]); \ 84366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr5 0x%08x", isrptr[5 << 2]); \ 85366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr6 0x%08x", isrptr[6 << 2]); \ 86366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]); \ 87366f6083SPeter Grehan } while (0) 88366f6083SPeter Grehan 89366f6083SPeter Grehan static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic"); 90366f6083SPeter Grehan 91366f6083SPeter Grehan #define PRIO(x) ((x) >> 4) 92366f6083SPeter Grehan 93366f6083SPeter Grehan #define VLAPIC_VERSION (16) 94366f6083SPeter Grehan #define VLAPIC_MAXLVT_ENTRIES (5) 95366f6083SPeter Grehan 96a2da7af6SNeel Natu #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) 972d3a73edSNeel Natu 98edf89256SNeel Natu enum boot_state { 99edf89256SNeel Natu BS_INIT, 100edf89256SNeel Natu BS_SIPI, 101edf89256SNeel Natu BS_RUNNING 102edf89256SNeel Natu }; 103edf89256SNeel Natu 104366f6083SPeter Grehan struct vlapic { 105366f6083SPeter Grehan struct vm *vm; 106366f6083SPeter Grehan int vcpuid; 107366f6083SPeter Grehan 108366f6083SPeter Grehan struct LAPIC apic; 109366f6083SPeter Grehan 110366f6083SPeter Grehan int esr_update; 111366f6083SPeter Grehan 112*fb03ca4eSNeel Natu struct callout callout; /* vlapic timer */ 113*fb03ca4eSNeel Natu struct bintime timer_fire_bt; /* callout expiry time */ 114*fb03ca4eSNeel Natu struct bintime timer_freq_bt; /* timer frequency */ 115*fb03ca4eSNeel Natu struct bintime timer_period_bt; /* timer period */ 116*fb03ca4eSNeel Natu struct mtx timer_mtx; 117366f6083SPeter Grehan 118366f6083SPeter Grehan /* 119366f6083SPeter Grehan * The 'isrvec_stk' is a stack of vectors injected by the local apic. 120366f6083SPeter Grehan * A vector is popped from the stack when the processor does an EOI. 121366f6083SPeter Grehan * The vector on the top of the stack is used to compute the 122366f6083SPeter Grehan * Processor Priority in conjunction with the TPR. 123366f6083SPeter Grehan */ 124366f6083SPeter Grehan uint8_t isrvec_stk[ISRVEC_STK_SIZE]; 125366f6083SPeter Grehan int isrvec_stk_top; 1262d3a73edSNeel Natu 1272d3a73edSNeel Natu uint64_t msr_apicbase; 128edf89256SNeel Natu enum boot_state boot_state; 129366f6083SPeter Grehan }; 130366f6083SPeter Grehan 131*fb03ca4eSNeel Natu /* 132*fb03ca4eSNeel Natu * The 'vlapic->timer_mtx' is used to provide mutual exclusion between the 133*fb03ca4eSNeel Natu * vlapic_callout_handler() and vcpu accesses to the following registers: 134*fb03ca4eSNeel Natu * - initial count register aka icr_timer 135*fb03ca4eSNeel Natu * - current count register aka ccr_timer 136*fb03ca4eSNeel Natu * - divide config register aka dcr_timer 137*fb03ca4eSNeel Natu * - timer LVT register 138*fb03ca4eSNeel Natu * 139*fb03ca4eSNeel Natu * Note that the vlapic_callout_handler() does not write to any of these 140*fb03ca4eSNeel Natu * registers so they can be safely read from the vcpu context without locking. 141*fb03ca4eSNeel Natu */ 142*fb03ca4eSNeel Natu #define VLAPIC_TIMER_LOCK(vlapic) mtx_lock(&((vlapic)->timer_mtx)) 143*fb03ca4eSNeel Natu #define VLAPIC_TIMER_UNLOCK(vlapic) mtx_unlock(&((vlapic)->timer_mtx)) 144*fb03ca4eSNeel Natu #define VLAPIC_TIMER_LOCKED(vlapic) mtx_owned(&((vlapic)->timer_mtx)) 145*fb03ca4eSNeel Natu 1462e25737aSNeel Natu #define VLAPIC_BUS_FREQ tsc_freq 1472e25737aSNeel Natu 1482e25737aSNeel Natu static int 1492e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr) 1502e25737aSNeel Natu { 1512e25737aSNeel Natu switch (dcr & 0xB) { 152117e8f37SPeter Grehan case APIC_TDCR_1: 153117e8f37SPeter Grehan return (1); 1542e25737aSNeel Natu case APIC_TDCR_2: 1552e25737aSNeel Natu return (2); 1562e25737aSNeel Natu case APIC_TDCR_4: 1572e25737aSNeel Natu return (4); 1582e25737aSNeel Natu case APIC_TDCR_8: 1592e25737aSNeel Natu return (8); 1602e25737aSNeel Natu case APIC_TDCR_16: 1612e25737aSNeel Natu return (16); 1622e25737aSNeel Natu case APIC_TDCR_32: 1632e25737aSNeel Natu return (32); 1642e25737aSNeel Natu case APIC_TDCR_64: 1652e25737aSNeel Natu return (64); 1662e25737aSNeel Natu case APIC_TDCR_128: 1672e25737aSNeel Natu return (128); 1682e25737aSNeel Natu default: 1692e25737aSNeel Natu panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr); 1702e25737aSNeel Natu } 1712e25737aSNeel Natu } 1722e25737aSNeel Natu 173366f6083SPeter Grehan static void 174366f6083SPeter Grehan vlapic_mask_lvts(uint32_t *lvts, int num_lvt) 175366f6083SPeter Grehan { 176366f6083SPeter Grehan int i; 177366f6083SPeter Grehan for (i = 0; i < num_lvt; i++) { 178366f6083SPeter Grehan *lvts |= APIC_LVT_M; 179366f6083SPeter Grehan lvts += 4; 180366f6083SPeter Grehan } 181366f6083SPeter Grehan } 182366f6083SPeter Grehan 183366f6083SPeter Grehan #if 0 184366f6083SPeter Grehan static inline void 185366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) 186366f6083SPeter Grehan { 187366f6083SPeter Grehan printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset, 188366f6083SPeter Grehan *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS, 189366f6083SPeter Grehan *lvt & APIC_LVTT_M); 190366f6083SPeter Grehan } 191366f6083SPeter Grehan #endif 192366f6083SPeter Grehan 193*fb03ca4eSNeel Natu static uint32_t 194366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic) 195366f6083SPeter Grehan { 196*fb03ca4eSNeel Natu struct bintime bt_now, bt_rem; 197*fb03ca4eSNeel Natu struct LAPIC *lapic; 198*fb03ca4eSNeel Natu uint32_t ccr; 199*fb03ca4eSNeel Natu 200*fb03ca4eSNeel Natu ccr = 0; 201*fb03ca4eSNeel Natu lapic = &vlapic->apic; 202*fb03ca4eSNeel Natu 203*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 204*fb03ca4eSNeel Natu if (callout_active(&vlapic->callout)) { 205*fb03ca4eSNeel Natu /* 206*fb03ca4eSNeel Natu * If the timer is scheduled to expire in the future then 207*fb03ca4eSNeel Natu * compute the value of 'ccr' based on the remaining time. 208*fb03ca4eSNeel Natu */ 209*fb03ca4eSNeel Natu binuptime(&bt_now); 210*fb03ca4eSNeel Natu if (bintime_cmp(&vlapic->timer_fire_bt, &bt_now, >)) { 211*fb03ca4eSNeel Natu bt_rem = vlapic->timer_fire_bt; 212*fb03ca4eSNeel Natu bintime_sub(&bt_rem, &bt_now); 213*fb03ca4eSNeel Natu ccr += bt_rem.sec * BT2FREQ(&vlapic->timer_freq_bt); 214*fb03ca4eSNeel Natu ccr += bt_rem.frac / vlapic->timer_freq_bt.frac; 215*fb03ca4eSNeel Natu } 216*fb03ca4eSNeel Natu } 217*fb03ca4eSNeel Natu KASSERT(ccr <= lapic->icr_timer, ("vlapic_get_ccr: invalid ccr %#x, " 218*fb03ca4eSNeel Natu "icr_timer is %#x", ccr, lapic->icr_timer)); 219*fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic ccr_timer = %#x, icr_timer = %#x", 220*fb03ca4eSNeel Natu ccr, lapic->icr_timer); 221*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 222*fb03ca4eSNeel Natu return (ccr); 223*fb03ca4eSNeel Natu } 224*fb03ca4eSNeel Natu 225*fb03ca4eSNeel Natu static void 226*fb03ca4eSNeel Natu vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr) 227*fb03ca4eSNeel Natu { 228*fb03ca4eSNeel Natu struct LAPIC *lapic; 229*fb03ca4eSNeel Natu int divisor; 230*fb03ca4eSNeel Natu 231*fb03ca4eSNeel Natu lapic = &vlapic->apic; 232*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 233*fb03ca4eSNeel Natu 234*fb03ca4eSNeel Natu lapic->dcr_timer = dcr; 235*fb03ca4eSNeel Natu divisor = vlapic_timer_divisor(dcr); 236*fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", dcr, divisor); 237*fb03ca4eSNeel Natu 238*fb03ca4eSNeel Natu /* 239*fb03ca4eSNeel Natu * Update the timer frequency and the timer period. 240*fb03ca4eSNeel Natu * 241*fb03ca4eSNeel Natu * XXX changes to the frequency divider will not take effect until 242*fb03ca4eSNeel Natu * the timer is reloaded. 243*fb03ca4eSNeel Natu */ 244*fb03ca4eSNeel Natu FREQ2BT(VLAPIC_BUS_FREQ / divisor, &vlapic->timer_freq_bt); 245*fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 246*fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 247*fb03ca4eSNeel Natu 248*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 249366f6083SPeter Grehan } 250366f6083SPeter Grehan 251366f6083SPeter Grehan static void 252366f6083SPeter Grehan vlapic_update_errors(struct vlapic *vlapic) 253366f6083SPeter Grehan { 254366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 255366f6083SPeter Grehan lapic->esr = 0; // XXX 256366f6083SPeter Grehan } 257366f6083SPeter Grehan 258366f6083SPeter Grehan static void 259366f6083SPeter Grehan vlapic_init_ipi(struct vlapic *vlapic) 260366f6083SPeter Grehan { 261366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 262366f6083SPeter Grehan lapic->version = VLAPIC_VERSION; 263366f6083SPeter Grehan lapic->version |= (VLAPIC_MAXLVT_ENTRIES < MAXLVTSHIFT); 264366f6083SPeter Grehan lapic->dfr = 0xffffffff; 265366f6083SPeter Grehan lapic->svr = APIC_SVR_VECTOR; 266366f6083SPeter Grehan vlapic_mask_lvts(&lapic->lvt_timer, VLAPIC_MAXLVT_ENTRIES+1); 267366f6083SPeter Grehan } 268366f6083SPeter Grehan 269366f6083SPeter Grehan static int 27003cd0501SNeel Natu vlapic_reset(struct vlapic *vlapic) 271366f6083SPeter Grehan { 272366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 273366f6083SPeter Grehan 274366f6083SPeter Grehan memset(lapic, 0, sizeof(*lapic)); 275366f6083SPeter Grehan lapic->apr = vlapic->vcpuid; 276366f6083SPeter Grehan vlapic_init_ipi(vlapic); 277*fb03ca4eSNeel Natu vlapic_set_dcr(vlapic, 0); 278366f6083SPeter Grehan 279edf89256SNeel Natu if (vlapic->vcpuid == 0) 280edf89256SNeel Natu vlapic->boot_state = BS_RUNNING; /* BSP */ 281edf89256SNeel Natu else 282edf89256SNeel Natu vlapic->boot_state = BS_INIT; /* AP */ 283edf89256SNeel Natu 284366f6083SPeter Grehan return 0; 285366f6083SPeter Grehan 286366f6083SPeter Grehan } 287366f6083SPeter Grehan 288366f6083SPeter Grehan void 289b5b28fc9SNeel Natu vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) 290366f6083SPeter Grehan { 291366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 292b5b28fc9SNeel Natu uint32_t *irrptr, *tmrptr, mask; 293366f6083SPeter Grehan int idx; 294366f6083SPeter Grehan 295366f6083SPeter Grehan if (vector < 0 || vector >= 256) 296366f6083SPeter Grehan panic("vlapic_set_intr_ready: invalid vector %d\n", vector); 297366f6083SPeter Grehan 2981c052192SNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) { 2991c052192SNeel Natu VLAPIC_CTR1(vlapic, "vlapic is software disabled, ignoring " 3001c052192SNeel Natu "interrupt %d", vector); 3011c052192SNeel Natu return; 3021c052192SNeel Natu } 3031c052192SNeel Natu 304366f6083SPeter Grehan idx = (vector / 32) * 4; 305b5b28fc9SNeel Natu mask = 1 << (vector % 32); 306b5b28fc9SNeel Natu 307366f6083SPeter Grehan irrptr = &lapic->irr0; 308b5b28fc9SNeel Natu atomic_set_int(&irrptr[idx], mask); 309b5b28fc9SNeel Natu 310b5b28fc9SNeel Natu /* 311b5b28fc9SNeel Natu * Upon acceptance of an interrupt into the IRR the corresponding 312b5b28fc9SNeel Natu * TMR bit is cleared for edge-triggered interrupts and set for 313b5b28fc9SNeel Natu * level-triggered interrupts. 314b5b28fc9SNeel Natu */ 315b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 316b5b28fc9SNeel Natu if (level) 317b5b28fc9SNeel Natu atomic_set_int(&tmrptr[idx], mask); 318b5b28fc9SNeel Natu else 319b5b28fc9SNeel Natu atomic_clear_int(&tmrptr[idx], mask); 320b5b28fc9SNeel Natu 321366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready"); 322366f6083SPeter Grehan } 323366f6083SPeter Grehan 324366f6083SPeter Grehan static __inline uint32_t * 325*fb03ca4eSNeel Natu vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) 326366f6083SPeter Grehan { 327366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 328366f6083SPeter Grehan int i; 329366f6083SPeter Grehan 330366f6083SPeter Grehan if (offset < APIC_OFFSET_TIMER_LVT || offset > APIC_OFFSET_ERROR_LVT) { 331366f6083SPeter Grehan panic("vlapic_get_lvt: invalid LVT\n"); 332366f6083SPeter Grehan } 333366f6083SPeter Grehan i = (offset - APIC_OFFSET_TIMER_LVT) >> 2; 334366f6083SPeter Grehan return ((&lapic->lvt_timer) + i);; 335366f6083SPeter Grehan } 336366f6083SPeter Grehan 337*fb03ca4eSNeel Natu static __inline uint32_t 338*fb03ca4eSNeel Natu vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) 339*fb03ca4eSNeel Natu { 340*fb03ca4eSNeel Natu 341*fb03ca4eSNeel Natu return (*vlapic_get_lvtptr(vlapic, offset)); 342*fb03ca4eSNeel Natu } 343*fb03ca4eSNeel Natu 344*fb03ca4eSNeel Natu static void 345*fb03ca4eSNeel Natu vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val) 346*fb03ca4eSNeel Natu { 347*fb03ca4eSNeel Natu uint32_t *lvtptr; 348*fb03ca4eSNeel Natu struct LAPIC *lapic; 349*fb03ca4eSNeel Natu 350*fb03ca4eSNeel Natu lapic = &vlapic->apic; 351*fb03ca4eSNeel Natu lvtptr = vlapic_get_lvtptr(vlapic, offset); 352*fb03ca4eSNeel Natu 353*fb03ca4eSNeel Natu if (offset == APIC_OFFSET_TIMER_LVT) 354*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 355*fb03ca4eSNeel Natu 356*fb03ca4eSNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) 357*fb03ca4eSNeel Natu val |= APIC_LVT_M; 358*fb03ca4eSNeel Natu *lvtptr = val; 359*fb03ca4eSNeel Natu 360*fb03ca4eSNeel Natu if (offset == APIC_OFFSET_TIMER_LVT) 361*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 362*fb03ca4eSNeel Natu } 363*fb03ca4eSNeel Natu 364366f6083SPeter Grehan #if 1 365366f6083SPeter Grehan static void 366366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic) 367366f6083SPeter Grehan { 368366f6083SPeter Grehan int i; 369366f6083SPeter Grehan uint32_t *isrptr; 370366f6083SPeter Grehan 371366f6083SPeter Grehan isrptr = &vlapic->apic.isr0; 372366f6083SPeter Grehan for (i = 0; i < 8; i++) 373366f6083SPeter Grehan printf("ISR%d 0x%08x\n", i, isrptr[i * 4]); 374366f6083SPeter Grehan 375366f6083SPeter Grehan for (i = 0; i <= vlapic->isrvec_stk_top; i++) 376366f6083SPeter Grehan printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]); 377366f6083SPeter Grehan } 378366f6083SPeter Grehan #endif 379366f6083SPeter Grehan 380366f6083SPeter Grehan /* 381366f6083SPeter Grehan * Algorithm adopted from section "Interrupt, Task and Processor Priority" 382366f6083SPeter Grehan * in Intel Architecture Manual Vol 3a. 383366f6083SPeter Grehan */ 384366f6083SPeter Grehan static void 385366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic) 386366f6083SPeter Grehan { 387366f6083SPeter Grehan int isrvec, tpr, ppr; 388366f6083SPeter Grehan 389366f6083SPeter Grehan /* 390366f6083SPeter Grehan * Note that the value on the stack at index 0 is always 0. 391366f6083SPeter Grehan * 392366f6083SPeter Grehan * This is a placeholder for the value of ISRV when none of the 393366f6083SPeter Grehan * bits is set in the ISRx registers. 394366f6083SPeter Grehan */ 395366f6083SPeter Grehan isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top]; 396366f6083SPeter Grehan tpr = vlapic->apic.tpr; 397366f6083SPeter Grehan 398366f6083SPeter Grehan #if 1 399366f6083SPeter Grehan { 400366f6083SPeter Grehan int i, lastprio, curprio, vector, idx; 401366f6083SPeter Grehan uint32_t *isrptr; 402366f6083SPeter Grehan 403366f6083SPeter Grehan if (vlapic->isrvec_stk_top == 0 && isrvec != 0) 404366f6083SPeter Grehan panic("isrvec_stk is corrupted: %d", isrvec); 405366f6083SPeter Grehan 406366f6083SPeter Grehan /* 407366f6083SPeter Grehan * Make sure that the priority of the nested interrupts is 408366f6083SPeter Grehan * always increasing. 409366f6083SPeter Grehan */ 410366f6083SPeter Grehan lastprio = -1; 411366f6083SPeter Grehan for (i = 1; i <= vlapic->isrvec_stk_top; i++) { 412366f6083SPeter Grehan curprio = PRIO(vlapic->isrvec_stk[i]); 413366f6083SPeter Grehan if (curprio <= lastprio) { 414366f6083SPeter Grehan dump_isrvec_stk(vlapic); 415366f6083SPeter Grehan panic("isrvec_stk does not satisfy invariant"); 416366f6083SPeter Grehan } 417366f6083SPeter Grehan lastprio = curprio; 418366f6083SPeter Grehan } 419366f6083SPeter Grehan 420366f6083SPeter Grehan /* 421366f6083SPeter Grehan * Make sure that each bit set in the ISRx registers has a 422366f6083SPeter Grehan * corresponding entry on the isrvec stack. 423366f6083SPeter Grehan */ 424366f6083SPeter Grehan i = 1; 425366f6083SPeter Grehan isrptr = &vlapic->apic.isr0; 426366f6083SPeter Grehan for (vector = 0; vector < 256; vector++) { 427366f6083SPeter Grehan idx = (vector / 32) * 4; 428366f6083SPeter Grehan if (isrptr[idx] & (1 << (vector % 32))) { 429366f6083SPeter Grehan if (i > vlapic->isrvec_stk_top || 430366f6083SPeter Grehan vlapic->isrvec_stk[i] != vector) { 431366f6083SPeter Grehan dump_isrvec_stk(vlapic); 432366f6083SPeter Grehan panic("ISR and isrvec_stk out of sync"); 433366f6083SPeter Grehan } 434366f6083SPeter Grehan i++; 435366f6083SPeter Grehan } 436366f6083SPeter Grehan } 437366f6083SPeter Grehan } 438366f6083SPeter Grehan #endif 439366f6083SPeter Grehan 440366f6083SPeter Grehan if (PRIO(tpr) >= PRIO(isrvec)) 441366f6083SPeter Grehan ppr = tpr; 442366f6083SPeter Grehan else 443366f6083SPeter Grehan ppr = isrvec & 0xf0; 444366f6083SPeter Grehan 445366f6083SPeter Grehan vlapic->apic.ppr = ppr; 446366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr); 447366f6083SPeter Grehan } 448366f6083SPeter Grehan 449366f6083SPeter Grehan static void 450366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic) 451366f6083SPeter Grehan { 452366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 453b5b28fc9SNeel Natu uint32_t *isrptr, *tmrptr; 454b5b28fc9SNeel Natu int i, idx, bitpos, vector; 455366f6083SPeter Grehan 456366f6083SPeter Grehan isrptr = &lapic->isr0; 457b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 458366f6083SPeter Grehan 459366f6083SPeter Grehan /* 460366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 461366f6083SPeter Grehan * by the processor. 462366f6083SPeter Grehan */ 463366f6083SPeter Grehan for (i = 7; i > 0; i--) { 464366f6083SPeter Grehan idx = i * 4; 465366f6083SPeter Grehan bitpos = fls(isrptr[idx]); 466b5b28fc9SNeel Natu if (bitpos-- != 0) { 467366f6083SPeter Grehan if (vlapic->isrvec_stk_top <= 0) { 468366f6083SPeter Grehan panic("invalid vlapic isrvec_stk_top %d", 469366f6083SPeter Grehan vlapic->isrvec_stk_top); 470366f6083SPeter Grehan } 471b5b28fc9SNeel Natu isrptr[idx] &= ~(1 << bitpos); 472366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi"); 473366f6083SPeter Grehan vlapic->isrvec_stk_top--; 474366f6083SPeter Grehan vlapic_update_ppr(vlapic); 475b5b28fc9SNeel Natu if ((tmrptr[idx] & (1 << bitpos)) != 0) { 476b5b28fc9SNeel Natu vector = i * 32 + bitpos; 477b5b28fc9SNeel Natu vioapic_process_eoi(vlapic->vm, vlapic->vcpuid, 478b5b28fc9SNeel Natu vector); 479b5b28fc9SNeel Natu } 480366f6083SPeter Grehan return; 481366f6083SPeter Grehan } 482366f6083SPeter Grehan } 483366f6083SPeter Grehan } 484366f6083SPeter Grehan 485366f6083SPeter Grehan static __inline int 486*fb03ca4eSNeel Natu vlapic_get_lvt_field(uint32_t lvt, uint32_t mask) 487366f6083SPeter Grehan { 488*fb03ca4eSNeel Natu 489*fb03ca4eSNeel Natu return (lvt & mask); 490366f6083SPeter Grehan } 491366f6083SPeter Grehan 492366f6083SPeter Grehan static __inline int 493366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic) 494366f6083SPeter Grehan { 495*fb03ca4eSNeel Natu uint32_t lvt; 496366f6083SPeter Grehan 497366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 498366f6083SPeter Grehan 499366f6083SPeter Grehan return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC)); 500366f6083SPeter Grehan } 501366f6083SPeter Grehan 50277d8fd9bSNeel Natu static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic"); 50377d8fd9bSNeel Natu 504366f6083SPeter Grehan static void 505366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic) 506366f6083SPeter Grehan { 507366f6083SPeter Grehan int vector; 508*fb03ca4eSNeel Natu uint32_t lvt; 509*fb03ca4eSNeel Natu 510*fb03ca4eSNeel Natu KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked")); 511366f6083SPeter Grehan 512366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 513366f6083SPeter Grehan 514366f6083SPeter Grehan if (!vlapic_get_lvt_field(lvt, APIC_LVTT_M)) { 51577d8fd9bSNeel Natu vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1); 516366f6083SPeter Grehan vector = vlapic_get_lvt_field(lvt, APIC_LVTT_VECTOR); 517b5b28fc9SNeel Natu vlapic_set_intr_ready(vlapic, vector, false); 518*fb03ca4eSNeel Natu vcpu_notify_event(vlapic->vm, vlapic->vcpuid); 519366f6083SPeter Grehan } 520366f6083SPeter Grehan } 521366f6083SPeter Grehan 522*fb03ca4eSNeel Natu static void 523*fb03ca4eSNeel Natu vlapic_callout_handler(void *arg) 524*fb03ca4eSNeel Natu { 525*fb03ca4eSNeel Natu struct vlapic *vlapic; 526*fb03ca4eSNeel Natu struct bintime bt, btnow; 527*fb03ca4eSNeel Natu sbintime_t rem_sbt; 528*fb03ca4eSNeel Natu 529*fb03ca4eSNeel Natu vlapic = arg; 530*fb03ca4eSNeel Natu 531*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 532*fb03ca4eSNeel Natu if (callout_pending(&vlapic->callout)) /* callout was reset */ 533*fb03ca4eSNeel Natu goto done; 534*fb03ca4eSNeel Natu 535*fb03ca4eSNeel Natu if (!callout_active(&vlapic->callout)) /* callout was stopped */ 536*fb03ca4eSNeel Natu goto done; 537*fb03ca4eSNeel Natu 538*fb03ca4eSNeel Natu callout_deactivate(&vlapic->callout); 539*fb03ca4eSNeel Natu 540*fb03ca4eSNeel Natu KASSERT(vlapic->apic.icr_timer != 0, ("vlapic timer is disabled")); 541*fb03ca4eSNeel Natu 542*fb03ca4eSNeel Natu vlapic_fire_timer(vlapic); 543*fb03ca4eSNeel Natu 544*fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) { 545*fb03ca4eSNeel Natu binuptime(&btnow); 546*fb03ca4eSNeel Natu KASSERT(bintime_cmp(&btnow, &vlapic->timer_fire_bt, >=), 547*fb03ca4eSNeel Natu ("vlapic callout at %#lx.%#lx, expected at %#lx.#%lx", 548*fb03ca4eSNeel Natu btnow.sec, btnow.frac, vlapic->timer_fire_bt.sec, 549*fb03ca4eSNeel Natu vlapic->timer_fire_bt.frac)); 550*fb03ca4eSNeel Natu 551*fb03ca4eSNeel Natu /* 552*fb03ca4eSNeel Natu * Compute the delta between when the timer was supposed to 553*fb03ca4eSNeel Natu * fire and the present time. 554*fb03ca4eSNeel Natu */ 555*fb03ca4eSNeel Natu bt = btnow; 556*fb03ca4eSNeel Natu bintime_sub(&bt, &vlapic->timer_fire_bt); 557*fb03ca4eSNeel Natu 558*fb03ca4eSNeel Natu rem_sbt = bttosbt(vlapic->timer_period_bt); 559*fb03ca4eSNeel Natu if (bintime_cmp(&bt, &vlapic->timer_period_bt, <)) { 560*fb03ca4eSNeel Natu /* 561*fb03ca4eSNeel Natu * Adjust the time until the next countdown downward 562*fb03ca4eSNeel Natu * to account for the lost time. 563*fb03ca4eSNeel Natu */ 564*fb03ca4eSNeel Natu rem_sbt -= bttosbt(bt); 565*fb03ca4eSNeel Natu } else { 566*fb03ca4eSNeel Natu /* 567*fb03ca4eSNeel Natu * If the delta is greater than the timer period then 568*fb03ca4eSNeel Natu * just reset our time base instead of trying to catch 569*fb03ca4eSNeel Natu * up. 570*fb03ca4eSNeel Natu */ 571*fb03ca4eSNeel Natu vlapic->timer_fire_bt = btnow; 572*fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic timer lagging by %lu " 573*fb03ca4eSNeel Natu "usecs, period is %lu usecs - resetting time base", 574*fb03ca4eSNeel Natu bttosbt(bt) / SBT_1US, 575*fb03ca4eSNeel Natu bttosbt(vlapic->timer_period_bt) / SBT_1US); 576*fb03ca4eSNeel Natu } 577*fb03ca4eSNeel Natu 578*fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 579*fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, rem_sbt, 0, 580*fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 581*fb03ca4eSNeel Natu } 582*fb03ca4eSNeel Natu done: 583*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 584*fb03ca4eSNeel Natu } 585*fb03ca4eSNeel Natu 586*fb03ca4eSNeel Natu static void 587*fb03ca4eSNeel Natu vlapic_set_icr_timer(struct vlapic *vlapic, uint32_t icr_timer) 588*fb03ca4eSNeel Natu { 589*fb03ca4eSNeel Natu struct LAPIC *lapic; 590*fb03ca4eSNeel Natu sbintime_t sbt; 591*fb03ca4eSNeel Natu 592*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 593*fb03ca4eSNeel Natu 594*fb03ca4eSNeel Natu lapic = &vlapic->apic; 595*fb03ca4eSNeel Natu lapic->icr_timer = icr_timer; 596*fb03ca4eSNeel Natu 597*fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 598*fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, icr_timer); 599*fb03ca4eSNeel Natu 600*fb03ca4eSNeel Natu if (icr_timer != 0) { 601*fb03ca4eSNeel Natu binuptime(&vlapic->timer_fire_bt); 602*fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 603*fb03ca4eSNeel Natu 604*fb03ca4eSNeel Natu sbt = bttosbt(vlapic->timer_period_bt); 605*fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, sbt, 0, 606*fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 607*fb03ca4eSNeel Natu } else 608*fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 609*fb03ca4eSNeel Natu 610*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 611*fb03ca4eSNeel Natu } 612*fb03ca4eSNeel Natu 6130acb0d84SNeel Natu static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu"); 6140acb0d84SNeel Natu 615366f6083SPeter Grehan static int 616366f6083SPeter Grehan lapic_process_icr(struct vlapic *vlapic, uint64_t icrval) 617366f6083SPeter Grehan { 618366f6083SPeter Grehan int i; 619a5615c90SPeter Grehan cpuset_t dmask; 620366f6083SPeter Grehan uint32_t dest, vec, mode; 621edf89256SNeel Natu struct vlapic *vlapic2; 622edf89256SNeel Natu struct vm_exit *vmexit; 623366f6083SPeter Grehan 624a2da7af6SNeel Natu if (x2apic(vlapic)) 625366f6083SPeter Grehan dest = icrval >> 32; 626a2da7af6SNeel Natu else 627a2da7af6SNeel Natu dest = icrval >> (32 + 24); 628366f6083SPeter Grehan vec = icrval & APIC_VECTOR_MASK; 629366f6083SPeter Grehan mode = icrval & APIC_DELMODE_MASK; 630366f6083SPeter Grehan 631366f6083SPeter Grehan if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) { 632366f6083SPeter Grehan switch (icrval & APIC_DEST_MASK) { 633366f6083SPeter Grehan case APIC_DEST_DESTFLD: 634a5615c90SPeter Grehan CPU_SETOF(dest, &dmask); 635366f6083SPeter Grehan break; 636366f6083SPeter Grehan case APIC_DEST_SELF: 637a5615c90SPeter Grehan CPU_SETOF(vlapic->vcpuid, &dmask); 638366f6083SPeter Grehan break; 639366f6083SPeter Grehan case APIC_DEST_ALLISELF: 640366f6083SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 641366f6083SPeter Grehan break; 642366f6083SPeter Grehan case APIC_DEST_ALLESELF: 643a5615c90SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 644a5615c90SPeter Grehan CPU_CLR(vlapic->vcpuid, &dmask); 645366f6083SPeter Grehan break; 6461e2751ddSSergey Kandaurov default: 6471e2751ddSSergey Kandaurov CPU_ZERO(&dmask); /* satisfy gcc */ 6481e2751ddSSergey Kandaurov break; 649366f6083SPeter Grehan } 650366f6083SPeter Grehan 65182f2974aSSergey Kandaurov while ((i = CPU_FFS(&dmask)) != 0) { 652a5615c90SPeter Grehan i--; 653a5615c90SPeter Grehan CPU_CLR(i, &dmask); 6540acb0d84SNeel Natu if (mode == APIC_DELMODE_FIXED) { 655b5b28fc9SNeel Natu lapic_intr_edge(vlapic->vm, i, vec); 6560acb0d84SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 6570acb0d84SNeel Natu IPIS_SENT, i, 1); 6580acb0d84SNeel Natu } else 659366f6083SPeter Grehan vm_inject_nmi(vlapic->vm, i); 660366f6083SPeter Grehan } 661366f6083SPeter Grehan 662366f6083SPeter Grehan return (0); /* handled completely in the kernel */ 663366f6083SPeter Grehan } 664366f6083SPeter Grehan 665edf89256SNeel Natu if (mode == APIC_DELMODE_INIT) { 666edf89256SNeel Natu if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) 667edf89256SNeel Natu return (0); 668edf89256SNeel Natu 669edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 670edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 671edf89256SNeel Natu 672edf89256SNeel Natu /* move from INIT to waiting-for-SIPI state */ 673edf89256SNeel Natu if (vlapic2->boot_state == BS_INIT) { 674edf89256SNeel Natu vlapic2->boot_state = BS_SIPI; 675edf89256SNeel Natu } 676edf89256SNeel Natu 677edf89256SNeel Natu return (0); 678edf89256SNeel Natu } 679edf89256SNeel Natu } 680edf89256SNeel Natu 681edf89256SNeel Natu if (mode == APIC_DELMODE_STARTUP) { 682edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 683edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 684edf89256SNeel Natu 685edf89256SNeel Natu /* 686edf89256SNeel Natu * Ignore SIPIs in any state other than wait-for-SIPI 687edf89256SNeel Natu */ 688edf89256SNeel Natu if (vlapic2->boot_state != BS_SIPI) 689edf89256SNeel Natu return (0); 690edf89256SNeel Natu 691edf89256SNeel Natu vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); 692edf89256SNeel Natu vmexit->exitcode = VM_EXITCODE_SPINUP_AP; 693edf89256SNeel Natu vmexit->u.spinup_ap.vcpu = dest; 694edf89256SNeel Natu vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT; 695edf89256SNeel Natu 696366f6083SPeter Grehan /* 697366f6083SPeter Grehan * XXX this assumes that the startup IPI always succeeds 698366f6083SPeter Grehan */ 699edf89256SNeel Natu vlapic2->boot_state = BS_RUNNING; 700edf89256SNeel Natu vm_activate_cpu(vlapic2->vm, dest); 701edf89256SNeel Natu 702edf89256SNeel Natu return (0); 703edf89256SNeel Natu } 704edf89256SNeel Natu } 705366f6083SPeter Grehan 706366f6083SPeter Grehan /* 707366f6083SPeter Grehan * This will cause a return to userland. 708366f6083SPeter Grehan */ 709366f6083SPeter Grehan return (1); 710366f6083SPeter Grehan } 711366f6083SPeter Grehan 712366f6083SPeter Grehan int 713366f6083SPeter Grehan vlapic_pending_intr(struct vlapic *vlapic) 714366f6083SPeter Grehan { 715366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 716366f6083SPeter Grehan int idx, i, bitpos, vector; 717366f6083SPeter Grehan uint32_t *irrptr, val; 718366f6083SPeter Grehan 719366f6083SPeter Grehan irrptr = &lapic->irr0; 720366f6083SPeter Grehan 721366f6083SPeter Grehan /* 722366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 723366f6083SPeter Grehan * by the processor. 724366f6083SPeter Grehan */ 725366f6083SPeter Grehan for (i = 7; i > 0; i--) { 726366f6083SPeter Grehan idx = i * 4; 727366f6083SPeter Grehan val = atomic_load_acq_int(&irrptr[idx]); 728366f6083SPeter Grehan bitpos = fls(val); 729366f6083SPeter Grehan if (bitpos != 0) { 730366f6083SPeter Grehan vector = i * 32 + (bitpos - 1); 731366f6083SPeter Grehan if (PRIO(vector) > PRIO(lapic->ppr)) { 732366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "pending intr %d", vector); 733366f6083SPeter Grehan return (vector); 734366f6083SPeter Grehan } else 735366f6083SPeter Grehan break; 736366f6083SPeter Grehan } 737366f6083SPeter Grehan } 738366f6083SPeter Grehan return (-1); 739366f6083SPeter Grehan } 740366f6083SPeter Grehan 741366f6083SPeter Grehan void 742366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector) 743366f6083SPeter Grehan { 744366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 745366f6083SPeter Grehan uint32_t *irrptr, *isrptr; 746366f6083SPeter Grehan int idx, stk_top; 747366f6083SPeter Grehan 748366f6083SPeter Grehan /* 749366f6083SPeter Grehan * clear the ready bit for vector being accepted in irr 750366f6083SPeter Grehan * and set the vector as in service in isr. 751366f6083SPeter Grehan */ 752366f6083SPeter Grehan idx = (vector / 32) * 4; 753366f6083SPeter Grehan 754366f6083SPeter Grehan irrptr = &lapic->irr0; 755366f6083SPeter Grehan atomic_clear_int(&irrptr[idx], 1 << (vector % 32)); 756366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted"); 757366f6083SPeter Grehan 758366f6083SPeter Grehan isrptr = &lapic->isr0; 759366f6083SPeter Grehan isrptr[idx] |= 1 << (vector % 32); 760366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted"); 761366f6083SPeter Grehan 762366f6083SPeter Grehan /* 763366f6083SPeter Grehan * Update the PPR 764366f6083SPeter Grehan */ 765366f6083SPeter Grehan vlapic->isrvec_stk_top++; 766366f6083SPeter Grehan 767366f6083SPeter Grehan stk_top = vlapic->isrvec_stk_top; 768366f6083SPeter Grehan if (stk_top >= ISRVEC_STK_SIZE) 769366f6083SPeter Grehan panic("isrvec_stk_top overflow %d", stk_top); 770366f6083SPeter Grehan 771366f6083SPeter Grehan vlapic->isrvec_stk[stk_top] = vector; 772366f6083SPeter Grehan vlapic_update_ppr(vlapic); 773366f6083SPeter Grehan } 774366f6083SPeter Grehan 7751c052192SNeel Natu static void 7761c052192SNeel Natu lapic_set_svr(struct vlapic *vlapic, uint32_t new) 7771c052192SNeel Natu { 7781c052192SNeel Natu struct LAPIC *lapic; 7791c052192SNeel Natu uint32_t old, changed; 7801c052192SNeel Natu 7811c052192SNeel Natu lapic = &vlapic->apic; 7821c052192SNeel Natu old = lapic->svr; 7831c052192SNeel Natu changed = old ^ new; 7841c052192SNeel Natu if ((changed & APIC_SVR_ENABLE) != 0) { 7851c052192SNeel Natu if ((new & APIC_SVR_ENABLE) == 0) { 786*fb03ca4eSNeel Natu /* 787*fb03ca4eSNeel Natu * The apic is now disabled so stop the apic timer. 788*fb03ca4eSNeel Natu */ 7891c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-disabled"); 790*fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 791*fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 792*fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 7931c052192SNeel Natu } else { 794*fb03ca4eSNeel Natu /* 795*fb03ca4eSNeel Natu * The apic is now enabled so restart the apic timer 796*fb03ca4eSNeel Natu * if it is configured in periodic mode. 797*fb03ca4eSNeel Natu */ 7981c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-enabled"); 799*fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) 800*fb03ca4eSNeel Natu vlapic_set_icr_timer(vlapic, lapic->icr_timer); 8011c052192SNeel Natu } 8021c052192SNeel Natu } 8031c052192SNeel Natu lapic->svr = new; 8041c052192SNeel Natu } 8051c052192SNeel Natu 806366f6083SPeter Grehan int 80703cd0501SNeel Natu vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data) 808366f6083SPeter Grehan { 809366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 810366f6083SPeter Grehan uint32_t *reg; 811366f6083SPeter Grehan int i; 812366f6083SPeter Grehan 813366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 814366f6083SPeter Grehan *data = 0; 8151c052192SNeel Natu goto done; 816366f6083SPeter Grehan } 817366f6083SPeter Grehan 818366f6083SPeter Grehan offset &= ~3; 819366f6083SPeter Grehan switch(offset) 820366f6083SPeter Grehan { 821366f6083SPeter Grehan case APIC_OFFSET_ID: 8222d3a73edSNeel Natu if (x2apic(vlapic)) 8232d3a73edSNeel Natu *data = vlapic->vcpuid; 8242d3a73edSNeel Natu else 8252d3a73edSNeel Natu *data = vlapic->vcpuid << 24; 826366f6083SPeter Grehan break; 827366f6083SPeter Grehan case APIC_OFFSET_VER: 828366f6083SPeter Grehan *data = lapic->version; 829366f6083SPeter Grehan break; 830366f6083SPeter Grehan case APIC_OFFSET_TPR: 831366f6083SPeter Grehan *data = lapic->tpr; 832366f6083SPeter Grehan break; 833366f6083SPeter Grehan case APIC_OFFSET_APR: 834366f6083SPeter Grehan *data = lapic->apr; 835366f6083SPeter Grehan break; 836366f6083SPeter Grehan case APIC_OFFSET_PPR: 837366f6083SPeter Grehan *data = lapic->ppr; 838366f6083SPeter Grehan break; 839366f6083SPeter Grehan case APIC_OFFSET_EOI: 840366f6083SPeter Grehan *data = lapic->eoi; 841366f6083SPeter Grehan break; 842366f6083SPeter Grehan case APIC_OFFSET_LDR: 843366f6083SPeter Grehan *data = lapic->ldr; 844366f6083SPeter Grehan break; 845366f6083SPeter Grehan case APIC_OFFSET_DFR: 846366f6083SPeter Grehan *data = lapic->dfr; 847366f6083SPeter Grehan break; 848366f6083SPeter Grehan case APIC_OFFSET_SVR: 849366f6083SPeter Grehan *data = lapic->svr; 850366f6083SPeter Grehan break; 851366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 852366f6083SPeter Grehan i = (offset - APIC_OFFSET_ISR0) >> 2; 853366f6083SPeter Grehan reg = &lapic->isr0; 854366f6083SPeter Grehan *data = *(reg + i); 855366f6083SPeter Grehan break; 856366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 857366f6083SPeter Grehan i = (offset - APIC_OFFSET_TMR0) >> 2; 858366f6083SPeter Grehan reg = &lapic->tmr0; 859366f6083SPeter Grehan *data = *(reg + i); 860366f6083SPeter Grehan break; 861366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 862366f6083SPeter Grehan i = (offset - APIC_OFFSET_IRR0) >> 2; 863366f6083SPeter Grehan reg = &lapic->irr0; 864366f6083SPeter Grehan *data = atomic_load_acq_int(reg + i); 865366f6083SPeter Grehan break; 866366f6083SPeter Grehan case APIC_OFFSET_ESR: 867366f6083SPeter Grehan *data = lapic->esr; 868366f6083SPeter Grehan break; 869366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 870366f6083SPeter Grehan *data = lapic->icr_lo; 871366f6083SPeter Grehan break; 872366f6083SPeter Grehan case APIC_OFFSET_ICR_HI: 873366f6083SPeter Grehan *data = lapic->icr_hi; 874366f6083SPeter Grehan break; 875366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 876*fb03ca4eSNeel Natu *data = vlapic_get_lvt(vlapic, offset); 877366f6083SPeter Grehan break; 878366f6083SPeter Grehan case APIC_OFFSET_ICR: 879366f6083SPeter Grehan *data = lapic->icr_timer; 880366f6083SPeter Grehan break; 881366f6083SPeter Grehan case APIC_OFFSET_CCR: 882366f6083SPeter Grehan *data = vlapic_get_ccr(vlapic); 883366f6083SPeter Grehan break; 884366f6083SPeter Grehan case APIC_OFFSET_DCR: 885366f6083SPeter Grehan *data = lapic->dcr_timer; 886366f6083SPeter Grehan break; 887366f6083SPeter Grehan case APIC_OFFSET_RRR: 888366f6083SPeter Grehan default: 889366f6083SPeter Grehan *data = 0; 890366f6083SPeter Grehan break; 891366f6083SPeter Grehan } 8921c052192SNeel Natu done: 8931c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic read offset %#x, data %#lx", offset, *data); 894366f6083SPeter Grehan return 0; 895366f6083SPeter Grehan } 896366f6083SPeter Grehan 897366f6083SPeter Grehan int 89803cd0501SNeel Natu vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data) 899366f6083SPeter Grehan { 900366f6083SPeter Grehan struct LAPIC *lapic = &vlapic->apic; 901366f6083SPeter Grehan int retval; 902366f6083SPeter Grehan 9031c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic write offset %#x, data %#lx", offset, data); 9041c052192SNeel Natu 905366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 906366f6083SPeter Grehan return 0; 907366f6083SPeter Grehan } 908366f6083SPeter Grehan 909366f6083SPeter Grehan retval = 0; 910366f6083SPeter Grehan offset &= ~3; 911366f6083SPeter Grehan switch(offset) 912366f6083SPeter Grehan { 913366f6083SPeter Grehan case APIC_OFFSET_ID: 914366f6083SPeter Grehan break; 915366f6083SPeter Grehan case APIC_OFFSET_TPR: 916366f6083SPeter Grehan lapic->tpr = data & 0xff; 917366f6083SPeter Grehan vlapic_update_ppr(vlapic); 918366f6083SPeter Grehan break; 919366f6083SPeter Grehan case APIC_OFFSET_EOI: 920366f6083SPeter Grehan vlapic_process_eoi(vlapic); 921366f6083SPeter Grehan break; 922366f6083SPeter Grehan case APIC_OFFSET_LDR: 923366f6083SPeter Grehan break; 924366f6083SPeter Grehan case APIC_OFFSET_DFR: 925366f6083SPeter Grehan break; 926366f6083SPeter Grehan case APIC_OFFSET_SVR: 9271c052192SNeel Natu lapic_set_svr(vlapic, data); 928366f6083SPeter Grehan break; 929366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 930a2da7af6SNeel Natu if (!x2apic(vlapic)) { 931a2da7af6SNeel Natu data &= 0xffffffff; 932a2da7af6SNeel Natu data |= (uint64_t)lapic->icr_hi << 32; 933a2da7af6SNeel Natu } 934366f6083SPeter Grehan retval = lapic_process_icr(vlapic, data); 935366f6083SPeter Grehan break; 936a2da7af6SNeel Natu case APIC_OFFSET_ICR_HI: 937a2da7af6SNeel Natu if (!x2apic(vlapic)) { 938a2da7af6SNeel Natu retval = 0; 939a2da7af6SNeel Natu lapic->icr_hi = data; 940a2da7af6SNeel Natu } 941a2da7af6SNeel Natu break; 942366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 943*fb03ca4eSNeel Natu vlapic_set_lvt(vlapic, offset, data); 944366f6083SPeter Grehan break; 945366f6083SPeter Grehan case APIC_OFFSET_ICR: 946*fb03ca4eSNeel Natu vlapic_set_icr_timer(vlapic, data); 947366f6083SPeter Grehan break; 948366f6083SPeter Grehan 949366f6083SPeter Grehan case APIC_OFFSET_DCR: 950*fb03ca4eSNeel Natu vlapic_set_dcr(vlapic, data); 951366f6083SPeter Grehan break; 952366f6083SPeter Grehan 953366f6083SPeter Grehan case APIC_OFFSET_ESR: 954366f6083SPeter Grehan vlapic_update_errors(vlapic); 955366f6083SPeter Grehan break; 956366f6083SPeter Grehan case APIC_OFFSET_VER: 957366f6083SPeter Grehan case APIC_OFFSET_APR: 958366f6083SPeter Grehan case APIC_OFFSET_PPR: 959366f6083SPeter Grehan case APIC_OFFSET_RRR: 960366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 961366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 962366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 963366f6083SPeter Grehan case APIC_OFFSET_CCR: 964366f6083SPeter Grehan default: 965366f6083SPeter Grehan // Read only. 966366f6083SPeter Grehan break; 967366f6083SPeter Grehan } 968366f6083SPeter Grehan 969366f6083SPeter Grehan return (retval); 970366f6083SPeter Grehan } 971366f6083SPeter Grehan 972366f6083SPeter Grehan struct vlapic * 973366f6083SPeter Grehan vlapic_init(struct vm *vm, int vcpuid) 974366f6083SPeter Grehan { 975366f6083SPeter Grehan struct vlapic *vlapic; 976366f6083SPeter Grehan 977366f6083SPeter Grehan vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO); 978366f6083SPeter Grehan vlapic->vm = vm; 979366f6083SPeter Grehan vlapic->vcpuid = vcpuid; 9802d3a73edSNeel Natu 981*fb03ca4eSNeel Natu mtx_init(&vlapic->timer_mtx, "vlapic timer mtx", NULL, MTX_DEF); 982*fb03ca4eSNeel Natu callout_init(&vlapic->callout, 1); 983*fb03ca4eSNeel Natu 984a2da7af6SNeel Natu vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; 9852d3a73edSNeel Natu 9862d3a73edSNeel Natu if (vcpuid == 0) 9872d3a73edSNeel Natu vlapic->msr_apicbase |= APICBASE_BSP; 9882d3a73edSNeel Natu 98903cd0501SNeel Natu vlapic_reset(vlapic); 990366f6083SPeter Grehan 991366f6083SPeter Grehan return (vlapic); 992366f6083SPeter Grehan } 993366f6083SPeter Grehan 994366f6083SPeter Grehan void 995366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic) 996366f6083SPeter Grehan { 99703cd0501SNeel Natu 998*fb03ca4eSNeel Natu callout_drain(&vlapic->callout); 999366f6083SPeter Grehan free(vlapic, M_VLAPIC); 1000366f6083SPeter Grehan } 10012d3a73edSNeel Natu 10022d3a73edSNeel Natu uint64_t 10032d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic) 10042d3a73edSNeel Natu { 10052d3a73edSNeel Natu 10062d3a73edSNeel Natu return (vlapic->msr_apicbase); 10072d3a73edSNeel Natu } 10082d3a73edSNeel Natu 10092d3a73edSNeel Natu void 10102d3a73edSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val) 10112d3a73edSNeel Natu { 1012a2da7af6SNeel Natu int err; 1013a2da7af6SNeel Natu enum x2apic_state state; 1014a2da7af6SNeel Natu 1015a2da7af6SNeel Natu err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state); 1016a2da7af6SNeel Natu if (err) 1017a2da7af6SNeel Natu panic("vlapic_set_apicbase: err %d fetching x2apic state", err); 1018a2da7af6SNeel Natu 1019a2da7af6SNeel Natu if (state == X2APIC_DISABLED) 1020a2da7af6SNeel Natu val &= ~APICBASE_X2APIC; 10212d3a73edSNeel Natu 10222d3a73edSNeel Natu vlapic->msr_apicbase = val; 10232d3a73edSNeel Natu } 102473820fb0SNeel Natu 102573820fb0SNeel Natu void 102673820fb0SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 102773820fb0SNeel Natu { 102873820fb0SNeel Natu struct vlapic *vlapic; 102973820fb0SNeel Natu 103073820fb0SNeel Natu vlapic = vm_lapic(vm, vcpuid); 103173820fb0SNeel Natu 1032485f986aSNeel Natu if (state == X2APIC_DISABLED) 103373820fb0SNeel Natu vlapic->msr_apicbase &= ~APICBASE_X2APIC; 103473820fb0SNeel Natu } 10351c052192SNeel Natu 10361c052192SNeel Natu bool 10371c052192SNeel Natu vlapic_enabled(struct vlapic *vlapic) 10381c052192SNeel Natu { 10391c052192SNeel Natu struct LAPIC *lapic = &vlapic->apic; 10401c052192SNeel Natu 10411c052192SNeel Natu if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && 10421c052192SNeel Natu (lapic->svr & APIC_SVR_ENABLE) != 0) 10431c052192SNeel Natu return (true); 10441c052192SNeel Natu else 10451c052192SNeel Natu return (false); 10461c052192SNeel Natu } 1047