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 PRIO(x) ((x) >> 4) 58366f6083SPeter Grehan 59366f6083SPeter Grehan #define VLAPIC_VERSION (16) 60366f6083SPeter Grehan 61a2da7af6SNeel Natu #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) 622d3a73edSNeel Natu 63fb03ca4eSNeel Natu /* 64fb03ca4eSNeel Natu * The 'vlapic->timer_mtx' is used to provide mutual exclusion between the 65fafe8844SNeel Natu * vlapic_callout_handler() and vcpu accesses to: 66fafe8844SNeel Natu * - timer_freq_bt, timer_period_bt, timer_fire_bt 67fb03ca4eSNeel Natu * - timer LVT register 68fb03ca4eSNeel Natu */ 69becd9849SNeel Natu #define VLAPIC_TIMER_LOCK(vlapic) mtx_lock_spin(&((vlapic)->timer_mtx)) 70becd9849SNeel Natu #define VLAPIC_TIMER_UNLOCK(vlapic) mtx_unlock_spin(&((vlapic)->timer_mtx)) 71fb03ca4eSNeel Natu #define VLAPIC_TIMER_LOCKED(vlapic) mtx_owned(&((vlapic)->timer_mtx)) 72fb03ca4eSNeel Natu 732e25737aSNeel Natu #define VLAPIC_BUS_FREQ tsc_freq 742e25737aSNeel Natu 754f8be175SNeel Natu static __inline uint32_t 764f8be175SNeel Natu vlapic_get_id(struct vlapic *vlapic) 774f8be175SNeel Natu { 784f8be175SNeel Natu 794f8be175SNeel Natu if (x2apic(vlapic)) 804f8be175SNeel Natu return (vlapic->vcpuid); 814f8be175SNeel Natu else 824f8be175SNeel Natu return (vlapic->vcpuid << 24); 834f8be175SNeel Natu } 844f8be175SNeel Natu 853f0ddc7cSNeel Natu static uint32_t 863f0ddc7cSNeel Natu x2apic_ldr(struct vlapic *vlapic) 874f8be175SNeel Natu { 884f8be175SNeel Natu int apicid; 894f8be175SNeel Natu uint32_t ldr; 904f8be175SNeel Natu 914f8be175SNeel Natu apicid = vlapic_get_id(vlapic); 924f8be175SNeel Natu ldr = 1 << (apicid & 0xf); 934f8be175SNeel Natu ldr |= (apicid & 0xffff0) << 12; 944f8be175SNeel Natu return (ldr); 954f8be175SNeel Natu } 964f8be175SNeel Natu 973f0ddc7cSNeel Natu void 983f0ddc7cSNeel Natu vlapic_dfr_write_handler(struct vlapic *vlapic) 994f8be175SNeel Natu { 1004f8be175SNeel Natu struct LAPIC *lapic; 1014f8be175SNeel Natu 102de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1034f8be175SNeel Natu if (x2apic(vlapic)) { 1043f0ddc7cSNeel Natu VM_CTR1(vlapic->vm, "ignoring write to DFR in x2apic mode: %#x", 1053f0ddc7cSNeel Natu lapic->dfr); 1063f0ddc7cSNeel Natu lapic->dfr = 0; 1074f8be175SNeel Natu return; 1084f8be175SNeel Natu } 1094f8be175SNeel Natu 1103f0ddc7cSNeel Natu lapic->dfr &= APIC_DFR_MODEL_MASK; 1113f0ddc7cSNeel Natu lapic->dfr |= APIC_DFR_RESERVED; 1123f0ddc7cSNeel Natu 1133f0ddc7cSNeel Natu if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT) 1144f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model"); 1153f0ddc7cSNeel Natu else if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER) 1164f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model"); 1174f8be175SNeel Natu else 1183f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "DFR in Unknown Model %#x", lapic->dfr); 1194f8be175SNeel Natu } 1204f8be175SNeel Natu 1213f0ddc7cSNeel Natu void 1223f0ddc7cSNeel Natu vlapic_ldr_write_handler(struct vlapic *vlapic) 1234f8be175SNeel Natu { 1244f8be175SNeel Natu struct LAPIC *lapic; 1254f8be175SNeel Natu 1263f0ddc7cSNeel Natu lapic = vlapic->apic_page; 1273f0ddc7cSNeel Natu 1284f8be175SNeel Natu /* LDR is read-only in x2apic mode */ 1294f8be175SNeel Natu if (x2apic(vlapic)) { 1303f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "ignoring write to LDR in x2apic mode: %#x", 1313f0ddc7cSNeel Natu lapic->ldr); 1323f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 1333f0ddc7cSNeel Natu } else { 1343f0ddc7cSNeel Natu lapic->ldr &= ~APIC_LDR_RESERVED; 1353f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr); 1363f0ddc7cSNeel Natu } 1374f8be175SNeel Natu } 1384f8be175SNeel Natu 1393f0ddc7cSNeel Natu void 1403f0ddc7cSNeel Natu vlapic_id_write_handler(struct vlapic *vlapic) 1413f0ddc7cSNeel Natu { 1423f0ddc7cSNeel Natu struct LAPIC *lapic; 1433f0ddc7cSNeel Natu 1443f0ddc7cSNeel Natu /* 1453f0ddc7cSNeel Natu * We don't allow the ID register to be modified so reset it back to 1463f0ddc7cSNeel Natu * its default value. 1473f0ddc7cSNeel Natu */ 148de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1493f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 1504f8be175SNeel Natu } 1514f8be175SNeel Natu 1522e25737aSNeel Natu static int 1532e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr) 1542e25737aSNeel Natu { 1552e25737aSNeel Natu switch (dcr & 0xB) { 156117e8f37SPeter Grehan case APIC_TDCR_1: 157117e8f37SPeter Grehan return (1); 1582e25737aSNeel Natu case APIC_TDCR_2: 1592e25737aSNeel Natu return (2); 1602e25737aSNeel Natu case APIC_TDCR_4: 1612e25737aSNeel Natu return (4); 1622e25737aSNeel Natu case APIC_TDCR_8: 1632e25737aSNeel Natu return (8); 1642e25737aSNeel Natu case APIC_TDCR_16: 1652e25737aSNeel Natu return (16); 1662e25737aSNeel Natu case APIC_TDCR_32: 1672e25737aSNeel Natu return (32); 1682e25737aSNeel Natu case APIC_TDCR_64: 1692e25737aSNeel Natu return (64); 1702e25737aSNeel Natu case APIC_TDCR_128: 1712e25737aSNeel Natu return (128); 1722e25737aSNeel Natu default: 1732e25737aSNeel Natu panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr); 1742e25737aSNeel Natu } 1752e25737aSNeel Natu } 1762e25737aSNeel Natu 177366f6083SPeter Grehan #if 0 178366f6083SPeter Grehan static inline void 179366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) 180366f6083SPeter Grehan { 181366f6083SPeter Grehan printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset, 182366f6083SPeter Grehan *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS, 183366f6083SPeter Grehan *lvt & APIC_LVTT_M); 184366f6083SPeter Grehan } 185366f6083SPeter Grehan #endif 186366f6083SPeter Grehan 187fb03ca4eSNeel Natu static uint32_t 188366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic) 189366f6083SPeter Grehan { 190fb03ca4eSNeel Natu struct bintime bt_now, bt_rem; 191fb03ca4eSNeel Natu struct LAPIC *lapic; 192fb03ca4eSNeel Natu uint32_t ccr; 193fb03ca4eSNeel Natu 194fb03ca4eSNeel Natu ccr = 0; 195de5ea6b6SNeel Natu lapic = vlapic->apic_page; 196fb03ca4eSNeel Natu 197fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 198fb03ca4eSNeel Natu if (callout_active(&vlapic->callout)) { 199fb03ca4eSNeel Natu /* 200fb03ca4eSNeel Natu * If the timer is scheduled to expire in the future then 201fb03ca4eSNeel Natu * compute the value of 'ccr' based on the remaining time. 202fb03ca4eSNeel Natu */ 203fb03ca4eSNeel Natu binuptime(&bt_now); 204fb03ca4eSNeel Natu if (bintime_cmp(&vlapic->timer_fire_bt, &bt_now, >)) { 205fb03ca4eSNeel Natu bt_rem = vlapic->timer_fire_bt; 206fb03ca4eSNeel Natu bintime_sub(&bt_rem, &bt_now); 207fb03ca4eSNeel Natu ccr += bt_rem.sec * BT2FREQ(&vlapic->timer_freq_bt); 208fb03ca4eSNeel Natu ccr += bt_rem.frac / vlapic->timer_freq_bt.frac; 209fb03ca4eSNeel Natu } 210fb03ca4eSNeel Natu } 211fb03ca4eSNeel Natu KASSERT(ccr <= lapic->icr_timer, ("vlapic_get_ccr: invalid ccr %#x, " 212fb03ca4eSNeel Natu "icr_timer is %#x", ccr, lapic->icr_timer)); 213fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic ccr_timer = %#x, icr_timer = %#x", 214fb03ca4eSNeel Natu ccr, lapic->icr_timer); 215fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 216fb03ca4eSNeel Natu return (ccr); 217fb03ca4eSNeel Natu } 218fb03ca4eSNeel Natu 219fafe8844SNeel Natu void 220fafe8844SNeel Natu vlapic_dcr_write_handler(struct vlapic *vlapic) 221fb03ca4eSNeel Natu { 222fb03ca4eSNeel Natu struct LAPIC *lapic; 223fb03ca4eSNeel Natu int divisor; 224fb03ca4eSNeel Natu 225de5ea6b6SNeel Natu lapic = vlapic->apic_page; 226fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 227fb03ca4eSNeel Natu 228fafe8844SNeel Natu divisor = vlapic_timer_divisor(lapic->dcr_timer); 229fafe8844SNeel Natu VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", 230fafe8844SNeel Natu lapic->dcr_timer, divisor); 231fb03ca4eSNeel Natu 232fb03ca4eSNeel Natu /* 233fb03ca4eSNeel Natu * Update the timer frequency and the timer period. 234fb03ca4eSNeel Natu * 235fb03ca4eSNeel Natu * XXX changes to the frequency divider will not take effect until 236fb03ca4eSNeel Natu * the timer is reloaded. 237fb03ca4eSNeel Natu */ 238fb03ca4eSNeel Natu FREQ2BT(VLAPIC_BUS_FREQ / divisor, &vlapic->timer_freq_bt); 239fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 240fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 241fb03ca4eSNeel Natu 242fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 243366f6083SPeter Grehan } 244366f6083SPeter Grehan 245fafe8844SNeel Natu void 246fafe8844SNeel Natu vlapic_esr_write_handler(struct vlapic *vlapic) 247366f6083SPeter Grehan { 248de5ea6b6SNeel Natu struct LAPIC *lapic; 249de5ea6b6SNeel Natu 250de5ea6b6SNeel Natu lapic = vlapic->apic_page; 251330baf58SJohn Baldwin lapic->esr = vlapic->esr_pending; 252330baf58SJohn Baldwin vlapic->esr_pending = 0; 253366f6083SPeter Grehan } 254366f6083SPeter Grehan 2554d1e82a8SNeel Natu int 256b5b28fc9SNeel Natu vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) 257366f6083SPeter Grehan { 2584d1e82a8SNeel Natu struct LAPIC *lapic; 259b5b28fc9SNeel Natu uint32_t *irrptr, *tmrptr, mask; 260366f6083SPeter Grehan int idx; 261366f6083SPeter Grehan 2624d1e82a8SNeel Natu KASSERT(vector >= 0 && vector < 256, ("invalid vector %d", vector)); 263366f6083SPeter Grehan 2644d1e82a8SNeel Natu lapic = vlapic->apic_page; 2651c052192SNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) { 2661c052192SNeel Natu VLAPIC_CTR1(vlapic, "vlapic is software disabled, ignoring " 2671c052192SNeel Natu "interrupt %d", vector); 2684d1e82a8SNeel Natu return (0); 2691c052192SNeel Natu } 2701c052192SNeel Natu 271330baf58SJohn Baldwin if (vector < 16) { 272330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR); 2734d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "vlapic ignoring interrupt to vector %d", 2744d1e82a8SNeel Natu vector); 2754d1e82a8SNeel Natu return (1); 276330baf58SJohn Baldwin } 277330baf58SJohn Baldwin 278*88c4b8d1SNeel Natu if (vlapic->ops.set_intr_ready) 279*88c4b8d1SNeel Natu return ((*vlapic->ops.set_intr_ready)(vlapic, vector, level)); 280*88c4b8d1SNeel Natu 281366f6083SPeter Grehan idx = (vector / 32) * 4; 282b5b28fc9SNeel Natu mask = 1 << (vector % 32); 283b5b28fc9SNeel Natu 284366f6083SPeter Grehan irrptr = &lapic->irr0; 285b5b28fc9SNeel Natu atomic_set_int(&irrptr[idx], mask); 286b5b28fc9SNeel Natu 287b5b28fc9SNeel Natu /* 288b5b28fc9SNeel Natu * Upon acceptance of an interrupt into the IRR the corresponding 289b5b28fc9SNeel Natu * TMR bit is cleared for edge-triggered interrupts and set for 290b5b28fc9SNeel Natu * level-triggered interrupts. 291b5b28fc9SNeel Natu */ 292b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 293b5b28fc9SNeel Natu if (level) 294b5b28fc9SNeel Natu atomic_set_int(&tmrptr[idx], mask); 295b5b28fc9SNeel Natu else 296b5b28fc9SNeel Natu atomic_clear_int(&tmrptr[idx], mask); 297b5b28fc9SNeel Natu 298366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready"); 2994d1e82a8SNeel Natu return (1); 300366f6083SPeter Grehan } 301366f6083SPeter Grehan 302366f6083SPeter Grehan static __inline uint32_t * 303fb03ca4eSNeel Natu vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) 304366f6083SPeter Grehan { 305de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 306366f6083SPeter Grehan int i; 307366f6083SPeter Grehan 308330baf58SJohn Baldwin switch (offset) { 309330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 310330baf58SJohn Baldwin return (&lapic->lvt_cmci); 311330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 312366f6083SPeter Grehan i = (offset - APIC_OFFSET_TIMER_LVT) >> 2; 313366f6083SPeter Grehan return ((&lapic->lvt_timer) + i);; 314330baf58SJohn Baldwin default: 315330baf58SJohn Baldwin panic("vlapic_get_lvt: invalid LVT\n"); 316330baf58SJohn Baldwin } 317366f6083SPeter Grehan } 318366f6083SPeter Grehan 3197c05bc31SNeel Natu static __inline int 3207c05bc31SNeel Natu lvt_off_to_idx(uint32_t offset) 3217c05bc31SNeel Natu { 3227c05bc31SNeel Natu int index; 3237c05bc31SNeel Natu 3247c05bc31SNeel Natu switch (offset) { 3257c05bc31SNeel Natu case APIC_OFFSET_CMCI_LVT: 3267c05bc31SNeel Natu index = APIC_LVT_CMCI; 3277c05bc31SNeel Natu break; 3287c05bc31SNeel Natu case APIC_OFFSET_TIMER_LVT: 3297c05bc31SNeel Natu index = APIC_LVT_TIMER; 3307c05bc31SNeel Natu break; 3317c05bc31SNeel Natu case APIC_OFFSET_THERM_LVT: 3327c05bc31SNeel Natu index = APIC_LVT_THERMAL; 3337c05bc31SNeel Natu break; 3347c05bc31SNeel Natu case APIC_OFFSET_PERF_LVT: 3357c05bc31SNeel Natu index = APIC_LVT_PMC; 3367c05bc31SNeel Natu break; 3377c05bc31SNeel Natu case APIC_OFFSET_LINT0_LVT: 3387c05bc31SNeel Natu index = APIC_LVT_LINT0; 3397c05bc31SNeel Natu break; 3407c05bc31SNeel Natu case APIC_OFFSET_LINT1_LVT: 3417c05bc31SNeel Natu index = APIC_LVT_LINT1; 3427c05bc31SNeel Natu break; 3437c05bc31SNeel Natu case APIC_OFFSET_ERROR_LVT: 3447c05bc31SNeel Natu index = APIC_LVT_ERROR; 3457c05bc31SNeel Natu break; 3467c05bc31SNeel Natu default: 3477c05bc31SNeel Natu index = -1; 3487c05bc31SNeel Natu break; 3497c05bc31SNeel Natu } 3507c05bc31SNeel Natu KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: " 3517c05bc31SNeel Natu "invalid lvt index %d for offset %#x", index, offset)); 3527c05bc31SNeel Natu 3537c05bc31SNeel Natu return (index); 3547c05bc31SNeel Natu } 3557c05bc31SNeel Natu 356fb03ca4eSNeel Natu static __inline uint32_t 357fb03ca4eSNeel Natu vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) 358fb03ca4eSNeel Natu { 3597c05bc31SNeel Natu int idx; 3607c05bc31SNeel Natu uint32_t val; 361fb03ca4eSNeel Natu 3627c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 3637c05bc31SNeel Natu val = atomic_load_acq_32(&vlapic->lvt_last[idx]); 3647c05bc31SNeel Natu return (val); 365fb03ca4eSNeel Natu } 366fb03ca4eSNeel Natu 3677c05bc31SNeel Natu void 3687c05bc31SNeel Natu vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset) 369fb03ca4eSNeel Natu { 3707c05bc31SNeel Natu uint32_t *lvtptr, mask, val; 371fb03ca4eSNeel Natu struct LAPIC *lapic; 3727c05bc31SNeel Natu int idx; 373fb03ca4eSNeel Natu 374de5ea6b6SNeel Natu lapic = vlapic->apic_page; 375fb03ca4eSNeel Natu lvtptr = vlapic_get_lvtptr(vlapic, offset); 3767c05bc31SNeel Natu val = *lvtptr; 3777c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 378fb03ca4eSNeel Natu 379fb03ca4eSNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) 380fb03ca4eSNeel Natu val |= APIC_LVT_M; 381330baf58SJohn Baldwin mask = APIC_LVT_M | APIC_LVT_DS | APIC_LVT_VECTOR; 382330baf58SJohn Baldwin switch (offset) { 383330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT: 384330baf58SJohn Baldwin mask |= APIC_LVTT_TM; 385330baf58SJohn Baldwin break; 386330baf58SJohn Baldwin case APIC_OFFSET_ERROR_LVT: 387330baf58SJohn Baldwin break; 388330baf58SJohn Baldwin case APIC_OFFSET_LINT0_LVT: 389330baf58SJohn Baldwin case APIC_OFFSET_LINT1_LVT: 390330baf58SJohn Baldwin mask |= APIC_LVT_TM | APIC_LVT_RIRR | APIC_LVT_IIPP; 391330baf58SJohn Baldwin /* FALLTHROUGH */ 392330baf58SJohn Baldwin default: 393330baf58SJohn Baldwin mask |= APIC_LVT_DM; 394330baf58SJohn Baldwin break; 395330baf58SJohn Baldwin } 3967c05bc31SNeel Natu val &= mask; 3977c05bc31SNeel Natu *lvtptr = val; 3987c05bc31SNeel Natu atomic_store_rel_32(&vlapic->lvt_last[idx], val); 3997c05bc31SNeel Natu } 400fb03ca4eSNeel Natu 4017c05bc31SNeel Natu static void 4027c05bc31SNeel Natu vlapic_mask_lvts(struct vlapic *vlapic) 4037c05bc31SNeel Natu { 4047c05bc31SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 4057c05bc31SNeel Natu 4067c05bc31SNeel Natu lapic->lvt_cmci |= APIC_LVT_M; 4077c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT); 4087c05bc31SNeel Natu 4097c05bc31SNeel Natu lapic->lvt_timer |= APIC_LVT_M; 4107c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT); 4117c05bc31SNeel Natu 4127c05bc31SNeel Natu lapic->lvt_thermal |= APIC_LVT_M; 4137c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT); 4147c05bc31SNeel Natu 4157c05bc31SNeel Natu lapic->lvt_pcint |= APIC_LVT_M; 4167c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT); 4177c05bc31SNeel Natu 4187c05bc31SNeel Natu lapic->lvt_lint0 |= APIC_LVT_M; 4197c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT); 4207c05bc31SNeel Natu 4217c05bc31SNeel Natu lapic->lvt_lint1 |= APIC_LVT_M; 4227c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT); 4237c05bc31SNeel Natu 4247c05bc31SNeel Natu lapic->lvt_error |= APIC_LVT_M; 4257c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT); 426fb03ca4eSNeel Natu } 427fb03ca4eSNeel Natu 428330baf58SJohn Baldwin static int 429330baf58SJohn Baldwin vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt) 430330baf58SJohn Baldwin { 431330baf58SJohn Baldwin uint32_t vec, mode; 432330baf58SJohn Baldwin 433330baf58SJohn Baldwin if (lvt & APIC_LVT_M) 434330baf58SJohn Baldwin return (0); 435330baf58SJohn Baldwin 436330baf58SJohn Baldwin vec = lvt & APIC_LVT_VECTOR; 437330baf58SJohn Baldwin mode = lvt & APIC_LVT_DM; 438330baf58SJohn Baldwin 439330baf58SJohn Baldwin switch (mode) { 440330baf58SJohn Baldwin case APIC_LVT_DM_FIXED: 441330baf58SJohn Baldwin if (vec < 16) { 442330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 443330baf58SJohn Baldwin return (0); 444330baf58SJohn Baldwin } 4454d1e82a8SNeel Natu if (vlapic_set_intr_ready(vlapic, vec, false)) 446de5ea6b6SNeel Natu vcpu_notify_event(vlapic->vm, vlapic->vcpuid, true); 447330baf58SJohn Baldwin break; 448330baf58SJohn Baldwin case APIC_LVT_DM_NMI: 449330baf58SJohn Baldwin vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 450330baf58SJohn Baldwin break; 451330baf58SJohn Baldwin default: 452330baf58SJohn Baldwin // Other modes ignored 453330baf58SJohn Baldwin return (0); 454330baf58SJohn Baldwin } 455330baf58SJohn Baldwin return (1); 456330baf58SJohn Baldwin } 457330baf58SJohn Baldwin 458366f6083SPeter Grehan #if 1 459366f6083SPeter Grehan static void 460366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic) 461366f6083SPeter Grehan { 462366f6083SPeter Grehan int i; 463366f6083SPeter Grehan uint32_t *isrptr; 464366f6083SPeter Grehan 465de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 466366f6083SPeter Grehan for (i = 0; i < 8; i++) 467366f6083SPeter Grehan printf("ISR%d 0x%08x\n", i, isrptr[i * 4]); 468366f6083SPeter Grehan 469366f6083SPeter Grehan for (i = 0; i <= vlapic->isrvec_stk_top; i++) 470366f6083SPeter Grehan printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]); 471366f6083SPeter Grehan } 472366f6083SPeter Grehan #endif 473366f6083SPeter Grehan 474366f6083SPeter Grehan /* 475366f6083SPeter Grehan * Algorithm adopted from section "Interrupt, Task and Processor Priority" 476366f6083SPeter Grehan * in Intel Architecture Manual Vol 3a. 477366f6083SPeter Grehan */ 478366f6083SPeter Grehan static void 479366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic) 480366f6083SPeter Grehan { 481366f6083SPeter Grehan int isrvec, tpr, ppr; 482366f6083SPeter Grehan 483366f6083SPeter Grehan /* 484366f6083SPeter Grehan * Note that the value on the stack at index 0 is always 0. 485366f6083SPeter Grehan * 486366f6083SPeter Grehan * This is a placeholder for the value of ISRV when none of the 487366f6083SPeter Grehan * bits is set in the ISRx registers. 488366f6083SPeter Grehan */ 489366f6083SPeter Grehan isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top]; 490de5ea6b6SNeel Natu tpr = vlapic->apic_page->tpr; 491366f6083SPeter Grehan 492366f6083SPeter Grehan #if 1 493366f6083SPeter Grehan { 494366f6083SPeter Grehan int i, lastprio, curprio, vector, idx; 495366f6083SPeter Grehan uint32_t *isrptr; 496366f6083SPeter Grehan 497366f6083SPeter Grehan if (vlapic->isrvec_stk_top == 0 && isrvec != 0) 498366f6083SPeter Grehan panic("isrvec_stk is corrupted: %d", isrvec); 499366f6083SPeter Grehan 500366f6083SPeter Grehan /* 501366f6083SPeter Grehan * Make sure that the priority of the nested interrupts is 502366f6083SPeter Grehan * always increasing. 503366f6083SPeter Grehan */ 504366f6083SPeter Grehan lastprio = -1; 505366f6083SPeter Grehan for (i = 1; i <= vlapic->isrvec_stk_top; i++) { 506366f6083SPeter Grehan curprio = PRIO(vlapic->isrvec_stk[i]); 507366f6083SPeter Grehan if (curprio <= lastprio) { 508366f6083SPeter Grehan dump_isrvec_stk(vlapic); 509366f6083SPeter Grehan panic("isrvec_stk does not satisfy invariant"); 510366f6083SPeter Grehan } 511366f6083SPeter Grehan lastprio = curprio; 512366f6083SPeter Grehan } 513366f6083SPeter Grehan 514366f6083SPeter Grehan /* 515366f6083SPeter Grehan * Make sure that each bit set in the ISRx registers has a 516366f6083SPeter Grehan * corresponding entry on the isrvec stack. 517366f6083SPeter Grehan */ 518366f6083SPeter Grehan i = 1; 519de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 520366f6083SPeter Grehan for (vector = 0; vector < 256; vector++) { 521366f6083SPeter Grehan idx = (vector / 32) * 4; 522366f6083SPeter Grehan if (isrptr[idx] & (1 << (vector % 32))) { 523366f6083SPeter Grehan if (i > vlapic->isrvec_stk_top || 524366f6083SPeter Grehan vlapic->isrvec_stk[i] != vector) { 525366f6083SPeter Grehan dump_isrvec_stk(vlapic); 526366f6083SPeter Grehan panic("ISR and isrvec_stk out of sync"); 527366f6083SPeter Grehan } 528366f6083SPeter Grehan i++; 529366f6083SPeter Grehan } 530366f6083SPeter Grehan } 531366f6083SPeter Grehan } 532366f6083SPeter Grehan #endif 533366f6083SPeter Grehan 534366f6083SPeter Grehan if (PRIO(tpr) >= PRIO(isrvec)) 535366f6083SPeter Grehan ppr = tpr; 536366f6083SPeter Grehan else 537366f6083SPeter Grehan ppr = isrvec & 0xf0; 538366f6083SPeter Grehan 539de5ea6b6SNeel Natu vlapic->apic_page->ppr = ppr; 540366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr); 541366f6083SPeter Grehan } 542366f6083SPeter Grehan 543366f6083SPeter Grehan static void 544366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic) 545366f6083SPeter Grehan { 546de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 547b5b28fc9SNeel Natu uint32_t *isrptr, *tmrptr; 548b5b28fc9SNeel Natu int i, idx, bitpos, vector; 549366f6083SPeter Grehan 550366f6083SPeter Grehan isrptr = &lapic->isr0; 551b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 552366f6083SPeter Grehan 553366f6083SPeter Grehan /* 554366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 555366f6083SPeter Grehan * by the processor. 556366f6083SPeter Grehan */ 557366f6083SPeter Grehan for (i = 7; i > 0; i--) { 558366f6083SPeter Grehan idx = i * 4; 559366f6083SPeter Grehan bitpos = fls(isrptr[idx]); 560b5b28fc9SNeel Natu if (bitpos-- != 0) { 561366f6083SPeter Grehan if (vlapic->isrvec_stk_top <= 0) { 562366f6083SPeter Grehan panic("invalid vlapic isrvec_stk_top %d", 563366f6083SPeter Grehan vlapic->isrvec_stk_top); 564366f6083SPeter Grehan } 565b5b28fc9SNeel Natu isrptr[idx] &= ~(1 << bitpos); 566366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi"); 567366f6083SPeter Grehan vlapic->isrvec_stk_top--; 568366f6083SPeter Grehan vlapic_update_ppr(vlapic); 569b5b28fc9SNeel Natu if ((tmrptr[idx] & (1 << bitpos)) != 0) { 570b5b28fc9SNeel Natu vector = i * 32 + bitpos; 571b5b28fc9SNeel Natu vioapic_process_eoi(vlapic->vm, vlapic->vcpuid, 572b5b28fc9SNeel Natu vector); 573b5b28fc9SNeel Natu } 574366f6083SPeter Grehan return; 575366f6083SPeter Grehan } 576366f6083SPeter Grehan } 577366f6083SPeter Grehan } 578366f6083SPeter Grehan 579366f6083SPeter Grehan static __inline int 580fb03ca4eSNeel Natu vlapic_get_lvt_field(uint32_t lvt, uint32_t mask) 581366f6083SPeter Grehan { 582fb03ca4eSNeel Natu 583fb03ca4eSNeel Natu return (lvt & mask); 584366f6083SPeter Grehan } 585366f6083SPeter Grehan 586366f6083SPeter Grehan static __inline int 587366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic) 588366f6083SPeter Grehan { 589fb03ca4eSNeel Natu uint32_t lvt; 590366f6083SPeter Grehan 591366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 592366f6083SPeter Grehan 593366f6083SPeter Grehan return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC)); 594366f6083SPeter Grehan } 595366f6083SPeter Grehan 596330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_ERROR, "error interrupts generated by vlapic"); 597330baf58SJohn Baldwin 598330baf58SJohn Baldwin void 599330baf58SJohn Baldwin vlapic_set_error(struct vlapic *vlapic, uint32_t mask) 600330baf58SJohn Baldwin { 601330baf58SJohn Baldwin uint32_t lvt; 602330baf58SJohn Baldwin 603330baf58SJohn Baldwin vlapic->esr_pending |= mask; 604330baf58SJohn Baldwin if (vlapic->esr_firing) 605330baf58SJohn Baldwin return; 606330baf58SJohn Baldwin vlapic->esr_firing = 1; 607330baf58SJohn Baldwin 608330baf58SJohn Baldwin // The error LVT always uses the fixed delivery mode. 609330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT); 610330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) { 611330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_ERROR, 1); 612330baf58SJohn Baldwin } 613330baf58SJohn Baldwin vlapic->esr_firing = 0; 614330baf58SJohn Baldwin } 615330baf58SJohn Baldwin 61677d8fd9bSNeel Natu static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic"); 61777d8fd9bSNeel Natu 618366f6083SPeter Grehan static void 619366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic) 620366f6083SPeter Grehan { 621fb03ca4eSNeel Natu uint32_t lvt; 622fb03ca4eSNeel Natu 623fb03ca4eSNeel Natu KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked")); 624366f6083SPeter Grehan 625330baf58SJohn Baldwin // The timer LVT always uses the fixed delivery mode. 626366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 627330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) { 62877d8fd9bSNeel Natu vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1); 629366f6083SPeter Grehan } 630366f6083SPeter Grehan } 631366f6083SPeter Grehan 632330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_CMC, 633330baf58SJohn Baldwin "corrected machine check interrupts generated by vlapic"); 634330baf58SJohn Baldwin 635330baf58SJohn Baldwin void 636330baf58SJohn Baldwin vlapic_fire_cmci(struct vlapic *vlapic) 637330baf58SJohn Baldwin { 638330baf58SJohn Baldwin uint32_t lvt; 639330baf58SJohn Baldwin 640330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT); 641330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt)) { 642330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_CMC, 1); 643330baf58SJohn Baldwin } 644330baf58SJohn Baldwin } 645330baf58SJohn Baldwin 6467c05bc31SNeel Natu static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1, 647330baf58SJohn Baldwin "lvts triggered"); 648330baf58SJohn Baldwin 649330baf58SJohn Baldwin int 650330baf58SJohn Baldwin vlapic_trigger_lvt(struct vlapic *vlapic, int vector) 651330baf58SJohn Baldwin { 652330baf58SJohn Baldwin uint32_t lvt; 653330baf58SJohn Baldwin 654330baf58SJohn Baldwin switch (vector) { 655330baf58SJohn Baldwin case APIC_LVT_LINT0: 656330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT); 657330baf58SJohn Baldwin break; 658330baf58SJohn Baldwin case APIC_LVT_LINT1: 659330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT1_LVT); 660330baf58SJohn Baldwin break; 661330baf58SJohn Baldwin case APIC_LVT_TIMER: 662330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 663330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 664330baf58SJohn Baldwin break; 665330baf58SJohn Baldwin case APIC_LVT_ERROR: 666330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT); 667330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 668330baf58SJohn Baldwin break; 669330baf58SJohn Baldwin case APIC_LVT_PMC: 670330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_PERF_LVT); 671330baf58SJohn Baldwin break; 672330baf58SJohn Baldwin case APIC_LVT_THERMAL: 673330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_THERM_LVT); 674330baf58SJohn Baldwin break; 675330baf58SJohn Baldwin case APIC_LVT_CMCI: 676330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT); 677330baf58SJohn Baldwin break; 678330baf58SJohn Baldwin default: 679330baf58SJohn Baldwin return (EINVAL); 680330baf58SJohn Baldwin } 681330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt)) { 682330baf58SJohn Baldwin vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 683330baf58SJohn Baldwin LVTS_TRIGGERRED, vector, 1); 684330baf58SJohn Baldwin } 685330baf58SJohn Baldwin return (0); 686330baf58SJohn Baldwin } 687330baf58SJohn Baldwin 688fb03ca4eSNeel Natu static void 689fb03ca4eSNeel Natu vlapic_callout_handler(void *arg) 690fb03ca4eSNeel Natu { 691fb03ca4eSNeel Natu struct vlapic *vlapic; 692fb03ca4eSNeel Natu struct bintime bt, btnow; 693fb03ca4eSNeel Natu sbintime_t rem_sbt; 694fb03ca4eSNeel Natu 695fb03ca4eSNeel Natu vlapic = arg; 696fb03ca4eSNeel Natu 697fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 698fb03ca4eSNeel Natu if (callout_pending(&vlapic->callout)) /* callout was reset */ 699fb03ca4eSNeel Natu goto done; 700fb03ca4eSNeel Natu 701fb03ca4eSNeel Natu if (!callout_active(&vlapic->callout)) /* callout was stopped */ 702fb03ca4eSNeel Natu goto done; 703fb03ca4eSNeel Natu 704fb03ca4eSNeel Natu callout_deactivate(&vlapic->callout); 705fb03ca4eSNeel Natu 706fb03ca4eSNeel Natu vlapic_fire_timer(vlapic); 707fb03ca4eSNeel Natu 708fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) { 709fb03ca4eSNeel Natu binuptime(&btnow); 710fb03ca4eSNeel Natu KASSERT(bintime_cmp(&btnow, &vlapic->timer_fire_bt, >=), 711fb03ca4eSNeel Natu ("vlapic callout at %#lx.%#lx, expected at %#lx.#%lx", 712fb03ca4eSNeel Natu btnow.sec, btnow.frac, vlapic->timer_fire_bt.sec, 713fb03ca4eSNeel Natu vlapic->timer_fire_bt.frac)); 714fb03ca4eSNeel Natu 715fb03ca4eSNeel Natu /* 716fb03ca4eSNeel Natu * Compute the delta between when the timer was supposed to 717fb03ca4eSNeel Natu * fire and the present time. 718fb03ca4eSNeel Natu */ 719fb03ca4eSNeel Natu bt = btnow; 720fb03ca4eSNeel Natu bintime_sub(&bt, &vlapic->timer_fire_bt); 721fb03ca4eSNeel Natu 722fb03ca4eSNeel Natu rem_sbt = bttosbt(vlapic->timer_period_bt); 723fb03ca4eSNeel Natu if (bintime_cmp(&bt, &vlapic->timer_period_bt, <)) { 724fb03ca4eSNeel Natu /* 725fb03ca4eSNeel Natu * Adjust the time until the next countdown downward 726fb03ca4eSNeel Natu * to account for the lost time. 727fb03ca4eSNeel Natu */ 728fb03ca4eSNeel Natu rem_sbt -= bttosbt(bt); 729fb03ca4eSNeel Natu } else { 730fb03ca4eSNeel Natu /* 731fb03ca4eSNeel Natu * If the delta is greater than the timer period then 732fb03ca4eSNeel Natu * just reset our time base instead of trying to catch 733fb03ca4eSNeel Natu * up. 734fb03ca4eSNeel Natu */ 735fb03ca4eSNeel Natu vlapic->timer_fire_bt = btnow; 736fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic timer lagging by %lu " 737fb03ca4eSNeel Natu "usecs, period is %lu usecs - resetting time base", 738fb03ca4eSNeel Natu bttosbt(bt) / SBT_1US, 739fb03ca4eSNeel Natu bttosbt(vlapic->timer_period_bt) / SBT_1US); 740fb03ca4eSNeel Natu } 741fb03ca4eSNeel Natu 742fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 743fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, rem_sbt, 0, 744fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 745fb03ca4eSNeel Natu } 746fb03ca4eSNeel Natu done: 747fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 748fb03ca4eSNeel Natu } 749fb03ca4eSNeel Natu 750fafe8844SNeel Natu void 751fafe8844SNeel Natu vlapic_icrtmr_write_handler(struct vlapic *vlapic) 752fb03ca4eSNeel Natu { 753fb03ca4eSNeel Natu struct LAPIC *lapic; 754fb03ca4eSNeel Natu sbintime_t sbt; 755fafe8844SNeel Natu uint32_t icr_timer; 756fb03ca4eSNeel Natu 757fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 758fb03ca4eSNeel Natu 759de5ea6b6SNeel Natu lapic = vlapic->apic_page; 760fafe8844SNeel Natu icr_timer = lapic->icr_timer; 761fb03ca4eSNeel Natu 762fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 763fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, icr_timer); 764fb03ca4eSNeel Natu 765fb03ca4eSNeel Natu if (icr_timer != 0) { 766fb03ca4eSNeel Natu binuptime(&vlapic->timer_fire_bt); 767fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 768fb03ca4eSNeel Natu 769fb03ca4eSNeel Natu sbt = bttosbt(vlapic->timer_period_bt); 770fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, sbt, 0, 771fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 772fb03ca4eSNeel Natu } else 773fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 774fb03ca4eSNeel Natu 775fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 776fb03ca4eSNeel Natu } 777fb03ca4eSNeel Natu 7784f8be175SNeel Natu /* 7794f8be175SNeel Natu * This function populates 'dmask' with the set of vcpus that match the 7804f8be175SNeel Natu * addressing specified by the (dest, phys, lowprio) tuple. 7814f8be175SNeel Natu * 7824f8be175SNeel Natu * 'x2apic_dest' specifies whether 'dest' is interpreted as x2APIC (32-bit) 7834f8be175SNeel Natu * or xAPIC (8-bit) destination field. 7844f8be175SNeel Natu */ 7854f8be175SNeel Natu static void 7864f8be175SNeel Natu vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys, 7874f8be175SNeel Natu bool lowprio, bool x2apic_dest) 7884f8be175SNeel Natu { 7894f8be175SNeel Natu struct vlapic *vlapic; 7904f8be175SNeel Natu uint32_t dfr, ldr, ldest, cluster; 7914f8be175SNeel Natu uint32_t mda_flat_ldest, mda_cluster_ldest, mda_ldest, mda_cluster_id; 7924f8be175SNeel Natu cpuset_t amask; 7934f8be175SNeel Natu int vcpuid; 7944f8be175SNeel Natu 7954f8be175SNeel Natu if ((x2apic_dest && dest == 0xffffffff) || 7964f8be175SNeel Natu (!x2apic_dest && dest == 0xff)) { 7974f8be175SNeel Natu /* 7984f8be175SNeel Natu * Broadcast in both logical and physical modes. 7994f8be175SNeel Natu */ 8004f8be175SNeel Natu *dmask = vm_active_cpus(vm); 8014f8be175SNeel Natu return; 8024f8be175SNeel Natu } 8034f8be175SNeel Natu 8044f8be175SNeel Natu if (phys) { 8054f8be175SNeel Natu /* 8064f8be175SNeel Natu * Physical mode: destination is APIC ID. 8074f8be175SNeel Natu */ 8084f8be175SNeel Natu CPU_ZERO(dmask); 8094f8be175SNeel Natu vcpuid = vm_apicid2vcpuid(vm, dest); 8104f8be175SNeel Natu if (vcpuid < VM_MAXCPU) 8114f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8124f8be175SNeel Natu } else { 8134f8be175SNeel Natu /* 8144f8be175SNeel Natu * In the "Flat Model" the MDA is interpreted as an 8-bit wide 8154f8be175SNeel Natu * bitmask. This model is only avilable in the xAPIC mode. 8164f8be175SNeel Natu */ 8174f8be175SNeel Natu mda_flat_ldest = dest & 0xff; 8184f8be175SNeel Natu 8194f8be175SNeel Natu /* 8204f8be175SNeel Natu * In the "Cluster Model" the MDA is used to identify a 8214f8be175SNeel Natu * specific cluster and a set of APICs in that cluster. 8224f8be175SNeel Natu */ 8234f8be175SNeel Natu if (x2apic_dest) { 8244f8be175SNeel Natu mda_cluster_id = dest >> 16; 8254f8be175SNeel Natu mda_cluster_ldest = dest & 0xffff; 8264f8be175SNeel Natu } else { 8274f8be175SNeel Natu mda_cluster_id = (dest >> 4) & 0xf; 8284f8be175SNeel Natu mda_cluster_ldest = dest & 0xf; 8294f8be175SNeel Natu } 8304f8be175SNeel Natu 8314f8be175SNeel Natu /* 8324f8be175SNeel Natu * Logical mode: match each APIC that has a bit set 8334f8be175SNeel Natu * in it's LDR that matches a bit in the ldest. 8344f8be175SNeel Natu */ 8354f8be175SNeel Natu CPU_ZERO(dmask); 8364f8be175SNeel Natu amask = vm_active_cpus(vm); 8374f8be175SNeel Natu while ((vcpuid = CPU_FFS(&amask)) != 0) { 8384f8be175SNeel Natu vcpuid--; 8394f8be175SNeel Natu CPU_CLR(vcpuid, &amask); 8404f8be175SNeel Natu 8414f8be175SNeel Natu vlapic = vm_lapic(vm, vcpuid); 8423f0ddc7cSNeel Natu dfr = vlapic->apic_page->dfr; 8433f0ddc7cSNeel Natu ldr = vlapic->apic_page->ldr; 8444f8be175SNeel Natu 8454f8be175SNeel Natu if ((dfr & APIC_DFR_MODEL_MASK) == 8464f8be175SNeel Natu APIC_DFR_MODEL_FLAT) { 8474f8be175SNeel Natu ldest = ldr >> 24; 8484f8be175SNeel Natu mda_ldest = mda_flat_ldest; 8494f8be175SNeel Natu } else if ((dfr & APIC_DFR_MODEL_MASK) == 8504f8be175SNeel Natu APIC_DFR_MODEL_CLUSTER) { 8514f8be175SNeel Natu if (x2apic(vlapic)) { 8524f8be175SNeel Natu cluster = ldr >> 16; 8534f8be175SNeel Natu ldest = ldr & 0xffff; 8544f8be175SNeel Natu } else { 8554f8be175SNeel Natu cluster = ldr >> 28; 8564f8be175SNeel Natu ldest = (ldr >> 24) & 0xf; 8574f8be175SNeel Natu } 8584f8be175SNeel Natu if (cluster != mda_cluster_id) 8594f8be175SNeel Natu continue; 8604f8be175SNeel Natu mda_ldest = mda_cluster_ldest; 8614f8be175SNeel Natu } else { 8624f8be175SNeel Natu /* 8634f8be175SNeel Natu * Guest has configured a bad logical 8644f8be175SNeel Natu * model for this vcpu - skip it. 8654f8be175SNeel Natu */ 8664f8be175SNeel Natu VLAPIC_CTR1(vlapic, "vlapic has bad logical " 8674f8be175SNeel Natu "model %x - cannot deliver interrupt", dfr); 8684f8be175SNeel Natu continue; 8694f8be175SNeel Natu } 8704f8be175SNeel Natu 8714f8be175SNeel Natu if ((mda_ldest & ldest) != 0) { 8724f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8734f8be175SNeel Natu if (lowprio) 8744f8be175SNeel Natu break; 8754f8be175SNeel Natu } 8764f8be175SNeel Natu } 8774f8be175SNeel Natu } 8784f8be175SNeel Natu } 8794f8be175SNeel Natu 8800acb0d84SNeel Natu static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu"); 8810acb0d84SNeel Natu 882fafe8844SNeel Natu int 883fafe8844SNeel Natu vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu) 884366f6083SPeter Grehan { 885366f6083SPeter Grehan int i; 8864f8be175SNeel Natu bool phys; 887a5615c90SPeter Grehan cpuset_t dmask; 888fafe8844SNeel Natu uint64_t icrval; 889366f6083SPeter Grehan uint32_t dest, vec, mode; 890edf89256SNeel Natu struct vlapic *vlapic2; 891edf89256SNeel Natu struct vm_exit *vmexit; 892fafe8844SNeel Natu struct LAPIC *lapic; 893fafe8844SNeel Natu 894fafe8844SNeel Natu lapic = vlapic->apic_page; 895fafe8844SNeel Natu lapic->icr_lo &= ~APIC_DELSTAT_PEND; 896fafe8844SNeel Natu icrval = ((uint64_t)lapic->icr_hi << 32) | lapic->icr_lo; 897366f6083SPeter Grehan 898a2da7af6SNeel Natu if (x2apic(vlapic)) 899366f6083SPeter Grehan dest = icrval >> 32; 900a2da7af6SNeel Natu else 901a2da7af6SNeel Natu dest = icrval >> (32 + 24); 902366f6083SPeter Grehan vec = icrval & APIC_VECTOR_MASK; 903366f6083SPeter Grehan mode = icrval & APIC_DELMODE_MASK; 904366f6083SPeter Grehan 905330baf58SJohn Baldwin if (mode == APIC_DELMODE_FIXED && vec < 16) { 906330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 9074d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec); 908330baf58SJohn Baldwin return (0); 909330baf58SJohn Baldwin } 910330baf58SJohn Baldwin 9114d1e82a8SNeel Natu VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec); 9124d1e82a8SNeel Natu 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); 9424d1e82a8SNeel Natu VLAPIC_CTR2(vlapic, "vlapic sending ipi %d " 9434d1e82a8SNeel Natu "to vcpuid %d", vec, i); 9444d1e82a8SNeel Natu } else { 945366f6083SPeter Grehan vm_inject_nmi(vlapic->vm, i); 9464d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "vlapic sending ipi nmi " 9474d1e82a8SNeel Natu "to vcpuid %d", i); 9484d1e82a8SNeel Natu } 949366f6083SPeter Grehan } 950366f6083SPeter Grehan 951366f6083SPeter Grehan return (0); /* handled completely in the kernel */ 952366f6083SPeter Grehan } 953366f6083SPeter Grehan 954edf89256SNeel Natu if (mode == APIC_DELMODE_INIT) { 955edf89256SNeel Natu if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) 956edf89256SNeel Natu return (0); 957edf89256SNeel Natu 958edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 959edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 960edf89256SNeel Natu 961edf89256SNeel Natu /* move from INIT to waiting-for-SIPI state */ 962edf89256SNeel Natu if (vlapic2->boot_state == BS_INIT) { 963edf89256SNeel Natu vlapic2->boot_state = BS_SIPI; 964edf89256SNeel Natu } 965edf89256SNeel Natu 966edf89256SNeel Natu return (0); 967edf89256SNeel Natu } 968edf89256SNeel Natu } 969edf89256SNeel Natu 970edf89256SNeel Natu if (mode == APIC_DELMODE_STARTUP) { 971edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 972edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 973edf89256SNeel Natu 974edf89256SNeel Natu /* 975edf89256SNeel Natu * Ignore SIPIs in any state other than wait-for-SIPI 976edf89256SNeel Natu */ 977edf89256SNeel Natu if (vlapic2->boot_state != BS_SIPI) 978edf89256SNeel Natu return (0); 979edf89256SNeel Natu 980366f6083SPeter Grehan /* 981366f6083SPeter Grehan * XXX this assumes that the startup IPI always succeeds 982366f6083SPeter Grehan */ 983edf89256SNeel Natu vlapic2->boot_state = BS_RUNNING; 984edf89256SNeel Natu vm_activate_cpu(vlapic2->vm, dest); 985edf89256SNeel Natu 986becd9849SNeel Natu *retu = true; 987becd9849SNeel Natu vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); 988becd9849SNeel Natu vmexit->exitcode = VM_EXITCODE_SPINUP_AP; 989becd9849SNeel Natu vmexit->u.spinup_ap.vcpu = dest; 990becd9849SNeel Natu vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT; 991becd9849SNeel Natu 992edf89256SNeel Natu return (0); 993edf89256SNeel Natu } 994edf89256SNeel Natu } 995366f6083SPeter Grehan 996366f6083SPeter Grehan /* 997366f6083SPeter Grehan * This will cause a return to userland. 998366f6083SPeter Grehan */ 999366f6083SPeter Grehan return (1); 1000366f6083SPeter Grehan } 1001366f6083SPeter Grehan 1002366f6083SPeter Grehan int 10034d1e82a8SNeel Natu vlapic_pending_intr(struct vlapic *vlapic, int *vecptr) 1004366f6083SPeter Grehan { 1005de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1006366f6083SPeter Grehan int idx, i, bitpos, vector; 1007366f6083SPeter Grehan uint32_t *irrptr, val; 1008366f6083SPeter Grehan 1009*88c4b8d1SNeel Natu if (vlapic->ops.pending_intr) 1010*88c4b8d1SNeel Natu return ((*vlapic->ops.pending_intr)(vlapic, vecptr)); 1011*88c4b8d1SNeel Natu 1012366f6083SPeter Grehan irrptr = &lapic->irr0; 1013366f6083SPeter Grehan 1014366f6083SPeter Grehan /* 1015366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 1016366f6083SPeter Grehan * by the processor. 1017366f6083SPeter Grehan */ 1018366f6083SPeter Grehan for (i = 7; i > 0; i--) { 1019366f6083SPeter Grehan idx = i * 4; 1020366f6083SPeter Grehan val = atomic_load_acq_int(&irrptr[idx]); 1021366f6083SPeter Grehan bitpos = fls(val); 1022366f6083SPeter Grehan if (bitpos != 0) { 1023366f6083SPeter Grehan vector = i * 32 + (bitpos - 1); 1024366f6083SPeter Grehan if (PRIO(vector) > PRIO(lapic->ppr)) { 1025366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "pending intr %d", vector); 10264d1e82a8SNeel Natu if (vecptr != NULL) 10274d1e82a8SNeel Natu *vecptr = vector; 10284d1e82a8SNeel Natu return (1); 1029366f6083SPeter Grehan } else 1030366f6083SPeter Grehan break; 1031366f6083SPeter Grehan } 1032366f6083SPeter Grehan } 10334d1e82a8SNeel Natu return (0); 1034366f6083SPeter Grehan } 1035366f6083SPeter Grehan 1036366f6083SPeter Grehan void 1037366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector) 1038366f6083SPeter Grehan { 1039de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1040366f6083SPeter Grehan uint32_t *irrptr, *isrptr; 1041366f6083SPeter Grehan int idx, stk_top; 1042366f6083SPeter Grehan 1043*88c4b8d1SNeel Natu if (vlapic->ops.intr_accepted) 1044*88c4b8d1SNeel Natu return ((*vlapic->ops.intr_accepted)(vlapic, vector)); 1045*88c4b8d1SNeel Natu 1046366f6083SPeter Grehan /* 1047366f6083SPeter Grehan * clear the ready bit for vector being accepted in irr 1048366f6083SPeter Grehan * and set the vector as in service in isr. 1049366f6083SPeter Grehan */ 1050366f6083SPeter Grehan idx = (vector / 32) * 4; 1051366f6083SPeter Grehan 1052366f6083SPeter Grehan irrptr = &lapic->irr0; 1053366f6083SPeter Grehan atomic_clear_int(&irrptr[idx], 1 << (vector % 32)); 1054366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted"); 1055366f6083SPeter Grehan 1056366f6083SPeter Grehan isrptr = &lapic->isr0; 1057366f6083SPeter Grehan isrptr[idx] |= 1 << (vector % 32); 1058366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted"); 1059366f6083SPeter Grehan 1060366f6083SPeter Grehan /* 1061366f6083SPeter Grehan * Update the PPR 1062366f6083SPeter Grehan */ 1063366f6083SPeter Grehan vlapic->isrvec_stk_top++; 1064366f6083SPeter Grehan 1065366f6083SPeter Grehan stk_top = vlapic->isrvec_stk_top; 1066366f6083SPeter Grehan if (stk_top >= ISRVEC_STK_SIZE) 1067366f6083SPeter Grehan panic("isrvec_stk_top overflow %d", stk_top); 1068366f6083SPeter Grehan 1069366f6083SPeter Grehan vlapic->isrvec_stk[stk_top] = vector; 1070366f6083SPeter Grehan vlapic_update_ppr(vlapic); 1071366f6083SPeter Grehan } 1072366f6083SPeter Grehan 10732c52dcd9SNeel Natu void 10742c52dcd9SNeel Natu vlapic_svr_write_handler(struct vlapic *vlapic) 10751c052192SNeel Natu { 10761c052192SNeel Natu struct LAPIC *lapic; 10772c52dcd9SNeel Natu uint32_t old, new, changed; 10781c052192SNeel Natu 1079de5ea6b6SNeel Natu lapic = vlapic->apic_page; 10802c52dcd9SNeel Natu 10812c52dcd9SNeel Natu new = lapic->svr; 10822c52dcd9SNeel Natu old = vlapic->svr_last; 10832c52dcd9SNeel Natu vlapic->svr_last = new; 10842c52dcd9SNeel Natu 10851c052192SNeel Natu changed = old ^ new; 10861c052192SNeel Natu if ((changed & APIC_SVR_ENABLE) != 0) { 10871c052192SNeel Natu if ((new & APIC_SVR_ENABLE) == 0) { 1088fb03ca4eSNeel Natu /* 10892c52dcd9SNeel Natu * The apic is now disabled so stop the apic timer 10902c52dcd9SNeel Natu * and mask all the LVT entries. 1091fb03ca4eSNeel Natu */ 10921c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-disabled"); 1093fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 1094fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 1095fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 10962c52dcd9SNeel Natu vlapic_mask_lvts(vlapic); 10971c052192SNeel Natu } else { 1098fb03ca4eSNeel Natu /* 1099fb03ca4eSNeel Natu * The apic is now enabled so restart the apic timer 1100fb03ca4eSNeel Natu * if it is configured in periodic mode. 1101fb03ca4eSNeel Natu */ 11021c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-enabled"); 1103fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) 1104fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 11051c052192SNeel Natu } 11061c052192SNeel Natu } 11071c052192SNeel Natu } 11081c052192SNeel Natu 1109366f6083SPeter Grehan int 1110becd9849SNeel Natu vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu) 1111366f6083SPeter Grehan { 1112de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1113366f6083SPeter Grehan uint32_t *reg; 1114366f6083SPeter Grehan int i; 1115366f6083SPeter Grehan 1116366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1117366f6083SPeter Grehan *data = 0; 11181c052192SNeel Natu goto done; 1119366f6083SPeter Grehan } 1120366f6083SPeter Grehan 1121366f6083SPeter Grehan offset &= ~3; 1122366f6083SPeter Grehan switch(offset) 1123366f6083SPeter Grehan { 1124366f6083SPeter Grehan case APIC_OFFSET_ID: 11253f0ddc7cSNeel Natu *data = lapic->id; 1126366f6083SPeter Grehan break; 1127366f6083SPeter Grehan case APIC_OFFSET_VER: 1128366f6083SPeter Grehan *data = lapic->version; 1129366f6083SPeter Grehan break; 1130366f6083SPeter Grehan case APIC_OFFSET_TPR: 1131366f6083SPeter Grehan *data = lapic->tpr; 1132366f6083SPeter Grehan break; 1133366f6083SPeter Grehan case APIC_OFFSET_APR: 1134366f6083SPeter Grehan *data = lapic->apr; 1135366f6083SPeter Grehan break; 1136366f6083SPeter Grehan case APIC_OFFSET_PPR: 1137366f6083SPeter Grehan *data = lapic->ppr; 1138366f6083SPeter Grehan break; 1139366f6083SPeter Grehan case APIC_OFFSET_EOI: 1140366f6083SPeter Grehan *data = lapic->eoi; 1141366f6083SPeter Grehan break; 1142366f6083SPeter Grehan case APIC_OFFSET_LDR: 11433f0ddc7cSNeel Natu *data = lapic->ldr; 1144366f6083SPeter Grehan break; 1145366f6083SPeter Grehan case APIC_OFFSET_DFR: 11463f0ddc7cSNeel Natu *data = lapic->dfr; 1147366f6083SPeter Grehan break; 1148366f6083SPeter Grehan case APIC_OFFSET_SVR: 1149366f6083SPeter Grehan *data = lapic->svr; 1150366f6083SPeter Grehan break; 1151366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1152366f6083SPeter Grehan i = (offset - APIC_OFFSET_ISR0) >> 2; 1153366f6083SPeter Grehan reg = &lapic->isr0; 1154366f6083SPeter Grehan *data = *(reg + i); 1155366f6083SPeter Grehan break; 1156366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1157366f6083SPeter Grehan i = (offset - APIC_OFFSET_TMR0) >> 2; 1158366f6083SPeter Grehan reg = &lapic->tmr0; 1159366f6083SPeter Grehan *data = *(reg + i); 1160366f6083SPeter Grehan break; 1161366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1162366f6083SPeter Grehan i = (offset - APIC_OFFSET_IRR0) >> 2; 1163366f6083SPeter Grehan reg = &lapic->irr0; 1164366f6083SPeter Grehan *data = atomic_load_acq_int(reg + i); 1165366f6083SPeter Grehan break; 1166366f6083SPeter Grehan case APIC_OFFSET_ESR: 1167366f6083SPeter Grehan *data = lapic->esr; 1168366f6083SPeter Grehan break; 1169366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1170366f6083SPeter Grehan *data = lapic->icr_lo; 1171fafe8844SNeel Natu if (x2apic(vlapic)) 1172fafe8844SNeel Natu *data |= (uint64_t)lapic->icr_hi << 32; 1173366f6083SPeter Grehan break; 1174366f6083SPeter Grehan case APIC_OFFSET_ICR_HI: 1175366f6083SPeter Grehan *data = lapic->icr_hi; 1176366f6083SPeter Grehan break; 1177330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1178366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 1179fb03ca4eSNeel Natu *data = vlapic_get_lvt(vlapic, offset); 11807c05bc31SNeel Natu #ifdef INVARIANTS 11817c05bc31SNeel Natu reg = vlapic_get_lvtptr(vlapic, offset); 11827c05bc31SNeel Natu KASSERT(*data == *reg, ("inconsistent lvt value at " 11837c05bc31SNeel Natu "offset %#lx: %#lx/%#x", offset, *data, *reg)); 11847c05bc31SNeel Natu #endif 1185366f6083SPeter Grehan break; 1186de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1187366f6083SPeter Grehan *data = lapic->icr_timer; 1188366f6083SPeter Grehan break; 1189de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1190366f6083SPeter Grehan *data = vlapic_get_ccr(vlapic); 1191366f6083SPeter Grehan break; 1192de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1193366f6083SPeter Grehan *data = lapic->dcr_timer; 1194366f6083SPeter Grehan break; 1195366f6083SPeter Grehan case APIC_OFFSET_RRR: 1196366f6083SPeter Grehan default: 1197366f6083SPeter Grehan *data = 0; 1198366f6083SPeter Grehan break; 1199366f6083SPeter Grehan } 12001c052192SNeel Natu done: 12011c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic read offset %#x, data %#lx", offset, *data); 1202366f6083SPeter Grehan return 0; 1203366f6083SPeter Grehan } 1204366f6083SPeter Grehan 1205366f6083SPeter Grehan int 1206becd9849SNeel Natu vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu) 1207366f6083SPeter Grehan { 1208de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 12097c05bc31SNeel Natu uint32_t *regptr; 1210366f6083SPeter Grehan int retval; 1211366f6083SPeter Grehan 12123f0ddc7cSNeel Natu KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE, 12133f0ddc7cSNeel Natu ("vlapic_write: invalid offset %#lx", offset)); 12143f0ddc7cSNeel Natu 12151c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic write offset %#x, data %#lx", offset, data); 12161c052192SNeel Natu 1217366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1218366f6083SPeter Grehan return 0; 1219366f6083SPeter Grehan } 1220366f6083SPeter Grehan 1221366f6083SPeter Grehan retval = 0; 1222366f6083SPeter Grehan switch(offset) 1223366f6083SPeter Grehan { 1224366f6083SPeter Grehan case APIC_OFFSET_ID: 12253f0ddc7cSNeel Natu lapic->id = data; 12263f0ddc7cSNeel Natu vlapic_id_write_handler(vlapic); 1227366f6083SPeter Grehan break; 1228366f6083SPeter Grehan case APIC_OFFSET_TPR: 1229366f6083SPeter Grehan lapic->tpr = data & 0xff; 1230366f6083SPeter Grehan vlapic_update_ppr(vlapic); 1231366f6083SPeter Grehan break; 1232366f6083SPeter Grehan case APIC_OFFSET_EOI: 1233366f6083SPeter Grehan vlapic_process_eoi(vlapic); 1234366f6083SPeter Grehan break; 1235366f6083SPeter Grehan case APIC_OFFSET_LDR: 12363f0ddc7cSNeel Natu lapic->ldr = data; 12373f0ddc7cSNeel Natu vlapic_ldr_write_handler(vlapic); 1238366f6083SPeter Grehan break; 1239366f6083SPeter Grehan case APIC_OFFSET_DFR: 12403f0ddc7cSNeel Natu lapic->dfr = data; 12413f0ddc7cSNeel Natu vlapic_dfr_write_handler(vlapic); 1242366f6083SPeter Grehan break; 1243366f6083SPeter Grehan case APIC_OFFSET_SVR: 12442c52dcd9SNeel Natu lapic->svr = data; 12452c52dcd9SNeel Natu vlapic_svr_write_handler(vlapic); 1246366f6083SPeter Grehan break; 1247366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1248fafe8844SNeel Natu lapic->icr_lo = data; 1249fafe8844SNeel Natu if (x2apic(vlapic)) 1250fafe8844SNeel Natu lapic->icr_hi = data >> 32; 1251fafe8844SNeel Natu retval = vlapic_icrlo_write_handler(vlapic, retu); 1252366f6083SPeter Grehan break; 1253a2da7af6SNeel Natu case APIC_OFFSET_ICR_HI: 1254a2da7af6SNeel Natu lapic->icr_hi = data; 1255a2da7af6SNeel Natu break; 1256330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1257366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 12587c05bc31SNeel Natu regptr = vlapic_get_lvtptr(vlapic, offset); 12597c05bc31SNeel Natu *regptr = data; 12607c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, offset); 1261366f6083SPeter Grehan break; 1262de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1263fafe8844SNeel Natu lapic->icr_timer = data; 1264fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 1265366f6083SPeter Grehan break; 1266366f6083SPeter Grehan 1267de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1268fafe8844SNeel Natu lapic->dcr_timer = data; 1269fafe8844SNeel Natu vlapic_dcr_write_handler(vlapic); 1270366f6083SPeter Grehan break; 1271366f6083SPeter Grehan 1272366f6083SPeter Grehan case APIC_OFFSET_ESR: 1273fafe8844SNeel Natu vlapic_esr_write_handler(vlapic); 1274366f6083SPeter Grehan break; 1275366f6083SPeter Grehan case APIC_OFFSET_VER: 1276366f6083SPeter Grehan case APIC_OFFSET_APR: 1277366f6083SPeter Grehan case APIC_OFFSET_PPR: 1278366f6083SPeter Grehan case APIC_OFFSET_RRR: 1279366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1280366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1281366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1282de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1283366f6083SPeter Grehan default: 1284366f6083SPeter Grehan // Read only. 1285366f6083SPeter Grehan break; 1286366f6083SPeter Grehan } 1287366f6083SPeter Grehan 1288366f6083SPeter Grehan return (retval); 1289366f6083SPeter Grehan } 1290366f6083SPeter Grehan 12917c05bc31SNeel Natu static void 12927c05bc31SNeel Natu vlapic_reset(struct vlapic *vlapic) 12937c05bc31SNeel Natu { 12947c05bc31SNeel Natu struct LAPIC *lapic; 12957c05bc31SNeel Natu 12967c05bc31SNeel Natu lapic = vlapic->apic_page; 12977c05bc31SNeel Natu bzero(lapic, sizeof(struct LAPIC)); 12987c05bc31SNeel Natu 12997c05bc31SNeel Natu lapic->id = vlapic_get_id(vlapic); 13007c05bc31SNeel Natu lapic->version = VLAPIC_VERSION; 13017c05bc31SNeel Natu lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT); 13027c05bc31SNeel Natu lapic->dfr = 0xffffffff; 13037c05bc31SNeel Natu lapic->svr = APIC_SVR_VECTOR; 13047c05bc31SNeel Natu vlapic_mask_lvts(vlapic); 13057c05bc31SNeel Natu 13067c05bc31SNeel Natu lapic->dcr_timer = 0; 13077c05bc31SNeel Natu vlapic_dcr_write_handler(vlapic); 13087c05bc31SNeel Natu 13097c05bc31SNeel Natu if (vlapic->vcpuid == 0) 13107c05bc31SNeel Natu vlapic->boot_state = BS_RUNNING; /* BSP */ 13117c05bc31SNeel Natu else 13127c05bc31SNeel Natu vlapic->boot_state = BS_INIT; /* AP */ 13137c05bc31SNeel Natu 13147c05bc31SNeel Natu vlapic->svr_last = lapic->svr; 13157c05bc31SNeel Natu } 13167c05bc31SNeel Natu 1317de5ea6b6SNeel Natu void 1318de5ea6b6SNeel Natu vlapic_init(struct vlapic *vlapic) 1319366f6083SPeter Grehan { 1320de5ea6b6SNeel Natu KASSERT(vlapic->vm != NULL, ("vlapic_init: vm is not initialized")); 1321de5ea6b6SNeel Natu KASSERT(vlapic->vcpuid >= 0 && vlapic->vcpuid < VM_MAXCPU, 1322de5ea6b6SNeel Natu ("vlapic_init: vcpuid is not initialized")); 1323de5ea6b6SNeel Natu KASSERT(vlapic->apic_page != NULL, ("vlapic_init: apic_page is not " 1324de5ea6b6SNeel Natu "initialized")); 13252d3a73edSNeel Natu 1326becd9849SNeel Natu /* 1327becd9849SNeel Natu * If the vlapic is configured in x2apic mode then it will be 1328becd9849SNeel Natu * accessed in the critical section via the MSR emulation code. 1329becd9849SNeel Natu * 1330becd9849SNeel Natu * Therefore the timer mutex must be a spinlock because blockable 1331becd9849SNeel Natu * mutexes cannot be acquired in a critical section. 1332becd9849SNeel Natu */ 1333becd9849SNeel Natu mtx_init(&vlapic->timer_mtx, "vlapic timer mtx", NULL, MTX_SPIN); 1334fb03ca4eSNeel Natu callout_init(&vlapic->callout, 1); 1335fb03ca4eSNeel Natu 1336a2da7af6SNeel Natu vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; 13372d3a73edSNeel Natu 1338de5ea6b6SNeel Natu if (vlapic->vcpuid == 0) 13392d3a73edSNeel Natu vlapic->msr_apicbase |= APICBASE_BSP; 13402d3a73edSNeel Natu 134103cd0501SNeel Natu vlapic_reset(vlapic); 1342366f6083SPeter Grehan } 1343366f6083SPeter Grehan 1344366f6083SPeter Grehan void 1345366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic) 1346366f6083SPeter Grehan { 134703cd0501SNeel Natu 1348fb03ca4eSNeel Natu callout_drain(&vlapic->callout); 1349366f6083SPeter Grehan } 13502d3a73edSNeel Natu 13512d3a73edSNeel Natu uint64_t 13522d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic) 13532d3a73edSNeel Natu { 13542d3a73edSNeel Natu 13552d3a73edSNeel Natu return (vlapic->msr_apicbase); 13562d3a73edSNeel Natu } 13572d3a73edSNeel Natu 13582d3a73edSNeel Natu void 13593f0ddc7cSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t new) 13602d3a73edSNeel Natu { 13613f0ddc7cSNeel Natu struct LAPIC *lapic; 1362a2da7af6SNeel Natu enum x2apic_state state; 13633f0ddc7cSNeel Natu uint64_t old; 13643f0ddc7cSNeel Natu int err; 1365a2da7af6SNeel Natu 1366a2da7af6SNeel Natu err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state); 1367a2da7af6SNeel Natu if (err) 1368a2da7af6SNeel Natu panic("vlapic_set_apicbase: err %d fetching x2apic state", err); 1369a2da7af6SNeel Natu 1370a2da7af6SNeel Natu if (state == X2APIC_DISABLED) 13713f0ddc7cSNeel Natu new &= ~APICBASE_X2APIC; 13722d3a73edSNeel Natu 13733f0ddc7cSNeel Natu old = vlapic->msr_apicbase; 13743f0ddc7cSNeel Natu vlapic->msr_apicbase = new; 13753f0ddc7cSNeel Natu 13763f0ddc7cSNeel Natu /* 13773f0ddc7cSNeel Natu * If the vlapic is switching between xAPIC and x2APIC modes then 13783f0ddc7cSNeel Natu * reset the mode-dependent registers. 13793f0ddc7cSNeel Natu */ 13803f0ddc7cSNeel Natu if ((old ^ new) & APICBASE_X2APIC) { 13813f0ddc7cSNeel Natu lapic = vlapic->apic_page; 13823f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 13833f0ddc7cSNeel Natu if (x2apic(vlapic)) { 13843f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 13853f0ddc7cSNeel Natu lapic->dfr = 0; 13863f0ddc7cSNeel Natu } else { 13873f0ddc7cSNeel Natu lapic->ldr = 0; 13883f0ddc7cSNeel Natu lapic->dfr = 0xffffffff; 13893f0ddc7cSNeel Natu } 13903f0ddc7cSNeel Natu } 13912d3a73edSNeel Natu } 139273820fb0SNeel Natu 139373820fb0SNeel Natu void 139473820fb0SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 139573820fb0SNeel Natu { 139673820fb0SNeel Natu struct vlapic *vlapic; 139773820fb0SNeel Natu 139873820fb0SNeel Natu vlapic = vm_lapic(vm, vcpuid); 139973820fb0SNeel Natu 1400485f986aSNeel Natu if (state == X2APIC_DISABLED) 140173820fb0SNeel Natu vlapic->msr_apicbase &= ~APICBASE_X2APIC; 140273820fb0SNeel Natu } 14031c052192SNeel Natu 14044f8be175SNeel Natu void 14054f8be175SNeel Natu vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, 14064f8be175SNeel Natu int delmode, int vec) 14074f8be175SNeel Natu { 14084f8be175SNeel Natu bool lowprio; 14094f8be175SNeel Natu int vcpuid; 14104f8be175SNeel Natu cpuset_t dmask; 14114f8be175SNeel Natu 14124f8be175SNeel Natu if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) { 14134f8be175SNeel Natu VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode); 14144f8be175SNeel Natu return; 14154f8be175SNeel Natu } 14164f8be175SNeel Natu lowprio = (delmode == APIC_DELMODE_LOWPRIO); 14174f8be175SNeel Natu 14184f8be175SNeel Natu /* 14194f8be175SNeel Natu * We don't provide any virtual interrupt redirection hardware so 14204f8be175SNeel Natu * all interrupts originating from the ioapic or MSI specify the 14214f8be175SNeel Natu * 'dest' in the legacy xAPIC format. 14224f8be175SNeel Natu */ 14234f8be175SNeel Natu vlapic_calcdest(vm, &dmask, dest, phys, lowprio, false); 14244f8be175SNeel Natu 14254f8be175SNeel Natu while ((vcpuid = CPU_FFS(&dmask)) != 0) { 14264f8be175SNeel Natu vcpuid--; 14274f8be175SNeel Natu CPU_CLR(vcpuid, &dmask); 14284f8be175SNeel Natu lapic_set_intr(vm, vcpuid, vec, level); 14294f8be175SNeel Natu } 14304f8be175SNeel Natu } 14314f8be175SNeel Natu 1432de5ea6b6SNeel Natu void 1433de5ea6b6SNeel Natu vlapic_post_intr(struct vlapic *vlapic, int hostcpu) 1434de5ea6b6SNeel Natu { 1435de5ea6b6SNeel Natu /* 1436de5ea6b6SNeel Natu * Post an interrupt to the vcpu currently running on 'hostcpu'. 1437de5ea6b6SNeel Natu * 1438de5ea6b6SNeel Natu * This is done by leveraging features like Posted Interrupts (Intel) 1439de5ea6b6SNeel Natu * Doorbell MSR (AMD AVIC) that avoid a VM exit. 1440de5ea6b6SNeel Natu * 1441de5ea6b6SNeel Natu * If neither of these features are available then fallback to 1442de5ea6b6SNeel Natu * sending an IPI to 'hostcpu'. 1443de5ea6b6SNeel Natu */ 1444*88c4b8d1SNeel Natu if (vlapic->ops.post_intr) 1445*88c4b8d1SNeel Natu (*vlapic->ops.post_intr)(vlapic, hostcpu); 1446*88c4b8d1SNeel Natu else 1447de5ea6b6SNeel Natu ipi_cpu(hostcpu, vmm_ipinum); 1448de5ea6b6SNeel Natu } 1449de5ea6b6SNeel Natu 14501c052192SNeel Natu bool 14511c052192SNeel Natu vlapic_enabled(struct vlapic *vlapic) 14521c052192SNeel Natu { 1453de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 14541c052192SNeel Natu 14551c052192SNeel Natu if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && 14561c052192SNeel Natu (lapic->svr & APIC_SVR_ENABLE) != 0) 14571c052192SNeel Natu return (true); 14581c052192SNeel Natu else 14591c052192SNeel Natu return (false); 14601c052192SNeel Natu } 1461