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> 33fb03ca4eSNeel Natu #include <sys/lock.h> 34366f6083SPeter Grehan #include <sys/kernel.h> 35366f6083SPeter Grehan #include <sys/malloc.h> 36fb03ca4eSNeel Natu #include <sys/mutex.h> 37366f6083SPeter Grehan #include <sys/systm.h> 38a5615c90SPeter Grehan #include <sys/smp.h> 39366f6083SPeter Grehan 402d3a73edSNeel Natu #include <x86/specialreg.h> 4134a6b2d6SJohn Baldwin #include <x86/apicreg.h> 42366f6083SPeter Grehan 43de5ea6b6SNeel Natu #include <machine/clock.h> 44de5ea6b6SNeel Natu #include <machine/smp.h> 45de5ea6b6SNeel Natu 46366f6083SPeter Grehan #include <machine/vmm.h> 47366f6083SPeter Grehan 48de5ea6b6SNeel Natu #include "vmm_ipi.h" 49366f6083SPeter Grehan #include "vmm_lapic.h" 50366f6083SPeter Grehan #include "vmm_ktr.h" 51de5ea6b6SNeel Natu #include "vmm_stat.h" 52de5ea6b6SNeel Natu 53366f6083SPeter Grehan #include "vlapic.h" 54de5ea6b6SNeel Natu #include "vlapic_priv.h" 55b5b28fc9SNeel Natu #include "vioapic.h" 56366f6083SPeter Grehan 57366f6083SPeter Grehan #define VLAPIC_CTR0(vlapic, format) \ 58513c8d33SNeel Natu VCPU_CTR0((vlapic)->vm, (vlapic)->vcpuid, format) 59366f6083SPeter Grehan 60366f6083SPeter Grehan #define VLAPIC_CTR1(vlapic, format, p1) \ 61513c8d33SNeel Natu VCPU_CTR1((vlapic)->vm, (vlapic)->vcpuid, format, p1) 62366f6083SPeter Grehan 631c052192SNeel Natu #define VLAPIC_CTR2(vlapic, format, p1, p2) \ 641c052192SNeel Natu VCPU_CTR2((vlapic)->vm, (vlapic)->vcpuid, format, p1, p2) 651c052192SNeel Natu 66366f6083SPeter Grehan #define VLAPIC_CTR_IRR(vlapic, msg) \ 67366f6083SPeter Grehan do { \ 68de5ea6b6SNeel Natu uint32_t *irrptr = &(vlapic)->apic_page->irr0; \ 69366f6083SPeter Grehan irrptr[0] = irrptr[0]; /* silence compiler */ \ 70366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]); \ 71366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]); \ 72366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr2 0x%08x", irrptr[2 << 2]); \ 73366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr3 0x%08x", irrptr[3 << 2]); \ 74366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr4 0x%08x", irrptr[4 << 2]); \ 75366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr5 0x%08x", irrptr[5 << 2]); \ 76366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr6 0x%08x", irrptr[6 << 2]); \ 77366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " irr7 0x%08x", irrptr[7 << 2]); \ 78366f6083SPeter Grehan } while (0) 79366f6083SPeter Grehan 80366f6083SPeter Grehan #define VLAPIC_CTR_ISR(vlapic, msg) \ 81366f6083SPeter Grehan do { \ 82de5ea6b6SNeel Natu uint32_t *isrptr = &(vlapic)->apic_page->isr0; \ 83366f6083SPeter Grehan isrptr[0] = isrptr[0]; /* silence compiler */ \ 84366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]); \ 85366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]); \ 86366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr2 0x%08x", isrptr[2 << 2]); \ 87366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr3 0x%08x", isrptr[3 << 2]); \ 88366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr4 0x%08x", isrptr[4 << 2]); \ 89366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr5 0x%08x", isrptr[5 << 2]); \ 90366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr6 0x%08x", isrptr[6 << 2]); \ 91366f6083SPeter Grehan VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]); \ 92366f6083SPeter Grehan } while (0) 93366f6083SPeter Grehan 94366f6083SPeter Grehan #define PRIO(x) ((x) >> 4) 95366f6083SPeter Grehan 96366f6083SPeter Grehan #define VLAPIC_VERSION (16) 97330baf58SJohn Baldwin #define VLAPIC_MAXLVT_ENTRIES (APIC_LVT_CMCI) 98366f6083SPeter Grehan 99a2da7af6SNeel Natu #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) 1002d3a73edSNeel Natu 101fb03ca4eSNeel Natu /* 102fb03ca4eSNeel Natu * The 'vlapic->timer_mtx' is used to provide mutual exclusion between the 103fb03ca4eSNeel Natu * vlapic_callout_handler() and vcpu accesses to the following registers: 104fb03ca4eSNeel Natu * - initial count register aka icr_timer 105fb03ca4eSNeel Natu * - current count register aka ccr_timer 106fb03ca4eSNeel Natu * - divide config register aka dcr_timer 107fb03ca4eSNeel Natu * - timer LVT register 108fb03ca4eSNeel Natu * 109fb03ca4eSNeel Natu * Note that the vlapic_callout_handler() does not write to any of these 110fb03ca4eSNeel Natu * registers so they can be safely read from the vcpu context without locking. 111fb03ca4eSNeel Natu */ 112becd9849SNeel Natu #define VLAPIC_TIMER_LOCK(vlapic) mtx_lock_spin(&((vlapic)->timer_mtx)) 113becd9849SNeel Natu #define VLAPIC_TIMER_UNLOCK(vlapic) mtx_unlock_spin(&((vlapic)->timer_mtx)) 114fb03ca4eSNeel Natu #define VLAPIC_TIMER_LOCKED(vlapic) mtx_owned(&((vlapic)->timer_mtx)) 115fb03ca4eSNeel Natu 1162e25737aSNeel Natu #define VLAPIC_BUS_FREQ tsc_freq 1172e25737aSNeel Natu 1184f8be175SNeel Natu static __inline uint32_t 1194f8be175SNeel Natu vlapic_get_id(struct vlapic *vlapic) 1204f8be175SNeel Natu { 1214f8be175SNeel Natu 1224f8be175SNeel Natu if (x2apic(vlapic)) 1234f8be175SNeel Natu return (vlapic->vcpuid); 1244f8be175SNeel Natu else 1254f8be175SNeel Natu return (vlapic->vcpuid << 24); 1264f8be175SNeel Natu } 1274f8be175SNeel Natu 1283f0ddc7cSNeel Natu static uint32_t 1293f0ddc7cSNeel Natu x2apic_ldr(struct vlapic *vlapic) 1304f8be175SNeel Natu { 1314f8be175SNeel Natu int apicid; 1324f8be175SNeel Natu uint32_t ldr; 1334f8be175SNeel Natu 1344f8be175SNeel Natu apicid = vlapic_get_id(vlapic); 1354f8be175SNeel Natu ldr = 1 << (apicid & 0xf); 1364f8be175SNeel Natu ldr |= (apicid & 0xffff0) << 12; 1374f8be175SNeel Natu return (ldr); 1384f8be175SNeel Natu } 1394f8be175SNeel Natu 1403f0ddc7cSNeel Natu void 1413f0ddc7cSNeel Natu vlapic_dfr_write_handler(struct vlapic *vlapic) 1424f8be175SNeel Natu { 1434f8be175SNeel Natu struct LAPIC *lapic; 1444f8be175SNeel Natu 145de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1464f8be175SNeel Natu if (x2apic(vlapic)) { 1473f0ddc7cSNeel Natu VM_CTR1(vlapic->vm, "ignoring write to DFR in x2apic mode: %#x", 1483f0ddc7cSNeel Natu lapic->dfr); 1493f0ddc7cSNeel Natu lapic->dfr = 0; 1504f8be175SNeel Natu return; 1514f8be175SNeel Natu } 1524f8be175SNeel Natu 1533f0ddc7cSNeel Natu lapic->dfr &= APIC_DFR_MODEL_MASK; 1543f0ddc7cSNeel Natu lapic->dfr |= APIC_DFR_RESERVED; 1553f0ddc7cSNeel Natu 1563f0ddc7cSNeel Natu if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT) 1574f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model"); 1583f0ddc7cSNeel Natu else if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER) 1594f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model"); 1604f8be175SNeel Natu else 1613f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "DFR in Unknown Model %#x", lapic->dfr); 1624f8be175SNeel Natu } 1634f8be175SNeel Natu 1643f0ddc7cSNeel Natu void 1653f0ddc7cSNeel Natu vlapic_ldr_write_handler(struct vlapic *vlapic) 1664f8be175SNeel Natu { 1674f8be175SNeel Natu struct LAPIC *lapic; 1684f8be175SNeel Natu 1693f0ddc7cSNeel Natu lapic = vlapic->apic_page; 1703f0ddc7cSNeel Natu 1714f8be175SNeel Natu /* LDR is read-only in x2apic mode */ 1724f8be175SNeel Natu if (x2apic(vlapic)) { 1733f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "ignoring write to LDR in x2apic mode: %#x", 1743f0ddc7cSNeel Natu lapic->ldr); 1753f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 1763f0ddc7cSNeel Natu } else { 1773f0ddc7cSNeel Natu lapic->ldr &= ~APIC_LDR_RESERVED; 1783f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr); 1793f0ddc7cSNeel Natu } 1804f8be175SNeel Natu } 1814f8be175SNeel Natu 1823f0ddc7cSNeel Natu void 1833f0ddc7cSNeel Natu vlapic_id_write_handler(struct vlapic *vlapic) 1843f0ddc7cSNeel Natu { 1853f0ddc7cSNeel Natu struct LAPIC *lapic; 1863f0ddc7cSNeel Natu 1873f0ddc7cSNeel Natu /* 1883f0ddc7cSNeel Natu * We don't allow the ID register to be modified so reset it back to 1893f0ddc7cSNeel Natu * its default value. 1903f0ddc7cSNeel Natu */ 191de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1923f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 1934f8be175SNeel Natu } 1944f8be175SNeel Natu 1952e25737aSNeel Natu static int 1962e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr) 1972e25737aSNeel Natu { 1982e25737aSNeel Natu switch (dcr & 0xB) { 199117e8f37SPeter Grehan case APIC_TDCR_1: 200117e8f37SPeter Grehan return (1); 2012e25737aSNeel Natu case APIC_TDCR_2: 2022e25737aSNeel Natu return (2); 2032e25737aSNeel Natu case APIC_TDCR_4: 2042e25737aSNeel Natu return (4); 2052e25737aSNeel Natu case APIC_TDCR_8: 2062e25737aSNeel Natu return (8); 2072e25737aSNeel Natu case APIC_TDCR_16: 2082e25737aSNeel Natu return (16); 2092e25737aSNeel Natu case APIC_TDCR_32: 2102e25737aSNeel Natu return (32); 2112e25737aSNeel Natu case APIC_TDCR_64: 2122e25737aSNeel Natu return (64); 2132e25737aSNeel Natu case APIC_TDCR_128: 2142e25737aSNeel Natu return (128); 2152e25737aSNeel Natu default: 2162e25737aSNeel Natu panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr); 2172e25737aSNeel Natu } 2182e25737aSNeel Natu } 2192e25737aSNeel Natu 220366f6083SPeter Grehan static void 221*2c52dcd9SNeel Natu vlapic_mask_lvts(struct vlapic *vlapic) 222366f6083SPeter Grehan { 223*2c52dcd9SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 224*2c52dcd9SNeel Natu 225*2c52dcd9SNeel Natu lapic->lvt_cmci |= APIC_LVT_M; 226*2c52dcd9SNeel Natu lapic->lvt_timer |= APIC_LVT_M; 227*2c52dcd9SNeel Natu lapic->lvt_thermal |= APIC_LVT_M; 228*2c52dcd9SNeel Natu lapic->lvt_pcint |= APIC_LVT_M; 229*2c52dcd9SNeel Natu lapic->lvt_lint0 |= APIC_LVT_M; 230*2c52dcd9SNeel Natu lapic->lvt_lint1 |= APIC_LVT_M; 231*2c52dcd9SNeel Natu lapic->lvt_error |= APIC_LVT_M; 232366f6083SPeter Grehan } 233366f6083SPeter Grehan 234366f6083SPeter Grehan #if 0 235366f6083SPeter Grehan static inline void 236366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) 237366f6083SPeter Grehan { 238366f6083SPeter Grehan printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset, 239366f6083SPeter Grehan *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS, 240366f6083SPeter Grehan *lvt & APIC_LVTT_M); 241366f6083SPeter Grehan } 242366f6083SPeter Grehan #endif 243366f6083SPeter Grehan 244fb03ca4eSNeel Natu static uint32_t 245366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic) 246366f6083SPeter Grehan { 247fb03ca4eSNeel Natu struct bintime bt_now, bt_rem; 248fb03ca4eSNeel Natu struct LAPIC *lapic; 249fb03ca4eSNeel Natu uint32_t ccr; 250fb03ca4eSNeel Natu 251fb03ca4eSNeel Natu ccr = 0; 252de5ea6b6SNeel Natu lapic = vlapic->apic_page; 253fb03ca4eSNeel Natu 254fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 255fb03ca4eSNeel Natu if (callout_active(&vlapic->callout)) { 256fb03ca4eSNeel Natu /* 257fb03ca4eSNeel Natu * If the timer is scheduled to expire in the future then 258fb03ca4eSNeel Natu * compute the value of 'ccr' based on the remaining time. 259fb03ca4eSNeel Natu */ 260fb03ca4eSNeel Natu binuptime(&bt_now); 261fb03ca4eSNeel Natu if (bintime_cmp(&vlapic->timer_fire_bt, &bt_now, >)) { 262fb03ca4eSNeel Natu bt_rem = vlapic->timer_fire_bt; 263fb03ca4eSNeel Natu bintime_sub(&bt_rem, &bt_now); 264fb03ca4eSNeel Natu ccr += bt_rem.sec * BT2FREQ(&vlapic->timer_freq_bt); 265fb03ca4eSNeel Natu ccr += bt_rem.frac / vlapic->timer_freq_bt.frac; 266fb03ca4eSNeel Natu } 267fb03ca4eSNeel Natu } 268fb03ca4eSNeel Natu KASSERT(ccr <= lapic->icr_timer, ("vlapic_get_ccr: invalid ccr %#x, " 269fb03ca4eSNeel Natu "icr_timer is %#x", ccr, lapic->icr_timer)); 270fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic ccr_timer = %#x, icr_timer = %#x", 271fb03ca4eSNeel Natu ccr, lapic->icr_timer); 272fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 273fb03ca4eSNeel Natu return (ccr); 274fb03ca4eSNeel Natu } 275fb03ca4eSNeel Natu 276fb03ca4eSNeel Natu static void 277fb03ca4eSNeel Natu vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr) 278fb03ca4eSNeel Natu { 279fb03ca4eSNeel Natu struct LAPIC *lapic; 280fb03ca4eSNeel Natu int divisor; 281fb03ca4eSNeel Natu 282de5ea6b6SNeel Natu lapic = vlapic->apic_page; 283fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 284fb03ca4eSNeel Natu 285fb03ca4eSNeel Natu lapic->dcr_timer = dcr; 286fb03ca4eSNeel Natu divisor = vlapic_timer_divisor(dcr); 287fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", dcr, divisor); 288fb03ca4eSNeel Natu 289fb03ca4eSNeel Natu /* 290fb03ca4eSNeel Natu * Update the timer frequency and the timer period. 291fb03ca4eSNeel Natu * 292fb03ca4eSNeel Natu * XXX changes to the frequency divider will not take effect until 293fb03ca4eSNeel Natu * the timer is reloaded. 294fb03ca4eSNeel Natu */ 295fb03ca4eSNeel Natu FREQ2BT(VLAPIC_BUS_FREQ / divisor, &vlapic->timer_freq_bt); 296fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 297fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 298fb03ca4eSNeel Natu 299fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 300366f6083SPeter Grehan } 301366f6083SPeter Grehan 302366f6083SPeter Grehan static void 303366f6083SPeter Grehan vlapic_update_errors(struct vlapic *vlapic) 304366f6083SPeter Grehan { 305de5ea6b6SNeel Natu struct LAPIC *lapic; 306de5ea6b6SNeel Natu 307de5ea6b6SNeel Natu lapic = vlapic->apic_page; 308330baf58SJohn Baldwin lapic->esr = vlapic->esr_pending; 309330baf58SJohn Baldwin vlapic->esr_pending = 0; 310366f6083SPeter Grehan } 311366f6083SPeter Grehan 312366f6083SPeter Grehan static void 313a7835785SNeel Natu vlapic_reset(struct vlapic *vlapic) 314366f6083SPeter Grehan { 315a7835785SNeel Natu struct LAPIC *lapic; 316a7835785SNeel Natu 317de5ea6b6SNeel Natu lapic = vlapic->apic_page; 318a7835785SNeel Natu bzero(lapic, sizeof(struct LAPIC)); 319a7835785SNeel Natu 3203f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 321366f6083SPeter Grehan lapic->version = VLAPIC_VERSION; 322a83011d2SNeel Natu lapic->version |= (VLAPIC_MAXLVT_ENTRIES << MAXLVTSHIFT); 323366f6083SPeter Grehan lapic->dfr = 0xffffffff; 324366f6083SPeter Grehan lapic->svr = APIC_SVR_VECTOR; 325*2c52dcd9SNeel Natu vlapic_mask_lvts(vlapic); 326fb03ca4eSNeel Natu vlapic_set_dcr(vlapic, 0); 327366f6083SPeter Grehan 328edf89256SNeel Natu if (vlapic->vcpuid == 0) 329edf89256SNeel Natu vlapic->boot_state = BS_RUNNING; /* BSP */ 330edf89256SNeel Natu else 331edf89256SNeel Natu vlapic->boot_state = BS_INIT; /* AP */ 332*2c52dcd9SNeel Natu 333*2c52dcd9SNeel Natu vlapic->svr_last = lapic->svr; 334366f6083SPeter Grehan } 335366f6083SPeter Grehan 336366f6083SPeter Grehan void 337b5b28fc9SNeel Natu vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) 338366f6083SPeter Grehan { 339de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 340b5b28fc9SNeel Natu uint32_t *irrptr, *tmrptr, mask; 341366f6083SPeter Grehan int idx; 342366f6083SPeter Grehan 343366f6083SPeter Grehan if (vector < 0 || vector >= 256) 344366f6083SPeter Grehan panic("vlapic_set_intr_ready: invalid vector %d\n", vector); 345366f6083SPeter Grehan 3461c052192SNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) { 3471c052192SNeel Natu VLAPIC_CTR1(vlapic, "vlapic is software disabled, ignoring " 3481c052192SNeel Natu "interrupt %d", vector); 3491c052192SNeel Natu return; 3501c052192SNeel Natu } 3511c052192SNeel Natu 352330baf58SJohn Baldwin if (vector < 16) { 353330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR); 354330baf58SJohn Baldwin return; 355330baf58SJohn Baldwin } 356330baf58SJohn Baldwin 357366f6083SPeter Grehan idx = (vector / 32) * 4; 358b5b28fc9SNeel Natu mask = 1 << (vector % 32); 359b5b28fc9SNeel Natu 360366f6083SPeter Grehan irrptr = &lapic->irr0; 361b5b28fc9SNeel Natu atomic_set_int(&irrptr[idx], mask); 362b5b28fc9SNeel Natu 363b5b28fc9SNeel Natu /* 364b5b28fc9SNeel Natu * Upon acceptance of an interrupt into the IRR the corresponding 365b5b28fc9SNeel Natu * TMR bit is cleared for edge-triggered interrupts and set for 366b5b28fc9SNeel Natu * level-triggered interrupts. 367b5b28fc9SNeel Natu */ 368b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 369b5b28fc9SNeel Natu if (level) 370b5b28fc9SNeel Natu atomic_set_int(&tmrptr[idx], mask); 371b5b28fc9SNeel Natu else 372b5b28fc9SNeel Natu atomic_clear_int(&tmrptr[idx], mask); 373b5b28fc9SNeel Natu 374366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready"); 375366f6083SPeter Grehan } 376366f6083SPeter Grehan 377366f6083SPeter Grehan static __inline uint32_t * 378fb03ca4eSNeel Natu vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) 379366f6083SPeter Grehan { 380de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 381366f6083SPeter Grehan int i; 382366f6083SPeter Grehan 383330baf58SJohn Baldwin switch (offset) { 384330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 385330baf58SJohn Baldwin return (&lapic->lvt_cmci); 386330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 387366f6083SPeter Grehan i = (offset - APIC_OFFSET_TIMER_LVT) >> 2; 388366f6083SPeter Grehan return ((&lapic->lvt_timer) + i);; 389330baf58SJohn Baldwin default: 390330baf58SJohn Baldwin panic("vlapic_get_lvt: invalid LVT\n"); 391330baf58SJohn Baldwin } 392366f6083SPeter Grehan } 393366f6083SPeter Grehan 394fb03ca4eSNeel Natu static __inline uint32_t 395fb03ca4eSNeel Natu vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) 396fb03ca4eSNeel Natu { 397fb03ca4eSNeel Natu 398fb03ca4eSNeel Natu return (*vlapic_get_lvtptr(vlapic, offset)); 399fb03ca4eSNeel Natu } 400fb03ca4eSNeel Natu 401fb03ca4eSNeel Natu static void 402fb03ca4eSNeel Natu vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val) 403fb03ca4eSNeel Natu { 404330baf58SJohn Baldwin uint32_t *lvtptr, mask; 405fb03ca4eSNeel Natu struct LAPIC *lapic; 406fb03ca4eSNeel Natu 407de5ea6b6SNeel Natu lapic = vlapic->apic_page; 408fb03ca4eSNeel Natu lvtptr = vlapic_get_lvtptr(vlapic, offset); 409fb03ca4eSNeel Natu 410fb03ca4eSNeel Natu if (offset == APIC_OFFSET_TIMER_LVT) 411fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 412fb03ca4eSNeel Natu 413fb03ca4eSNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) 414fb03ca4eSNeel Natu val |= APIC_LVT_M; 415330baf58SJohn Baldwin mask = APIC_LVT_M | APIC_LVT_DS | APIC_LVT_VECTOR; 416330baf58SJohn Baldwin switch (offset) { 417330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT: 418330baf58SJohn Baldwin mask |= APIC_LVTT_TM; 419330baf58SJohn Baldwin break; 420330baf58SJohn Baldwin case APIC_OFFSET_ERROR_LVT: 421330baf58SJohn Baldwin break; 422330baf58SJohn Baldwin case APIC_OFFSET_LINT0_LVT: 423330baf58SJohn Baldwin case APIC_OFFSET_LINT1_LVT: 424330baf58SJohn Baldwin mask |= APIC_LVT_TM | APIC_LVT_RIRR | APIC_LVT_IIPP; 425330baf58SJohn Baldwin /* FALLTHROUGH */ 426330baf58SJohn Baldwin default: 427330baf58SJohn Baldwin mask |= APIC_LVT_DM; 428330baf58SJohn Baldwin break; 429330baf58SJohn Baldwin } 430330baf58SJohn Baldwin *lvtptr = val & mask; 431fb03ca4eSNeel Natu 432fb03ca4eSNeel Natu if (offset == APIC_OFFSET_TIMER_LVT) 433fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 434fb03ca4eSNeel Natu } 435fb03ca4eSNeel Natu 436330baf58SJohn Baldwin static int 437330baf58SJohn Baldwin vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt) 438330baf58SJohn Baldwin { 439330baf58SJohn Baldwin uint32_t vec, mode; 440330baf58SJohn Baldwin 441330baf58SJohn Baldwin if (lvt & APIC_LVT_M) 442330baf58SJohn Baldwin return (0); 443330baf58SJohn Baldwin 444330baf58SJohn Baldwin vec = lvt & APIC_LVT_VECTOR; 445330baf58SJohn Baldwin mode = lvt & APIC_LVT_DM; 446330baf58SJohn Baldwin 447330baf58SJohn Baldwin switch (mode) { 448330baf58SJohn Baldwin case APIC_LVT_DM_FIXED: 449330baf58SJohn Baldwin if (vec < 16) { 450330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 451330baf58SJohn Baldwin return (0); 452330baf58SJohn Baldwin } 453330baf58SJohn Baldwin vlapic_set_intr_ready(vlapic, vec, false); 454de5ea6b6SNeel Natu vcpu_notify_event(vlapic->vm, vlapic->vcpuid, true); 455330baf58SJohn Baldwin break; 456330baf58SJohn Baldwin case APIC_LVT_DM_NMI: 457330baf58SJohn Baldwin vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 458330baf58SJohn Baldwin break; 459330baf58SJohn Baldwin default: 460330baf58SJohn Baldwin // Other modes ignored 461330baf58SJohn Baldwin return (0); 462330baf58SJohn Baldwin } 463330baf58SJohn Baldwin return (1); 464330baf58SJohn Baldwin } 465330baf58SJohn Baldwin 466366f6083SPeter Grehan #if 1 467366f6083SPeter Grehan static void 468366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic) 469366f6083SPeter Grehan { 470366f6083SPeter Grehan int i; 471366f6083SPeter Grehan uint32_t *isrptr; 472366f6083SPeter Grehan 473de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 474366f6083SPeter Grehan for (i = 0; i < 8; i++) 475366f6083SPeter Grehan printf("ISR%d 0x%08x\n", i, isrptr[i * 4]); 476366f6083SPeter Grehan 477366f6083SPeter Grehan for (i = 0; i <= vlapic->isrvec_stk_top; i++) 478366f6083SPeter Grehan printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]); 479366f6083SPeter Grehan } 480366f6083SPeter Grehan #endif 481366f6083SPeter Grehan 482366f6083SPeter Grehan /* 483366f6083SPeter Grehan * Algorithm adopted from section "Interrupt, Task and Processor Priority" 484366f6083SPeter Grehan * in Intel Architecture Manual Vol 3a. 485366f6083SPeter Grehan */ 486366f6083SPeter Grehan static void 487366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic) 488366f6083SPeter Grehan { 489366f6083SPeter Grehan int isrvec, tpr, ppr; 490366f6083SPeter Grehan 491366f6083SPeter Grehan /* 492366f6083SPeter Grehan * Note that the value on the stack at index 0 is always 0. 493366f6083SPeter Grehan * 494366f6083SPeter Grehan * This is a placeholder for the value of ISRV when none of the 495366f6083SPeter Grehan * bits is set in the ISRx registers. 496366f6083SPeter Grehan */ 497366f6083SPeter Grehan isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top]; 498de5ea6b6SNeel Natu tpr = vlapic->apic_page->tpr; 499366f6083SPeter Grehan 500366f6083SPeter Grehan #if 1 501366f6083SPeter Grehan { 502366f6083SPeter Grehan int i, lastprio, curprio, vector, idx; 503366f6083SPeter Grehan uint32_t *isrptr; 504366f6083SPeter Grehan 505366f6083SPeter Grehan if (vlapic->isrvec_stk_top == 0 && isrvec != 0) 506366f6083SPeter Grehan panic("isrvec_stk is corrupted: %d", isrvec); 507366f6083SPeter Grehan 508366f6083SPeter Grehan /* 509366f6083SPeter Grehan * Make sure that the priority of the nested interrupts is 510366f6083SPeter Grehan * always increasing. 511366f6083SPeter Grehan */ 512366f6083SPeter Grehan lastprio = -1; 513366f6083SPeter Grehan for (i = 1; i <= vlapic->isrvec_stk_top; i++) { 514366f6083SPeter Grehan curprio = PRIO(vlapic->isrvec_stk[i]); 515366f6083SPeter Grehan if (curprio <= lastprio) { 516366f6083SPeter Grehan dump_isrvec_stk(vlapic); 517366f6083SPeter Grehan panic("isrvec_stk does not satisfy invariant"); 518366f6083SPeter Grehan } 519366f6083SPeter Grehan lastprio = curprio; 520366f6083SPeter Grehan } 521366f6083SPeter Grehan 522366f6083SPeter Grehan /* 523366f6083SPeter Grehan * Make sure that each bit set in the ISRx registers has a 524366f6083SPeter Grehan * corresponding entry on the isrvec stack. 525366f6083SPeter Grehan */ 526366f6083SPeter Grehan i = 1; 527de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 528366f6083SPeter Grehan for (vector = 0; vector < 256; vector++) { 529366f6083SPeter Grehan idx = (vector / 32) * 4; 530366f6083SPeter Grehan if (isrptr[idx] & (1 << (vector % 32))) { 531366f6083SPeter Grehan if (i > vlapic->isrvec_stk_top || 532366f6083SPeter Grehan vlapic->isrvec_stk[i] != vector) { 533366f6083SPeter Grehan dump_isrvec_stk(vlapic); 534366f6083SPeter Grehan panic("ISR and isrvec_stk out of sync"); 535366f6083SPeter Grehan } 536366f6083SPeter Grehan i++; 537366f6083SPeter Grehan } 538366f6083SPeter Grehan } 539366f6083SPeter Grehan } 540366f6083SPeter Grehan #endif 541366f6083SPeter Grehan 542366f6083SPeter Grehan if (PRIO(tpr) >= PRIO(isrvec)) 543366f6083SPeter Grehan ppr = tpr; 544366f6083SPeter Grehan else 545366f6083SPeter Grehan ppr = isrvec & 0xf0; 546366f6083SPeter Grehan 547de5ea6b6SNeel Natu vlapic->apic_page->ppr = ppr; 548366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr); 549366f6083SPeter Grehan } 550366f6083SPeter Grehan 551366f6083SPeter Grehan static void 552366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic) 553366f6083SPeter Grehan { 554de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 555b5b28fc9SNeel Natu uint32_t *isrptr, *tmrptr; 556b5b28fc9SNeel Natu int i, idx, bitpos, vector; 557366f6083SPeter Grehan 558366f6083SPeter Grehan isrptr = &lapic->isr0; 559b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 560366f6083SPeter Grehan 561366f6083SPeter Grehan /* 562366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 563366f6083SPeter Grehan * by the processor. 564366f6083SPeter Grehan */ 565366f6083SPeter Grehan for (i = 7; i > 0; i--) { 566366f6083SPeter Grehan idx = i * 4; 567366f6083SPeter Grehan bitpos = fls(isrptr[idx]); 568b5b28fc9SNeel Natu if (bitpos-- != 0) { 569366f6083SPeter Grehan if (vlapic->isrvec_stk_top <= 0) { 570366f6083SPeter Grehan panic("invalid vlapic isrvec_stk_top %d", 571366f6083SPeter Grehan vlapic->isrvec_stk_top); 572366f6083SPeter Grehan } 573b5b28fc9SNeel Natu isrptr[idx] &= ~(1 << bitpos); 574366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi"); 575366f6083SPeter Grehan vlapic->isrvec_stk_top--; 576366f6083SPeter Grehan vlapic_update_ppr(vlapic); 577b5b28fc9SNeel Natu if ((tmrptr[idx] & (1 << bitpos)) != 0) { 578b5b28fc9SNeel Natu vector = i * 32 + bitpos; 579b5b28fc9SNeel Natu vioapic_process_eoi(vlapic->vm, vlapic->vcpuid, 580b5b28fc9SNeel Natu vector); 581b5b28fc9SNeel Natu } 582366f6083SPeter Grehan return; 583366f6083SPeter Grehan } 584366f6083SPeter Grehan } 585366f6083SPeter Grehan } 586366f6083SPeter Grehan 587366f6083SPeter Grehan static __inline int 588fb03ca4eSNeel Natu vlapic_get_lvt_field(uint32_t lvt, uint32_t mask) 589366f6083SPeter Grehan { 590fb03ca4eSNeel Natu 591fb03ca4eSNeel Natu return (lvt & mask); 592366f6083SPeter Grehan } 593366f6083SPeter Grehan 594366f6083SPeter Grehan static __inline int 595366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic) 596366f6083SPeter Grehan { 597fb03ca4eSNeel Natu uint32_t lvt; 598366f6083SPeter Grehan 599366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 600366f6083SPeter Grehan 601366f6083SPeter Grehan return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC)); 602366f6083SPeter Grehan } 603366f6083SPeter Grehan 604330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_ERROR, "error interrupts generated by vlapic"); 605330baf58SJohn Baldwin 606330baf58SJohn Baldwin void 607330baf58SJohn Baldwin vlapic_set_error(struct vlapic *vlapic, uint32_t mask) 608330baf58SJohn Baldwin { 609330baf58SJohn Baldwin uint32_t lvt; 610330baf58SJohn Baldwin 611330baf58SJohn Baldwin vlapic->esr_pending |= mask; 612330baf58SJohn Baldwin if (vlapic->esr_firing) 613330baf58SJohn Baldwin return; 614330baf58SJohn Baldwin vlapic->esr_firing = 1; 615330baf58SJohn Baldwin 616330baf58SJohn Baldwin // The error LVT always uses the fixed delivery mode. 617330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT); 618330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) { 619330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_ERROR, 1); 620330baf58SJohn Baldwin } 621330baf58SJohn Baldwin vlapic->esr_firing = 0; 622330baf58SJohn Baldwin } 623330baf58SJohn Baldwin 62477d8fd9bSNeel Natu static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic"); 62577d8fd9bSNeel Natu 626366f6083SPeter Grehan static void 627366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic) 628366f6083SPeter Grehan { 629fb03ca4eSNeel Natu uint32_t lvt; 630fb03ca4eSNeel Natu 631fb03ca4eSNeel Natu KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked")); 632366f6083SPeter Grehan 633330baf58SJohn Baldwin // The timer LVT always uses the fixed delivery mode. 634366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 635330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) { 63677d8fd9bSNeel Natu vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1); 637366f6083SPeter Grehan } 638366f6083SPeter Grehan } 639366f6083SPeter Grehan 640330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_CMC, 641330baf58SJohn Baldwin "corrected machine check interrupts generated by vlapic"); 642330baf58SJohn Baldwin 643330baf58SJohn Baldwin void 644330baf58SJohn Baldwin vlapic_fire_cmci(struct vlapic *vlapic) 645330baf58SJohn Baldwin { 646330baf58SJohn Baldwin uint32_t lvt; 647330baf58SJohn Baldwin 648330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT); 649330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt)) { 650330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_CMC, 1); 651330baf58SJohn Baldwin } 652330baf58SJohn Baldwin } 653330baf58SJohn Baldwin 654330baf58SJohn Baldwin static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_ENTRIES, 655330baf58SJohn Baldwin "lvts triggered"); 656330baf58SJohn Baldwin 657330baf58SJohn Baldwin int 658330baf58SJohn Baldwin vlapic_trigger_lvt(struct vlapic *vlapic, int vector) 659330baf58SJohn Baldwin { 660330baf58SJohn Baldwin uint32_t lvt; 661330baf58SJohn Baldwin 662330baf58SJohn Baldwin switch (vector) { 663330baf58SJohn Baldwin case APIC_LVT_LINT0: 664330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT); 665330baf58SJohn Baldwin break; 666330baf58SJohn Baldwin case APIC_LVT_LINT1: 667330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT1_LVT); 668330baf58SJohn Baldwin break; 669330baf58SJohn Baldwin case APIC_LVT_TIMER: 670330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 671330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 672330baf58SJohn Baldwin break; 673330baf58SJohn Baldwin case APIC_LVT_ERROR: 674330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT); 675330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 676330baf58SJohn Baldwin break; 677330baf58SJohn Baldwin case APIC_LVT_PMC: 678330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_PERF_LVT); 679330baf58SJohn Baldwin break; 680330baf58SJohn Baldwin case APIC_LVT_THERMAL: 681330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_THERM_LVT); 682330baf58SJohn Baldwin break; 683330baf58SJohn Baldwin case APIC_LVT_CMCI: 684330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT); 685330baf58SJohn Baldwin break; 686330baf58SJohn Baldwin default: 687330baf58SJohn Baldwin return (EINVAL); 688330baf58SJohn Baldwin } 689330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt)) { 690330baf58SJohn Baldwin vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 691330baf58SJohn Baldwin LVTS_TRIGGERRED, vector, 1); 692330baf58SJohn Baldwin } 693330baf58SJohn Baldwin return (0); 694330baf58SJohn Baldwin } 695330baf58SJohn Baldwin 696fb03ca4eSNeel Natu static void 697fb03ca4eSNeel Natu vlapic_callout_handler(void *arg) 698fb03ca4eSNeel Natu { 699fb03ca4eSNeel Natu struct vlapic *vlapic; 700fb03ca4eSNeel Natu struct bintime bt, btnow; 701fb03ca4eSNeel Natu sbintime_t rem_sbt; 702fb03ca4eSNeel Natu 703fb03ca4eSNeel Natu vlapic = arg; 704fb03ca4eSNeel Natu 705fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 706fb03ca4eSNeel Natu if (callout_pending(&vlapic->callout)) /* callout was reset */ 707fb03ca4eSNeel Natu goto done; 708fb03ca4eSNeel Natu 709fb03ca4eSNeel Natu if (!callout_active(&vlapic->callout)) /* callout was stopped */ 710fb03ca4eSNeel Natu goto done; 711fb03ca4eSNeel Natu 712fb03ca4eSNeel Natu callout_deactivate(&vlapic->callout); 713fb03ca4eSNeel Natu 714de5ea6b6SNeel Natu KASSERT(vlapic->apic_page->icr_timer != 0, ("timer is disabled")); 715fb03ca4eSNeel Natu 716fb03ca4eSNeel Natu vlapic_fire_timer(vlapic); 717fb03ca4eSNeel Natu 718fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) { 719fb03ca4eSNeel Natu binuptime(&btnow); 720fb03ca4eSNeel Natu KASSERT(bintime_cmp(&btnow, &vlapic->timer_fire_bt, >=), 721fb03ca4eSNeel Natu ("vlapic callout at %#lx.%#lx, expected at %#lx.#%lx", 722fb03ca4eSNeel Natu btnow.sec, btnow.frac, vlapic->timer_fire_bt.sec, 723fb03ca4eSNeel Natu vlapic->timer_fire_bt.frac)); 724fb03ca4eSNeel Natu 725fb03ca4eSNeel Natu /* 726fb03ca4eSNeel Natu * Compute the delta between when the timer was supposed to 727fb03ca4eSNeel Natu * fire and the present time. 728fb03ca4eSNeel Natu */ 729fb03ca4eSNeel Natu bt = btnow; 730fb03ca4eSNeel Natu bintime_sub(&bt, &vlapic->timer_fire_bt); 731fb03ca4eSNeel Natu 732fb03ca4eSNeel Natu rem_sbt = bttosbt(vlapic->timer_period_bt); 733fb03ca4eSNeel Natu if (bintime_cmp(&bt, &vlapic->timer_period_bt, <)) { 734fb03ca4eSNeel Natu /* 735fb03ca4eSNeel Natu * Adjust the time until the next countdown downward 736fb03ca4eSNeel Natu * to account for the lost time. 737fb03ca4eSNeel Natu */ 738fb03ca4eSNeel Natu rem_sbt -= bttosbt(bt); 739fb03ca4eSNeel Natu } else { 740fb03ca4eSNeel Natu /* 741fb03ca4eSNeel Natu * If the delta is greater than the timer period then 742fb03ca4eSNeel Natu * just reset our time base instead of trying to catch 743fb03ca4eSNeel Natu * up. 744fb03ca4eSNeel Natu */ 745fb03ca4eSNeel Natu vlapic->timer_fire_bt = btnow; 746fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic timer lagging by %lu " 747fb03ca4eSNeel Natu "usecs, period is %lu usecs - resetting time base", 748fb03ca4eSNeel Natu bttosbt(bt) / SBT_1US, 749fb03ca4eSNeel Natu bttosbt(vlapic->timer_period_bt) / SBT_1US); 750fb03ca4eSNeel Natu } 751fb03ca4eSNeel Natu 752fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 753fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, rem_sbt, 0, 754fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 755fb03ca4eSNeel Natu } 756fb03ca4eSNeel Natu done: 757fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 758fb03ca4eSNeel Natu } 759fb03ca4eSNeel Natu 760fb03ca4eSNeel Natu static void 761fb03ca4eSNeel Natu vlapic_set_icr_timer(struct vlapic *vlapic, uint32_t icr_timer) 762fb03ca4eSNeel Natu { 763fb03ca4eSNeel Natu struct LAPIC *lapic; 764fb03ca4eSNeel Natu sbintime_t sbt; 765fb03ca4eSNeel Natu 766fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 767fb03ca4eSNeel Natu 768de5ea6b6SNeel Natu lapic = vlapic->apic_page; 769fb03ca4eSNeel Natu lapic->icr_timer = icr_timer; 770fb03ca4eSNeel Natu 771fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 772fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, icr_timer); 773fb03ca4eSNeel Natu 774fb03ca4eSNeel Natu if (icr_timer != 0) { 775fb03ca4eSNeel Natu binuptime(&vlapic->timer_fire_bt); 776fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 777fb03ca4eSNeel Natu 778fb03ca4eSNeel Natu sbt = bttosbt(vlapic->timer_period_bt); 779fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, sbt, 0, 780fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 781fb03ca4eSNeel Natu } else 782fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 783fb03ca4eSNeel Natu 784fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 785fb03ca4eSNeel Natu } 786fb03ca4eSNeel Natu 7874f8be175SNeel Natu /* 7884f8be175SNeel Natu * This function populates 'dmask' with the set of vcpus that match the 7894f8be175SNeel Natu * addressing specified by the (dest, phys, lowprio) tuple. 7904f8be175SNeel Natu * 7914f8be175SNeel Natu * 'x2apic_dest' specifies whether 'dest' is interpreted as x2APIC (32-bit) 7924f8be175SNeel Natu * or xAPIC (8-bit) destination field. 7934f8be175SNeel Natu */ 7944f8be175SNeel Natu static void 7954f8be175SNeel Natu vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys, 7964f8be175SNeel Natu bool lowprio, bool x2apic_dest) 7974f8be175SNeel Natu { 7984f8be175SNeel Natu struct vlapic *vlapic; 7994f8be175SNeel Natu uint32_t dfr, ldr, ldest, cluster; 8004f8be175SNeel Natu uint32_t mda_flat_ldest, mda_cluster_ldest, mda_ldest, mda_cluster_id; 8014f8be175SNeel Natu cpuset_t amask; 8024f8be175SNeel Natu int vcpuid; 8034f8be175SNeel Natu 8044f8be175SNeel Natu if ((x2apic_dest && dest == 0xffffffff) || 8054f8be175SNeel Natu (!x2apic_dest && dest == 0xff)) { 8064f8be175SNeel Natu /* 8074f8be175SNeel Natu * Broadcast in both logical and physical modes. 8084f8be175SNeel Natu */ 8094f8be175SNeel Natu *dmask = vm_active_cpus(vm); 8104f8be175SNeel Natu return; 8114f8be175SNeel Natu } 8124f8be175SNeel Natu 8134f8be175SNeel Natu if (phys) { 8144f8be175SNeel Natu /* 8154f8be175SNeel Natu * Physical mode: destination is APIC ID. 8164f8be175SNeel Natu */ 8174f8be175SNeel Natu CPU_ZERO(dmask); 8184f8be175SNeel Natu vcpuid = vm_apicid2vcpuid(vm, dest); 8194f8be175SNeel Natu if (vcpuid < VM_MAXCPU) 8204f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8214f8be175SNeel Natu } else { 8224f8be175SNeel Natu /* 8234f8be175SNeel Natu * In the "Flat Model" the MDA is interpreted as an 8-bit wide 8244f8be175SNeel Natu * bitmask. This model is only avilable in the xAPIC mode. 8254f8be175SNeel Natu */ 8264f8be175SNeel Natu mda_flat_ldest = dest & 0xff; 8274f8be175SNeel Natu 8284f8be175SNeel Natu /* 8294f8be175SNeel Natu * In the "Cluster Model" the MDA is used to identify a 8304f8be175SNeel Natu * specific cluster and a set of APICs in that cluster. 8314f8be175SNeel Natu */ 8324f8be175SNeel Natu if (x2apic_dest) { 8334f8be175SNeel Natu mda_cluster_id = dest >> 16; 8344f8be175SNeel Natu mda_cluster_ldest = dest & 0xffff; 8354f8be175SNeel Natu } else { 8364f8be175SNeel Natu mda_cluster_id = (dest >> 4) & 0xf; 8374f8be175SNeel Natu mda_cluster_ldest = dest & 0xf; 8384f8be175SNeel Natu } 8394f8be175SNeel Natu 8404f8be175SNeel Natu /* 8414f8be175SNeel Natu * Logical mode: match each APIC that has a bit set 8424f8be175SNeel Natu * in it's LDR that matches a bit in the ldest. 8434f8be175SNeel Natu */ 8444f8be175SNeel Natu CPU_ZERO(dmask); 8454f8be175SNeel Natu amask = vm_active_cpus(vm); 8464f8be175SNeel Natu while ((vcpuid = CPU_FFS(&amask)) != 0) { 8474f8be175SNeel Natu vcpuid--; 8484f8be175SNeel Natu CPU_CLR(vcpuid, &amask); 8494f8be175SNeel Natu 8504f8be175SNeel Natu vlapic = vm_lapic(vm, vcpuid); 8513f0ddc7cSNeel Natu dfr = vlapic->apic_page->dfr; 8523f0ddc7cSNeel Natu ldr = vlapic->apic_page->ldr; 8534f8be175SNeel Natu 8544f8be175SNeel Natu if ((dfr & APIC_DFR_MODEL_MASK) == 8554f8be175SNeel Natu APIC_DFR_MODEL_FLAT) { 8564f8be175SNeel Natu ldest = ldr >> 24; 8574f8be175SNeel Natu mda_ldest = mda_flat_ldest; 8584f8be175SNeel Natu } else if ((dfr & APIC_DFR_MODEL_MASK) == 8594f8be175SNeel Natu APIC_DFR_MODEL_CLUSTER) { 8604f8be175SNeel Natu if (x2apic(vlapic)) { 8614f8be175SNeel Natu cluster = ldr >> 16; 8624f8be175SNeel Natu ldest = ldr & 0xffff; 8634f8be175SNeel Natu } else { 8644f8be175SNeel Natu cluster = ldr >> 28; 8654f8be175SNeel Natu ldest = (ldr >> 24) & 0xf; 8664f8be175SNeel Natu } 8674f8be175SNeel Natu if (cluster != mda_cluster_id) 8684f8be175SNeel Natu continue; 8694f8be175SNeel Natu mda_ldest = mda_cluster_ldest; 8704f8be175SNeel Natu } else { 8714f8be175SNeel Natu /* 8724f8be175SNeel Natu * Guest has configured a bad logical 8734f8be175SNeel Natu * model for this vcpu - skip it. 8744f8be175SNeel Natu */ 8754f8be175SNeel Natu VLAPIC_CTR1(vlapic, "vlapic has bad logical " 8764f8be175SNeel Natu "model %x - cannot deliver interrupt", dfr); 8774f8be175SNeel Natu continue; 8784f8be175SNeel Natu } 8794f8be175SNeel Natu 8804f8be175SNeel Natu if ((mda_ldest & ldest) != 0) { 8814f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8824f8be175SNeel Natu if (lowprio) 8834f8be175SNeel Natu break; 8844f8be175SNeel Natu } 8854f8be175SNeel Natu } 8864f8be175SNeel Natu } 8874f8be175SNeel Natu } 8884f8be175SNeel Natu 8890acb0d84SNeel Natu static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu"); 8900acb0d84SNeel Natu 891366f6083SPeter Grehan static int 892becd9849SNeel Natu lapic_process_icr(struct vlapic *vlapic, uint64_t icrval, bool *retu) 893366f6083SPeter Grehan { 894366f6083SPeter Grehan int i; 8954f8be175SNeel Natu bool phys; 896a5615c90SPeter Grehan cpuset_t dmask; 897366f6083SPeter Grehan uint32_t dest, vec, mode; 898edf89256SNeel Natu struct vlapic *vlapic2; 899edf89256SNeel Natu struct vm_exit *vmexit; 900366f6083SPeter Grehan 901a2da7af6SNeel Natu if (x2apic(vlapic)) 902366f6083SPeter Grehan dest = icrval >> 32; 903a2da7af6SNeel Natu else 904a2da7af6SNeel Natu dest = icrval >> (32 + 24); 905366f6083SPeter Grehan vec = icrval & APIC_VECTOR_MASK; 906366f6083SPeter Grehan mode = icrval & APIC_DELMODE_MASK; 907366f6083SPeter Grehan 908330baf58SJohn Baldwin if (mode == APIC_DELMODE_FIXED && vec < 16) { 909330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 910330baf58SJohn Baldwin return (0); 911330baf58SJohn Baldwin } 912330baf58SJohn Baldwin 913366f6083SPeter Grehan if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) { 914366f6083SPeter Grehan switch (icrval & APIC_DEST_MASK) { 915366f6083SPeter Grehan case APIC_DEST_DESTFLD: 9164f8be175SNeel Natu phys = ((icrval & APIC_DESTMODE_LOG) == 0); 9174f8be175SNeel Natu vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false, 9184f8be175SNeel Natu x2apic(vlapic)); 919366f6083SPeter Grehan break; 920366f6083SPeter Grehan case APIC_DEST_SELF: 921a5615c90SPeter Grehan CPU_SETOF(vlapic->vcpuid, &dmask); 922366f6083SPeter Grehan break; 923366f6083SPeter Grehan case APIC_DEST_ALLISELF: 924366f6083SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 925366f6083SPeter Grehan break; 926366f6083SPeter Grehan case APIC_DEST_ALLESELF: 927a5615c90SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 928a5615c90SPeter Grehan CPU_CLR(vlapic->vcpuid, &dmask); 929366f6083SPeter Grehan break; 9301e2751ddSSergey Kandaurov default: 9311e2751ddSSergey Kandaurov CPU_ZERO(&dmask); /* satisfy gcc */ 9321e2751ddSSergey Kandaurov break; 933366f6083SPeter Grehan } 934366f6083SPeter Grehan 93582f2974aSSergey Kandaurov while ((i = CPU_FFS(&dmask)) != 0) { 936a5615c90SPeter Grehan i--; 937a5615c90SPeter Grehan CPU_CLR(i, &dmask); 9380acb0d84SNeel Natu if (mode == APIC_DELMODE_FIXED) { 939b5b28fc9SNeel Natu lapic_intr_edge(vlapic->vm, i, vec); 9400acb0d84SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 9410acb0d84SNeel Natu IPIS_SENT, i, 1); 9420acb0d84SNeel Natu } else 943366f6083SPeter Grehan vm_inject_nmi(vlapic->vm, i); 944366f6083SPeter Grehan } 945366f6083SPeter Grehan 946366f6083SPeter Grehan return (0); /* handled completely in the kernel */ 947366f6083SPeter Grehan } 948366f6083SPeter Grehan 949edf89256SNeel Natu if (mode == APIC_DELMODE_INIT) { 950edf89256SNeel Natu if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) 951edf89256SNeel Natu return (0); 952edf89256SNeel Natu 953edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 954edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 955edf89256SNeel Natu 956edf89256SNeel Natu /* move from INIT to waiting-for-SIPI state */ 957edf89256SNeel Natu if (vlapic2->boot_state == BS_INIT) { 958edf89256SNeel Natu vlapic2->boot_state = BS_SIPI; 959edf89256SNeel Natu } 960edf89256SNeel Natu 961edf89256SNeel Natu return (0); 962edf89256SNeel Natu } 963edf89256SNeel Natu } 964edf89256SNeel Natu 965edf89256SNeel Natu if (mode == APIC_DELMODE_STARTUP) { 966edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 967edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 968edf89256SNeel Natu 969edf89256SNeel Natu /* 970edf89256SNeel Natu * Ignore SIPIs in any state other than wait-for-SIPI 971edf89256SNeel Natu */ 972edf89256SNeel Natu if (vlapic2->boot_state != BS_SIPI) 973edf89256SNeel Natu return (0); 974edf89256SNeel Natu 975366f6083SPeter Grehan /* 976366f6083SPeter Grehan * XXX this assumes that the startup IPI always succeeds 977366f6083SPeter Grehan */ 978edf89256SNeel Natu vlapic2->boot_state = BS_RUNNING; 979edf89256SNeel Natu vm_activate_cpu(vlapic2->vm, dest); 980edf89256SNeel Natu 981becd9849SNeel Natu *retu = true; 982becd9849SNeel Natu vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); 983becd9849SNeel Natu vmexit->exitcode = VM_EXITCODE_SPINUP_AP; 984becd9849SNeel Natu vmexit->u.spinup_ap.vcpu = dest; 985becd9849SNeel Natu vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT; 986becd9849SNeel Natu 987edf89256SNeel Natu return (0); 988edf89256SNeel Natu } 989edf89256SNeel Natu } 990366f6083SPeter Grehan 991366f6083SPeter Grehan /* 992366f6083SPeter Grehan * This will cause a return to userland. 993366f6083SPeter Grehan */ 994366f6083SPeter Grehan return (1); 995366f6083SPeter Grehan } 996366f6083SPeter Grehan 997366f6083SPeter Grehan int 998366f6083SPeter Grehan vlapic_pending_intr(struct vlapic *vlapic) 999366f6083SPeter Grehan { 1000de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1001366f6083SPeter Grehan int idx, i, bitpos, vector; 1002366f6083SPeter Grehan uint32_t *irrptr, val; 1003366f6083SPeter Grehan 1004366f6083SPeter Grehan irrptr = &lapic->irr0; 1005366f6083SPeter Grehan 1006366f6083SPeter Grehan /* 1007366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 1008366f6083SPeter Grehan * by the processor. 1009366f6083SPeter Grehan */ 1010366f6083SPeter Grehan for (i = 7; i > 0; i--) { 1011366f6083SPeter Grehan idx = i * 4; 1012366f6083SPeter Grehan val = atomic_load_acq_int(&irrptr[idx]); 1013366f6083SPeter Grehan bitpos = fls(val); 1014366f6083SPeter Grehan if (bitpos != 0) { 1015366f6083SPeter Grehan vector = i * 32 + (bitpos - 1); 1016366f6083SPeter Grehan if (PRIO(vector) > PRIO(lapic->ppr)) { 1017366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "pending intr %d", vector); 1018366f6083SPeter Grehan return (vector); 1019366f6083SPeter Grehan } else 1020366f6083SPeter Grehan break; 1021366f6083SPeter Grehan } 1022366f6083SPeter Grehan } 1023366f6083SPeter Grehan return (-1); 1024366f6083SPeter Grehan } 1025366f6083SPeter Grehan 1026366f6083SPeter Grehan void 1027366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector) 1028366f6083SPeter Grehan { 1029de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1030366f6083SPeter Grehan uint32_t *irrptr, *isrptr; 1031366f6083SPeter Grehan int idx, stk_top; 1032366f6083SPeter Grehan 1033366f6083SPeter Grehan /* 1034366f6083SPeter Grehan * clear the ready bit for vector being accepted in irr 1035366f6083SPeter Grehan * and set the vector as in service in isr. 1036366f6083SPeter Grehan */ 1037366f6083SPeter Grehan idx = (vector / 32) * 4; 1038366f6083SPeter Grehan 1039366f6083SPeter Grehan irrptr = &lapic->irr0; 1040366f6083SPeter Grehan atomic_clear_int(&irrptr[idx], 1 << (vector % 32)); 1041366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted"); 1042366f6083SPeter Grehan 1043366f6083SPeter Grehan isrptr = &lapic->isr0; 1044366f6083SPeter Grehan isrptr[idx] |= 1 << (vector % 32); 1045366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted"); 1046366f6083SPeter Grehan 1047366f6083SPeter Grehan /* 1048366f6083SPeter Grehan * Update the PPR 1049366f6083SPeter Grehan */ 1050366f6083SPeter Grehan vlapic->isrvec_stk_top++; 1051366f6083SPeter Grehan 1052366f6083SPeter Grehan stk_top = vlapic->isrvec_stk_top; 1053366f6083SPeter Grehan if (stk_top >= ISRVEC_STK_SIZE) 1054366f6083SPeter Grehan panic("isrvec_stk_top overflow %d", stk_top); 1055366f6083SPeter Grehan 1056366f6083SPeter Grehan vlapic->isrvec_stk[stk_top] = vector; 1057366f6083SPeter Grehan vlapic_update_ppr(vlapic); 1058366f6083SPeter Grehan } 1059366f6083SPeter Grehan 1060*2c52dcd9SNeel Natu void 1061*2c52dcd9SNeel Natu vlapic_svr_write_handler(struct vlapic *vlapic) 10621c052192SNeel Natu { 10631c052192SNeel Natu struct LAPIC *lapic; 1064*2c52dcd9SNeel Natu uint32_t old, new, changed; 10651c052192SNeel Natu 1066de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1067*2c52dcd9SNeel Natu 1068*2c52dcd9SNeel Natu new = lapic->svr; 1069*2c52dcd9SNeel Natu old = vlapic->svr_last; 1070*2c52dcd9SNeel Natu vlapic->svr_last = new; 1071*2c52dcd9SNeel Natu 10721c052192SNeel Natu changed = old ^ new; 10731c052192SNeel Natu if ((changed & APIC_SVR_ENABLE) != 0) { 10741c052192SNeel Natu if ((new & APIC_SVR_ENABLE) == 0) { 1075fb03ca4eSNeel Natu /* 1076*2c52dcd9SNeel Natu * The apic is now disabled so stop the apic timer 1077*2c52dcd9SNeel Natu * and mask all the LVT entries. 1078fb03ca4eSNeel Natu */ 10791c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-disabled"); 1080fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 1081fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 1082fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 1083*2c52dcd9SNeel Natu vlapic_mask_lvts(vlapic); 10841c052192SNeel Natu } else { 1085fb03ca4eSNeel Natu /* 1086fb03ca4eSNeel Natu * The apic is now enabled so restart the apic timer 1087fb03ca4eSNeel Natu * if it is configured in periodic mode. 1088fb03ca4eSNeel Natu */ 10891c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-enabled"); 1090fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) 1091fb03ca4eSNeel Natu vlapic_set_icr_timer(vlapic, lapic->icr_timer); 10921c052192SNeel Natu } 10931c052192SNeel Natu } 10941c052192SNeel Natu } 10951c052192SNeel Natu 1096366f6083SPeter Grehan int 1097becd9849SNeel Natu vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu) 1098366f6083SPeter Grehan { 1099de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1100366f6083SPeter Grehan uint32_t *reg; 1101366f6083SPeter Grehan int i; 1102366f6083SPeter Grehan 1103366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1104366f6083SPeter Grehan *data = 0; 11051c052192SNeel Natu goto done; 1106366f6083SPeter Grehan } 1107366f6083SPeter Grehan 1108366f6083SPeter Grehan offset &= ~3; 1109366f6083SPeter Grehan switch(offset) 1110366f6083SPeter Grehan { 1111366f6083SPeter Grehan case APIC_OFFSET_ID: 11123f0ddc7cSNeel Natu *data = lapic->id; 1113366f6083SPeter Grehan break; 1114366f6083SPeter Grehan case APIC_OFFSET_VER: 1115366f6083SPeter Grehan *data = lapic->version; 1116366f6083SPeter Grehan break; 1117366f6083SPeter Grehan case APIC_OFFSET_TPR: 1118366f6083SPeter Grehan *data = lapic->tpr; 1119366f6083SPeter Grehan break; 1120366f6083SPeter Grehan case APIC_OFFSET_APR: 1121366f6083SPeter Grehan *data = lapic->apr; 1122366f6083SPeter Grehan break; 1123366f6083SPeter Grehan case APIC_OFFSET_PPR: 1124366f6083SPeter Grehan *data = lapic->ppr; 1125366f6083SPeter Grehan break; 1126366f6083SPeter Grehan case APIC_OFFSET_EOI: 1127366f6083SPeter Grehan *data = lapic->eoi; 1128366f6083SPeter Grehan break; 1129366f6083SPeter Grehan case APIC_OFFSET_LDR: 11303f0ddc7cSNeel Natu *data = lapic->ldr; 1131366f6083SPeter Grehan break; 1132366f6083SPeter Grehan case APIC_OFFSET_DFR: 11333f0ddc7cSNeel Natu *data = lapic->dfr; 1134366f6083SPeter Grehan break; 1135366f6083SPeter Grehan case APIC_OFFSET_SVR: 1136366f6083SPeter Grehan *data = lapic->svr; 1137366f6083SPeter Grehan break; 1138366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1139366f6083SPeter Grehan i = (offset - APIC_OFFSET_ISR0) >> 2; 1140366f6083SPeter Grehan reg = &lapic->isr0; 1141366f6083SPeter Grehan *data = *(reg + i); 1142366f6083SPeter Grehan break; 1143366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1144366f6083SPeter Grehan i = (offset - APIC_OFFSET_TMR0) >> 2; 1145366f6083SPeter Grehan reg = &lapic->tmr0; 1146366f6083SPeter Grehan *data = *(reg + i); 1147366f6083SPeter Grehan break; 1148366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1149366f6083SPeter Grehan i = (offset - APIC_OFFSET_IRR0) >> 2; 1150366f6083SPeter Grehan reg = &lapic->irr0; 1151366f6083SPeter Grehan *data = atomic_load_acq_int(reg + i); 1152366f6083SPeter Grehan break; 1153366f6083SPeter Grehan case APIC_OFFSET_ESR: 1154366f6083SPeter Grehan *data = lapic->esr; 1155366f6083SPeter Grehan break; 1156366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1157366f6083SPeter Grehan *data = lapic->icr_lo; 1158366f6083SPeter Grehan break; 1159366f6083SPeter Grehan case APIC_OFFSET_ICR_HI: 1160366f6083SPeter Grehan *data = lapic->icr_hi; 1161366f6083SPeter Grehan break; 1162330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1163366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 1164fb03ca4eSNeel Natu *data = vlapic_get_lvt(vlapic, offset); 1165366f6083SPeter Grehan break; 1166de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1167366f6083SPeter Grehan *data = lapic->icr_timer; 1168366f6083SPeter Grehan break; 1169de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1170366f6083SPeter Grehan *data = vlapic_get_ccr(vlapic); 1171366f6083SPeter Grehan break; 1172de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1173366f6083SPeter Grehan *data = lapic->dcr_timer; 1174366f6083SPeter Grehan break; 1175366f6083SPeter Grehan case APIC_OFFSET_RRR: 1176366f6083SPeter Grehan default: 1177366f6083SPeter Grehan *data = 0; 1178366f6083SPeter Grehan break; 1179366f6083SPeter Grehan } 11801c052192SNeel Natu done: 11811c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic read offset %#x, data %#lx", offset, *data); 1182366f6083SPeter Grehan return 0; 1183366f6083SPeter Grehan } 1184366f6083SPeter Grehan 1185366f6083SPeter Grehan int 1186becd9849SNeel Natu vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) 1187366f6083SPeter Grehan { 1188de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1189366f6083SPeter Grehan int retval; 1190366f6083SPeter Grehan 11913f0ddc7cSNeel Natu KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE, 11923f0ddc7cSNeel Natu ("vlapic_write: invalid offset %#lx", offset)); 11933f0ddc7cSNeel Natu 11941c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic write offset %#x, data %#lx", offset, data); 11951c052192SNeel Natu 1196366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1197366f6083SPeter Grehan return 0; 1198366f6083SPeter Grehan } 1199366f6083SPeter Grehan 1200366f6083SPeter Grehan retval = 0; 1201366f6083SPeter Grehan switch(offset) 1202366f6083SPeter Grehan { 1203366f6083SPeter Grehan case APIC_OFFSET_ID: 12043f0ddc7cSNeel Natu lapic->id = data; 12053f0ddc7cSNeel Natu vlapic_id_write_handler(vlapic); 1206366f6083SPeter Grehan break; 1207366f6083SPeter Grehan case APIC_OFFSET_TPR: 1208366f6083SPeter Grehan lapic->tpr = data & 0xff; 1209366f6083SPeter Grehan vlapic_update_ppr(vlapic); 1210366f6083SPeter Grehan break; 1211366f6083SPeter Grehan case APIC_OFFSET_EOI: 1212366f6083SPeter Grehan vlapic_process_eoi(vlapic); 1213366f6083SPeter Grehan break; 1214366f6083SPeter Grehan case APIC_OFFSET_LDR: 12153f0ddc7cSNeel Natu lapic->ldr = data; 12163f0ddc7cSNeel Natu vlapic_ldr_write_handler(vlapic); 1217366f6083SPeter Grehan break; 1218366f6083SPeter Grehan case APIC_OFFSET_DFR: 12193f0ddc7cSNeel Natu lapic->dfr = data; 12203f0ddc7cSNeel Natu vlapic_dfr_write_handler(vlapic); 1221366f6083SPeter Grehan break; 1222366f6083SPeter Grehan case APIC_OFFSET_SVR: 1223*2c52dcd9SNeel Natu lapic->svr = data; 1224*2c52dcd9SNeel Natu vlapic_svr_write_handler(vlapic); 1225366f6083SPeter Grehan break; 1226366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1227a2da7af6SNeel Natu if (!x2apic(vlapic)) { 1228a2da7af6SNeel Natu data &= 0xffffffff; 1229a2da7af6SNeel Natu data |= (uint64_t)lapic->icr_hi << 32; 1230a2da7af6SNeel Natu } 1231becd9849SNeel Natu retval = lapic_process_icr(vlapic, data, retu); 1232366f6083SPeter Grehan break; 1233a2da7af6SNeel Natu case APIC_OFFSET_ICR_HI: 1234a2da7af6SNeel Natu if (!x2apic(vlapic)) { 1235a2da7af6SNeel Natu retval = 0; 1236a2da7af6SNeel Natu lapic->icr_hi = data; 1237a2da7af6SNeel Natu } 1238a2da7af6SNeel Natu break; 1239330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1240366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 1241fb03ca4eSNeel Natu vlapic_set_lvt(vlapic, offset, data); 1242366f6083SPeter Grehan break; 1243de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1244fb03ca4eSNeel Natu vlapic_set_icr_timer(vlapic, data); 1245366f6083SPeter Grehan break; 1246366f6083SPeter Grehan 1247de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1248fb03ca4eSNeel Natu vlapic_set_dcr(vlapic, data); 1249366f6083SPeter Grehan break; 1250366f6083SPeter Grehan 1251366f6083SPeter Grehan case APIC_OFFSET_ESR: 1252366f6083SPeter Grehan vlapic_update_errors(vlapic); 1253366f6083SPeter Grehan break; 1254366f6083SPeter Grehan case APIC_OFFSET_VER: 1255366f6083SPeter Grehan case APIC_OFFSET_APR: 1256366f6083SPeter Grehan case APIC_OFFSET_PPR: 1257366f6083SPeter Grehan case APIC_OFFSET_RRR: 1258366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1259366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1260366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1261de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1262366f6083SPeter Grehan default: 1263366f6083SPeter Grehan // Read only. 1264366f6083SPeter Grehan break; 1265366f6083SPeter Grehan } 1266366f6083SPeter Grehan 1267366f6083SPeter Grehan return (retval); 1268366f6083SPeter Grehan } 1269366f6083SPeter Grehan 1270de5ea6b6SNeel Natu void 1271de5ea6b6SNeel Natu vlapic_init(struct vlapic *vlapic) 1272366f6083SPeter Grehan { 1273de5ea6b6SNeel Natu KASSERT(vlapic->vm != NULL, ("vlapic_init: vm is not initialized")); 1274de5ea6b6SNeel Natu KASSERT(vlapic->vcpuid >= 0 && vlapic->vcpuid < VM_MAXCPU, 1275de5ea6b6SNeel Natu ("vlapic_init: vcpuid is not initialized")); 1276de5ea6b6SNeel Natu KASSERT(vlapic->apic_page != NULL, ("vlapic_init: apic_page is not " 1277de5ea6b6SNeel Natu "initialized")); 12782d3a73edSNeel Natu 1279becd9849SNeel Natu /* 1280becd9849SNeel Natu * If the vlapic is configured in x2apic mode then it will be 1281becd9849SNeel Natu * accessed in the critical section via the MSR emulation code. 1282becd9849SNeel Natu * 1283becd9849SNeel Natu * Therefore the timer mutex must be a spinlock because blockable 1284becd9849SNeel Natu * mutexes cannot be acquired in a critical section. 1285becd9849SNeel Natu */ 1286becd9849SNeel Natu mtx_init(&vlapic->timer_mtx, "vlapic timer mtx", NULL, MTX_SPIN); 1287fb03ca4eSNeel Natu callout_init(&vlapic->callout, 1); 1288fb03ca4eSNeel Natu 1289a2da7af6SNeel Natu vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; 12902d3a73edSNeel Natu 1291de5ea6b6SNeel Natu if (vlapic->vcpuid == 0) 12922d3a73edSNeel Natu vlapic->msr_apicbase |= APICBASE_BSP; 12932d3a73edSNeel Natu 129403cd0501SNeel Natu vlapic_reset(vlapic); 1295366f6083SPeter Grehan } 1296366f6083SPeter Grehan 1297366f6083SPeter Grehan void 1298366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic) 1299366f6083SPeter Grehan { 130003cd0501SNeel Natu 1301fb03ca4eSNeel Natu callout_drain(&vlapic->callout); 1302366f6083SPeter Grehan } 13032d3a73edSNeel Natu 13042d3a73edSNeel Natu uint64_t 13052d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic) 13062d3a73edSNeel Natu { 13072d3a73edSNeel Natu 13082d3a73edSNeel Natu return (vlapic->msr_apicbase); 13092d3a73edSNeel Natu } 13102d3a73edSNeel Natu 13112d3a73edSNeel Natu void 13123f0ddc7cSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t new) 13132d3a73edSNeel Natu { 13143f0ddc7cSNeel Natu struct LAPIC *lapic; 1315a2da7af6SNeel Natu enum x2apic_state state; 13163f0ddc7cSNeel Natu uint64_t old; 13173f0ddc7cSNeel Natu int err; 1318a2da7af6SNeel Natu 1319a2da7af6SNeel Natu err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state); 1320a2da7af6SNeel Natu if (err) 1321a2da7af6SNeel Natu panic("vlapic_set_apicbase: err %d fetching x2apic state", err); 1322a2da7af6SNeel Natu 1323a2da7af6SNeel Natu if (state == X2APIC_DISABLED) 13243f0ddc7cSNeel Natu new &= ~APICBASE_X2APIC; 13252d3a73edSNeel Natu 13263f0ddc7cSNeel Natu old = vlapic->msr_apicbase; 13273f0ddc7cSNeel Natu vlapic->msr_apicbase = new; 13283f0ddc7cSNeel Natu 13293f0ddc7cSNeel Natu /* 13303f0ddc7cSNeel Natu * If the vlapic is switching between xAPIC and x2APIC modes then 13313f0ddc7cSNeel Natu * reset the mode-dependent registers. 13323f0ddc7cSNeel Natu */ 13333f0ddc7cSNeel Natu if ((old ^ new) & APICBASE_X2APIC) { 13343f0ddc7cSNeel Natu lapic = vlapic->apic_page; 13353f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 13363f0ddc7cSNeel Natu if (x2apic(vlapic)) { 13373f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 13383f0ddc7cSNeel Natu lapic->dfr = 0; 13393f0ddc7cSNeel Natu } else { 13403f0ddc7cSNeel Natu lapic->ldr = 0; 13413f0ddc7cSNeel Natu lapic->dfr = 0xffffffff; 13423f0ddc7cSNeel Natu } 13433f0ddc7cSNeel Natu } 13442d3a73edSNeel Natu } 134573820fb0SNeel Natu 134673820fb0SNeel Natu void 134773820fb0SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 134873820fb0SNeel Natu { 134973820fb0SNeel Natu struct vlapic *vlapic; 135073820fb0SNeel Natu 135173820fb0SNeel Natu vlapic = vm_lapic(vm, vcpuid); 135273820fb0SNeel Natu 1353485f986aSNeel Natu if (state == X2APIC_DISABLED) 135473820fb0SNeel Natu vlapic->msr_apicbase &= ~APICBASE_X2APIC; 135573820fb0SNeel Natu } 13561c052192SNeel Natu 13574f8be175SNeel Natu void 13584f8be175SNeel Natu vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, 13594f8be175SNeel Natu int delmode, int vec) 13604f8be175SNeel Natu { 13614f8be175SNeel Natu bool lowprio; 13624f8be175SNeel Natu int vcpuid; 13634f8be175SNeel Natu cpuset_t dmask; 13644f8be175SNeel Natu 13654f8be175SNeel Natu if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) { 13664f8be175SNeel Natu VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode); 13674f8be175SNeel Natu return; 13684f8be175SNeel Natu } 13694f8be175SNeel Natu lowprio = (delmode == APIC_DELMODE_LOWPRIO); 13704f8be175SNeel Natu 13714f8be175SNeel Natu /* 13724f8be175SNeel Natu * We don't provide any virtual interrupt redirection hardware so 13734f8be175SNeel Natu * all interrupts originating from the ioapic or MSI specify the 13744f8be175SNeel Natu * 'dest' in the legacy xAPIC format. 13754f8be175SNeel Natu */ 13764f8be175SNeel Natu vlapic_calcdest(vm, &dmask, dest, phys, lowprio, false); 13774f8be175SNeel Natu 13784f8be175SNeel Natu while ((vcpuid = CPU_FFS(&dmask)) != 0) { 13794f8be175SNeel Natu vcpuid--; 13804f8be175SNeel Natu CPU_CLR(vcpuid, &dmask); 13814f8be175SNeel Natu lapic_set_intr(vm, vcpuid, vec, level); 13824f8be175SNeel Natu } 13834f8be175SNeel Natu } 13844f8be175SNeel Natu 1385de5ea6b6SNeel Natu void 1386de5ea6b6SNeel Natu vlapic_post_intr(struct vlapic *vlapic, int hostcpu) 1387de5ea6b6SNeel Natu { 1388de5ea6b6SNeel Natu /* 1389de5ea6b6SNeel Natu * Post an interrupt to the vcpu currently running on 'hostcpu'. 1390de5ea6b6SNeel Natu * 1391de5ea6b6SNeel Natu * This is done by leveraging features like Posted Interrupts (Intel) 1392de5ea6b6SNeel Natu * Doorbell MSR (AMD AVIC) that avoid a VM exit. 1393de5ea6b6SNeel Natu * 1394de5ea6b6SNeel Natu * If neither of these features are available then fallback to 1395de5ea6b6SNeel Natu * sending an IPI to 'hostcpu'. 1396de5ea6b6SNeel Natu */ 1397de5ea6b6SNeel Natu ipi_cpu(hostcpu, vmm_ipinum); 1398de5ea6b6SNeel Natu } 1399de5ea6b6SNeel Natu 14001c052192SNeel Natu bool 14011c052192SNeel Natu vlapic_enabled(struct vlapic *vlapic) 14021c052192SNeel Natu { 1403de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 14041c052192SNeel Natu 14051c052192SNeel Natu if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && 14061c052192SNeel Natu (lapic->svr & APIC_SVR_ENABLE) != 0) 14071c052192SNeel Natu return (true); 14081c052192SNeel Natu else 14091c052192SNeel Natu return (false); 14101c052192SNeel Natu } 1411