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 73c5d216b7SNeel Natu /* 74c5d216b7SNeel Natu * APIC timer frequency: 75c5d216b7SNeel Natu * - arbitrary but chosen to be in the ballpark of contemporary hardware. 76c5d216b7SNeel Natu * - power-of-two to avoid loss of precision when converted to a bintime. 77c5d216b7SNeel Natu */ 78c5d216b7SNeel Natu #define VLAPIC_BUS_FREQ (128 * 1024 * 1024) 792e25737aSNeel Natu 804f8be175SNeel Natu static __inline uint32_t 814f8be175SNeel Natu vlapic_get_id(struct vlapic *vlapic) 824f8be175SNeel Natu { 834f8be175SNeel Natu 844f8be175SNeel Natu if (x2apic(vlapic)) 854f8be175SNeel Natu return (vlapic->vcpuid); 864f8be175SNeel Natu else 874f8be175SNeel Natu return (vlapic->vcpuid << 24); 884f8be175SNeel Natu } 894f8be175SNeel Natu 903f0ddc7cSNeel Natu static uint32_t 913f0ddc7cSNeel Natu x2apic_ldr(struct vlapic *vlapic) 924f8be175SNeel Natu { 934f8be175SNeel Natu int apicid; 944f8be175SNeel Natu uint32_t ldr; 954f8be175SNeel Natu 964f8be175SNeel Natu apicid = vlapic_get_id(vlapic); 974f8be175SNeel Natu ldr = 1 << (apicid & 0xf); 984f8be175SNeel Natu ldr |= (apicid & 0xffff0) << 12; 994f8be175SNeel Natu return (ldr); 1004f8be175SNeel Natu } 1014f8be175SNeel Natu 1023f0ddc7cSNeel Natu void 1033f0ddc7cSNeel Natu vlapic_dfr_write_handler(struct vlapic *vlapic) 1044f8be175SNeel Natu { 1054f8be175SNeel Natu struct LAPIC *lapic; 1064f8be175SNeel Natu 107de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1084f8be175SNeel Natu if (x2apic(vlapic)) { 1093f0ddc7cSNeel Natu VM_CTR1(vlapic->vm, "ignoring write to DFR in x2apic mode: %#x", 1103f0ddc7cSNeel Natu lapic->dfr); 1113f0ddc7cSNeel Natu lapic->dfr = 0; 1124f8be175SNeel Natu return; 1134f8be175SNeel Natu } 1144f8be175SNeel Natu 1153f0ddc7cSNeel Natu lapic->dfr &= APIC_DFR_MODEL_MASK; 1163f0ddc7cSNeel Natu lapic->dfr |= APIC_DFR_RESERVED; 1173f0ddc7cSNeel Natu 1183f0ddc7cSNeel Natu if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT) 1194f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model"); 1203f0ddc7cSNeel Natu else if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER) 1214f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model"); 1224f8be175SNeel Natu else 1233f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "DFR in Unknown Model %#x", lapic->dfr); 1244f8be175SNeel Natu } 1254f8be175SNeel Natu 1263f0ddc7cSNeel Natu void 1273f0ddc7cSNeel Natu vlapic_ldr_write_handler(struct vlapic *vlapic) 1284f8be175SNeel Natu { 1294f8be175SNeel Natu struct LAPIC *lapic; 1304f8be175SNeel Natu 1313f0ddc7cSNeel Natu lapic = vlapic->apic_page; 1323f0ddc7cSNeel Natu 1334f8be175SNeel Natu /* LDR is read-only in x2apic mode */ 1344f8be175SNeel Natu if (x2apic(vlapic)) { 1353f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "ignoring write to LDR in x2apic mode: %#x", 1363f0ddc7cSNeel Natu lapic->ldr); 1373f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 1383f0ddc7cSNeel Natu } else { 1393f0ddc7cSNeel Natu lapic->ldr &= ~APIC_LDR_RESERVED; 1403f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr); 1413f0ddc7cSNeel Natu } 1424f8be175SNeel Natu } 1434f8be175SNeel Natu 1443f0ddc7cSNeel Natu void 1453f0ddc7cSNeel Natu vlapic_id_write_handler(struct vlapic *vlapic) 1463f0ddc7cSNeel Natu { 1473f0ddc7cSNeel Natu struct LAPIC *lapic; 1483f0ddc7cSNeel Natu 1493f0ddc7cSNeel Natu /* 1503f0ddc7cSNeel Natu * We don't allow the ID register to be modified so reset it back to 1513f0ddc7cSNeel Natu * its default value. 1523f0ddc7cSNeel Natu */ 153de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1543f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 1554f8be175SNeel Natu } 1564f8be175SNeel Natu 1572e25737aSNeel Natu static int 1582e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr) 1592e25737aSNeel Natu { 1602e25737aSNeel Natu switch (dcr & 0xB) { 161117e8f37SPeter Grehan case APIC_TDCR_1: 162117e8f37SPeter Grehan return (1); 1632e25737aSNeel Natu case APIC_TDCR_2: 1642e25737aSNeel Natu return (2); 1652e25737aSNeel Natu case APIC_TDCR_4: 1662e25737aSNeel Natu return (4); 1672e25737aSNeel Natu case APIC_TDCR_8: 1682e25737aSNeel Natu return (8); 1692e25737aSNeel Natu case APIC_TDCR_16: 1702e25737aSNeel Natu return (16); 1712e25737aSNeel Natu case APIC_TDCR_32: 1722e25737aSNeel Natu return (32); 1732e25737aSNeel Natu case APIC_TDCR_64: 1742e25737aSNeel Natu return (64); 1752e25737aSNeel Natu case APIC_TDCR_128: 1762e25737aSNeel Natu return (128); 1772e25737aSNeel Natu default: 1782e25737aSNeel Natu panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr); 1792e25737aSNeel Natu } 1802e25737aSNeel Natu } 1812e25737aSNeel Natu 182366f6083SPeter Grehan #if 0 183366f6083SPeter Grehan static inline void 184366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) 185366f6083SPeter Grehan { 186366f6083SPeter Grehan printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset, 187366f6083SPeter Grehan *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS, 188366f6083SPeter Grehan *lvt & APIC_LVTT_M); 189366f6083SPeter Grehan } 190366f6083SPeter Grehan #endif 191366f6083SPeter Grehan 192fb03ca4eSNeel Natu static uint32_t 193366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic) 194366f6083SPeter Grehan { 195fb03ca4eSNeel Natu struct bintime bt_now, bt_rem; 196fb03ca4eSNeel Natu struct LAPIC *lapic; 197fb03ca4eSNeel Natu uint32_t ccr; 198fb03ca4eSNeel Natu 199fb03ca4eSNeel Natu ccr = 0; 200de5ea6b6SNeel Natu lapic = vlapic->apic_page; 201fb03ca4eSNeel Natu 202fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 203fb03ca4eSNeel Natu if (callout_active(&vlapic->callout)) { 204fb03ca4eSNeel Natu /* 205fb03ca4eSNeel Natu * If the timer is scheduled to expire in the future then 206fb03ca4eSNeel Natu * compute the value of 'ccr' based on the remaining time. 207fb03ca4eSNeel Natu */ 208fb03ca4eSNeel Natu binuptime(&bt_now); 209fb03ca4eSNeel Natu if (bintime_cmp(&vlapic->timer_fire_bt, &bt_now, >)) { 210fb03ca4eSNeel Natu bt_rem = vlapic->timer_fire_bt; 211fb03ca4eSNeel Natu bintime_sub(&bt_rem, &bt_now); 212fb03ca4eSNeel Natu ccr += bt_rem.sec * BT2FREQ(&vlapic->timer_freq_bt); 213fb03ca4eSNeel Natu ccr += bt_rem.frac / vlapic->timer_freq_bt.frac; 214fb03ca4eSNeel Natu } 215fb03ca4eSNeel Natu } 216fb03ca4eSNeel Natu KASSERT(ccr <= lapic->icr_timer, ("vlapic_get_ccr: invalid ccr %#x, " 217fb03ca4eSNeel Natu "icr_timer is %#x", ccr, lapic->icr_timer)); 218fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic ccr_timer = %#x, icr_timer = %#x", 219fb03ca4eSNeel Natu ccr, lapic->icr_timer); 220fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 221fb03ca4eSNeel Natu return (ccr); 222fb03ca4eSNeel Natu } 223fb03ca4eSNeel Natu 224fafe8844SNeel Natu void 225fafe8844SNeel Natu vlapic_dcr_write_handler(struct vlapic *vlapic) 226fb03ca4eSNeel Natu { 227fb03ca4eSNeel Natu struct LAPIC *lapic; 228fb03ca4eSNeel Natu int divisor; 229fb03ca4eSNeel Natu 230de5ea6b6SNeel Natu lapic = vlapic->apic_page; 231fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 232fb03ca4eSNeel Natu 233fafe8844SNeel Natu divisor = vlapic_timer_divisor(lapic->dcr_timer); 234fafe8844SNeel Natu VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", 235fafe8844SNeel Natu lapic->dcr_timer, divisor); 236fb03ca4eSNeel Natu 237fb03ca4eSNeel Natu /* 238fb03ca4eSNeel Natu * Update the timer frequency and the timer period. 239fb03ca4eSNeel Natu * 240fb03ca4eSNeel Natu * XXX changes to the frequency divider will not take effect until 241fb03ca4eSNeel Natu * the timer is reloaded. 242fb03ca4eSNeel Natu */ 243fb03ca4eSNeel Natu FREQ2BT(VLAPIC_BUS_FREQ / divisor, &vlapic->timer_freq_bt); 244fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 245fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 246fb03ca4eSNeel Natu 247fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 248366f6083SPeter Grehan } 249366f6083SPeter Grehan 250fafe8844SNeel Natu void 251fafe8844SNeel Natu vlapic_esr_write_handler(struct vlapic *vlapic) 252366f6083SPeter Grehan { 253de5ea6b6SNeel Natu struct LAPIC *lapic; 254de5ea6b6SNeel Natu 255de5ea6b6SNeel Natu lapic = vlapic->apic_page; 256330baf58SJohn Baldwin lapic->esr = vlapic->esr_pending; 257330baf58SJohn Baldwin vlapic->esr_pending = 0; 258366f6083SPeter Grehan } 259366f6083SPeter Grehan 2604d1e82a8SNeel Natu int 261b5b28fc9SNeel Natu vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) 262366f6083SPeter Grehan { 2634d1e82a8SNeel Natu struct LAPIC *lapic; 264b5b28fc9SNeel Natu uint32_t *irrptr, *tmrptr, mask; 265366f6083SPeter Grehan int idx; 266366f6083SPeter Grehan 2674d1e82a8SNeel Natu KASSERT(vector >= 0 && vector < 256, ("invalid vector %d", vector)); 268366f6083SPeter Grehan 2694d1e82a8SNeel Natu lapic = vlapic->apic_page; 2701c052192SNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) { 2711c052192SNeel Natu VLAPIC_CTR1(vlapic, "vlapic is software disabled, ignoring " 2721c052192SNeel Natu "interrupt %d", vector); 2734d1e82a8SNeel Natu return (0); 2741c052192SNeel Natu } 2751c052192SNeel Natu 276330baf58SJohn Baldwin if (vector < 16) { 277330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR); 2784d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "vlapic ignoring interrupt to vector %d", 2794d1e82a8SNeel Natu vector); 2804d1e82a8SNeel Natu return (1); 281330baf58SJohn Baldwin } 282330baf58SJohn Baldwin 28388c4b8d1SNeel Natu if (vlapic->ops.set_intr_ready) 28488c4b8d1SNeel Natu return ((*vlapic->ops.set_intr_ready)(vlapic, vector, level)); 28588c4b8d1SNeel Natu 286366f6083SPeter Grehan idx = (vector / 32) * 4; 287b5b28fc9SNeel Natu mask = 1 << (vector % 32); 288b5b28fc9SNeel Natu 289366f6083SPeter Grehan irrptr = &lapic->irr0; 290b5b28fc9SNeel Natu atomic_set_int(&irrptr[idx], mask); 291b5b28fc9SNeel Natu 292b5b28fc9SNeel Natu /* 2935b8a8cd1SNeel Natu * Verify that the trigger-mode of the interrupt matches with 2945b8a8cd1SNeel Natu * the vlapic TMR registers. 295b5b28fc9SNeel Natu */ 296b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 297294d0d88SNeel Natu if ((tmrptr[idx] & mask) != (level ? mask : 0)) { 298294d0d88SNeel Natu VLAPIC_CTR3(vlapic, "vlapic TMR[%d] is 0x%08x but " 299294d0d88SNeel Natu "interrupt is %s-triggered", idx / 4, tmrptr[idx], 300294d0d88SNeel Natu level ? "level" : "edge"); 301294d0d88SNeel Natu } 302b5b28fc9SNeel Natu 303366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready"); 3044d1e82a8SNeel Natu return (1); 305366f6083SPeter Grehan } 306366f6083SPeter Grehan 307366f6083SPeter Grehan static __inline uint32_t * 308fb03ca4eSNeel Natu vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) 309366f6083SPeter Grehan { 310de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 311366f6083SPeter Grehan int i; 312366f6083SPeter Grehan 313330baf58SJohn Baldwin switch (offset) { 314330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 315330baf58SJohn Baldwin return (&lapic->lvt_cmci); 316330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 317366f6083SPeter Grehan i = (offset - APIC_OFFSET_TIMER_LVT) >> 2; 318366f6083SPeter Grehan return ((&lapic->lvt_timer) + i);; 319330baf58SJohn Baldwin default: 320330baf58SJohn Baldwin panic("vlapic_get_lvt: invalid LVT\n"); 321330baf58SJohn Baldwin } 322366f6083SPeter Grehan } 323366f6083SPeter Grehan 3247c05bc31SNeel Natu static __inline int 3257c05bc31SNeel Natu lvt_off_to_idx(uint32_t offset) 3267c05bc31SNeel Natu { 3277c05bc31SNeel Natu int index; 3287c05bc31SNeel Natu 3297c05bc31SNeel Natu switch (offset) { 3307c05bc31SNeel Natu case APIC_OFFSET_CMCI_LVT: 3317c05bc31SNeel Natu index = APIC_LVT_CMCI; 3327c05bc31SNeel Natu break; 3337c05bc31SNeel Natu case APIC_OFFSET_TIMER_LVT: 3347c05bc31SNeel Natu index = APIC_LVT_TIMER; 3357c05bc31SNeel Natu break; 3367c05bc31SNeel Natu case APIC_OFFSET_THERM_LVT: 3377c05bc31SNeel Natu index = APIC_LVT_THERMAL; 3387c05bc31SNeel Natu break; 3397c05bc31SNeel Natu case APIC_OFFSET_PERF_LVT: 3407c05bc31SNeel Natu index = APIC_LVT_PMC; 3417c05bc31SNeel Natu break; 3427c05bc31SNeel Natu case APIC_OFFSET_LINT0_LVT: 3437c05bc31SNeel Natu index = APIC_LVT_LINT0; 3447c05bc31SNeel Natu break; 3457c05bc31SNeel Natu case APIC_OFFSET_LINT1_LVT: 3467c05bc31SNeel Natu index = APIC_LVT_LINT1; 3477c05bc31SNeel Natu break; 3487c05bc31SNeel Natu case APIC_OFFSET_ERROR_LVT: 3497c05bc31SNeel Natu index = APIC_LVT_ERROR; 3507c05bc31SNeel Natu break; 3517c05bc31SNeel Natu default: 3527c05bc31SNeel Natu index = -1; 3537c05bc31SNeel Natu break; 3547c05bc31SNeel Natu } 3557c05bc31SNeel Natu KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: " 3567c05bc31SNeel Natu "invalid lvt index %d for offset %#x", index, offset)); 3577c05bc31SNeel Natu 3587c05bc31SNeel Natu return (index); 3597c05bc31SNeel Natu } 3607c05bc31SNeel Natu 361fb03ca4eSNeel Natu static __inline uint32_t 362fb03ca4eSNeel Natu vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) 363fb03ca4eSNeel Natu { 3647c05bc31SNeel Natu int idx; 3657c05bc31SNeel Natu uint32_t val; 366fb03ca4eSNeel Natu 3677c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 3687c05bc31SNeel Natu val = atomic_load_acq_32(&vlapic->lvt_last[idx]); 3697c05bc31SNeel Natu return (val); 370fb03ca4eSNeel Natu } 371fb03ca4eSNeel Natu 3727c05bc31SNeel Natu void 3737c05bc31SNeel Natu vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset) 374fb03ca4eSNeel Natu { 3757c05bc31SNeel Natu uint32_t *lvtptr, mask, val; 376fb03ca4eSNeel Natu struct LAPIC *lapic; 3777c05bc31SNeel Natu int idx; 378fb03ca4eSNeel Natu 379de5ea6b6SNeel Natu lapic = vlapic->apic_page; 380fb03ca4eSNeel Natu lvtptr = vlapic_get_lvtptr(vlapic, offset); 3817c05bc31SNeel Natu val = *lvtptr; 3827c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 383fb03ca4eSNeel Natu 384fb03ca4eSNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) 385fb03ca4eSNeel Natu val |= APIC_LVT_M; 386330baf58SJohn Baldwin mask = APIC_LVT_M | APIC_LVT_DS | APIC_LVT_VECTOR; 387330baf58SJohn Baldwin switch (offset) { 388330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT: 389330baf58SJohn Baldwin mask |= APIC_LVTT_TM; 390330baf58SJohn Baldwin break; 391330baf58SJohn Baldwin case APIC_OFFSET_ERROR_LVT: 392330baf58SJohn Baldwin break; 393330baf58SJohn Baldwin case APIC_OFFSET_LINT0_LVT: 394330baf58SJohn Baldwin case APIC_OFFSET_LINT1_LVT: 395330baf58SJohn Baldwin mask |= APIC_LVT_TM | APIC_LVT_RIRR | APIC_LVT_IIPP; 396330baf58SJohn Baldwin /* FALLTHROUGH */ 397330baf58SJohn Baldwin default: 398330baf58SJohn Baldwin mask |= APIC_LVT_DM; 399330baf58SJohn Baldwin break; 400330baf58SJohn Baldwin } 4017c05bc31SNeel Natu val &= mask; 4027c05bc31SNeel Natu *lvtptr = val; 4037c05bc31SNeel Natu atomic_store_rel_32(&vlapic->lvt_last[idx], val); 4047c05bc31SNeel Natu } 405fb03ca4eSNeel Natu 4067c05bc31SNeel Natu static void 4077c05bc31SNeel Natu vlapic_mask_lvts(struct vlapic *vlapic) 4087c05bc31SNeel Natu { 4097c05bc31SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 4107c05bc31SNeel Natu 4117c05bc31SNeel Natu lapic->lvt_cmci |= APIC_LVT_M; 4127c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT); 4137c05bc31SNeel Natu 4147c05bc31SNeel Natu lapic->lvt_timer |= APIC_LVT_M; 4157c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT); 4167c05bc31SNeel Natu 4177c05bc31SNeel Natu lapic->lvt_thermal |= APIC_LVT_M; 4187c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT); 4197c05bc31SNeel Natu 4207c05bc31SNeel Natu lapic->lvt_pcint |= APIC_LVT_M; 4217c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT); 4227c05bc31SNeel Natu 4237c05bc31SNeel Natu lapic->lvt_lint0 |= APIC_LVT_M; 4247c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT); 4257c05bc31SNeel Natu 4267c05bc31SNeel Natu lapic->lvt_lint1 |= APIC_LVT_M; 4277c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT); 4287c05bc31SNeel Natu 4297c05bc31SNeel Natu lapic->lvt_error |= APIC_LVT_M; 4307c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT); 431fb03ca4eSNeel Natu } 432fb03ca4eSNeel Natu 433330baf58SJohn Baldwin static int 434330baf58SJohn Baldwin vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt) 435330baf58SJohn Baldwin { 436330baf58SJohn Baldwin uint32_t vec, mode; 437330baf58SJohn Baldwin 438330baf58SJohn Baldwin if (lvt & APIC_LVT_M) 439330baf58SJohn Baldwin return (0); 440330baf58SJohn Baldwin 441330baf58SJohn Baldwin vec = lvt & APIC_LVT_VECTOR; 442330baf58SJohn Baldwin mode = lvt & APIC_LVT_DM; 443330baf58SJohn Baldwin 444330baf58SJohn Baldwin switch (mode) { 445330baf58SJohn Baldwin case APIC_LVT_DM_FIXED: 446330baf58SJohn Baldwin if (vec < 16) { 447330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 448330baf58SJohn Baldwin return (0); 449330baf58SJohn Baldwin } 4504d1e82a8SNeel Natu if (vlapic_set_intr_ready(vlapic, vec, false)) 451de5ea6b6SNeel Natu vcpu_notify_event(vlapic->vm, vlapic->vcpuid, true); 452330baf58SJohn Baldwin break; 453330baf58SJohn Baldwin case APIC_LVT_DM_NMI: 454330baf58SJohn Baldwin vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 455330baf58SJohn Baldwin break; 456762fd208STycho Nightingale case APIC_LVT_DM_EXTINT: 4570775fbb4STycho Nightingale vm_inject_extint(vlapic->vm, vlapic->vcpuid); 458762fd208STycho Nightingale 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 6547c05bc31SNeel Natu static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1, 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 662762fd208STycho Nightingale if (vlapic_enabled(vlapic) == false) { 663762fd208STycho Nightingale /* 664762fd208STycho Nightingale * When the local APIC is global/hardware disabled, 665762fd208STycho Nightingale * LINT[1:0] pins are configured as INTR and NMI pins, 666762fd208STycho Nightingale * respectively. 667762fd208STycho Nightingale */ 668762fd208STycho Nightingale switch (vector) { 669762fd208STycho Nightingale case APIC_LVT_LINT0: 6700775fbb4STycho Nightingale vm_inject_extint(vlapic->vm, vlapic->vcpuid); 671762fd208STycho Nightingale break; 672762fd208STycho Nightingale case APIC_LVT_LINT1: 673762fd208STycho Nightingale vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 674762fd208STycho Nightingale break; 675762fd208STycho Nightingale default: 676762fd208STycho Nightingale break; 677762fd208STycho Nightingale } 678762fd208STycho Nightingale return (0); 679762fd208STycho Nightingale } 680762fd208STycho Nightingale 681330baf58SJohn Baldwin switch (vector) { 682330baf58SJohn Baldwin case APIC_LVT_LINT0: 683330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT); 684330baf58SJohn Baldwin break; 685330baf58SJohn Baldwin case APIC_LVT_LINT1: 686330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT1_LVT); 687330baf58SJohn Baldwin break; 688330baf58SJohn Baldwin case APIC_LVT_TIMER: 689330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 690330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 691330baf58SJohn Baldwin break; 692330baf58SJohn Baldwin case APIC_LVT_ERROR: 693330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT); 694330baf58SJohn Baldwin lvt |= APIC_LVT_DM_FIXED; 695330baf58SJohn Baldwin break; 696330baf58SJohn Baldwin case APIC_LVT_PMC: 697330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_PERF_LVT); 698330baf58SJohn Baldwin break; 699330baf58SJohn Baldwin case APIC_LVT_THERMAL: 700330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_THERM_LVT); 701330baf58SJohn Baldwin break; 702330baf58SJohn Baldwin case APIC_LVT_CMCI: 703330baf58SJohn Baldwin lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT); 704330baf58SJohn Baldwin break; 705330baf58SJohn Baldwin default: 706330baf58SJohn Baldwin return (EINVAL); 707330baf58SJohn Baldwin } 708330baf58SJohn Baldwin if (vlapic_fire_lvt(vlapic, lvt)) { 709330baf58SJohn Baldwin vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 710330baf58SJohn Baldwin LVTS_TRIGGERRED, vector, 1); 711330baf58SJohn Baldwin } 712330baf58SJohn Baldwin return (0); 713330baf58SJohn Baldwin } 714330baf58SJohn Baldwin 715fb03ca4eSNeel Natu static void 716fb03ca4eSNeel Natu vlapic_callout_handler(void *arg) 717fb03ca4eSNeel Natu { 718fb03ca4eSNeel Natu struct vlapic *vlapic; 719fb03ca4eSNeel Natu struct bintime bt, btnow; 720fb03ca4eSNeel Natu sbintime_t rem_sbt; 721fb03ca4eSNeel Natu 722fb03ca4eSNeel Natu vlapic = arg; 723fb03ca4eSNeel Natu 724fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 725fb03ca4eSNeel Natu if (callout_pending(&vlapic->callout)) /* callout was reset */ 726fb03ca4eSNeel Natu goto done; 727fb03ca4eSNeel Natu 728fb03ca4eSNeel Natu if (!callout_active(&vlapic->callout)) /* callout was stopped */ 729fb03ca4eSNeel Natu goto done; 730fb03ca4eSNeel Natu 731fb03ca4eSNeel Natu callout_deactivate(&vlapic->callout); 732fb03ca4eSNeel Natu 733fb03ca4eSNeel Natu vlapic_fire_timer(vlapic); 734fb03ca4eSNeel Natu 735fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) { 736fb03ca4eSNeel Natu binuptime(&btnow); 737fb03ca4eSNeel Natu KASSERT(bintime_cmp(&btnow, &vlapic->timer_fire_bt, >=), 738fb03ca4eSNeel Natu ("vlapic callout at %#lx.%#lx, expected at %#lx.#%lx", 739fb03ca4eSNeel Natu btnow.sec, btnow.frac, vlapic->timer_fire_bt.sec, 740fb03ca4eSNeel Natu vlapic->timer_fire_bt.frac)); 741fb03ca4eSNeel Natu 742fb03ca4eSNeel Natu /* 743fb03ca4eSNeel Natu * Compute the delta between when the timer was supposed to 744fb03ca4eSNeel Natu * fire and the present time. 745fb03ca4eSNeel Natu */ 746fb03ca4eSNeel Natu bt = btnow; 747fb03ca4eSNeel Natu bintime_sub(&bt, &vlapic->timer_fire_bt); 748fb03ca4eSNeel Natu 749fb03ca4eSNeel Natu rem_sbt = bttosbt(vlapic->timer_period_bt); 750fb03ca4eSNeel Natu if (bintime_cmp(&bt, &vlapic->timer_period_bt, <)) { 751fb03ca4eSNeel Natu /* 752fb03ca4eSNeel Natu * Adjust the time until the next countdown downward 753fb03ca4eSNeel Natu * to account for the lost time. 754fb03ca4eSNeel Natu */ 755fb03ca4eSNeel Natu rem_sbt -= bttosbt(bt); 756fb03ca4eSNeel Natu } else { 757fb03ca4eSNeel Natu /* 758fb03ca4eSNeel Natu * If the delta is greater than the timer period then 759fb03ca4eSNeel Natu * just reset our time base instead of trying to catch 760fb03ca4eSNeel Natu * up. 761fb03ca4eSNeel Natu */ 762fb03ca4eSNeel Natu vlapic->timer_fire_bt = btnow; 763fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic timer lagging by %lu " 764fb03ca4eSNeel Natu "usecs, period is %lu usecs - resetting time base", 765fb03ca4eSNeel Natu bttosbt(bt) / SBT_1US, 766fb03ca4eSNeel Natu bttosbt(vlapic->timer_period_bt) / SBT_1US); 767fb03ca4eSNeel Natu } 768fb03ca4eSNeel Natu 769fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 770fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, rem_sbt, 0, 771fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 772fb03ca4eSNeel Natu } 773fb03ca4eSNeel Natu done: 774fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 775fb03ca4eSNeel Natu } 776fb03ca4eSNeel Natu 777fafe8844SNeel Natu void 778fafe8844SNeel Natu vlapic_icrtmr_write_handler(struct vlapic *vlapic) 779fb03ca4eSNeel Natu { 780fb03ca4eSNeel Natu struct LAPIC *lapic; 781fb03ca4eSNeel Natu sbintime_t sbt; 782fafe8844SNeel Natu uint32_t icr_timer; 783fb03ca4eSNeel Natu 784fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 785fb03ca4eSNeel Natu 786de5ea6b6SNeel Natu lapic = vlapic->apic_page; 787fafe8844SNeel Natu icr_timer = lapic->icr_timer; 788fb03ca4eSNeel Natu 789fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 790fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, icr_timer); 791fb03ca4eSNeel Natu 792fb03ca4eSNeel Natu if (icr_timer != 0) { 793fb03ca4eSNeel Natu binuptime(&vlapic->timer_fire_bt); 794fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 795fb03ca4eSNeel Natu 796fb03ca4eSNeel Natu sbt = bttosbt(vlapic->timer_period_bt); 797fb03ca4eSNeel Natu callout_reset_sbt(&vlapic->callout, sbt, 0, 798fb03ca4eSNeel Natu vlapic_callout_handler, vlapic, 0); 799fb03ca4eSNeel Natu } else 800fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 801fb03ca4eSNeel Natu 802fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 803fb03ca4eSNeel Natu } 804fb03ca4eSNeel Natu 8054f8be175SNeel Natu /* 8064f8be175SNeel Natu * This function populates 'dmask' with the set of vcpus that match the 8074f8be175SNeel Natu * addressing specified by the (dest, phys, lowprio) tuple. 8084f8be175SNeel Natu * 8094f8be175SNeel Natu * 'x2apic_dest' specifies whether 'dest' is interpreted as x2APIC (32-bit) 8104f8be175SNeel Natu * or xAPIC (8-bit) destination field. 8114f8be175SNeel Natu */ 8124f8be175SNeel Natu static void 8134f8be175SNeel Natu vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys, 8144f8be175SNeel Natu bool lowprio, bool x2apic_dest) 8154f8be175SNeel Natu { 8164f8be175SNeel Natu struct vlapic *vlapic; 8174f8be175SNeel Natu uint32_t dfr, ldr, ldest, cluster; 8184f8be175SNeel Natu uint32_t mda_flat_ldest, mda_cluster_ldest, mda_ldest, mda_cluster_id; 8194f8be175SNeel Natu cpuset_t amask; 8204f8be175SNeel Natu int vcpuid; 8214f8be175SNeel Natu 8224f8be175SNeel Natu if ((x2apic_dest && dest == 0xffffffff) || 8234f8be175SNeel Natu (!x2apic_dest && dest == 0xff)) { 8244f8be175SNeel Natu /* 8254f8be175SNeel Natu * Broadcast in both logical and physical modes. 8264f8be175SNeel Natu */ 8274f8be175SNeel Natu *dmask = vm_active_cpus(vm); 8284f8be175SNeel Natu return; 8294f8be175SNeel Natu } 8304f8be175SNeel Natu 8314f8be175SNeel Natu if (phys) { 8324f8be175SNeel Natu /* 8334f8be175SNeel Natu * Physical mode: destination is APIC ID. 8344f8be175SNeel Natu */ 8354f8be175SNeel Natu CPU_ZERO(dmask); 8364f8be175SNeel Natu vcpuid = vm_apicid2vcpuid(vm, dest); 8374f8be175SNeel Natu if (vcpuid < VM_MAXCPU) 8384f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8394f8be175SNeel Natu } else { 8404f8be175SNeel Natu /* 8414f8be175SNeel Natu * In the "Flat Model" the MDA is interpreted as an 8-bit wide 8424f8be175SNeel Natu * bitmask. This model is only avilable in the xAPIC mode. 8434f8be175SNeel Natu */ 8444f8be175SNeel Natu mda_flat_ldest = dest & 0xff; 8454f8be175SNeel Natu 8464f8be175SNeel Natu /* 8474f8be175SNeel Natu * In the "Cluster Model" the MDA is used to identify a 8484f8be175SNeel Natu * specific cluster and a set of APICs in that cluster. 8494f8be175SNeel Natu */ 8504f8be175SNeel Natu if (x2apic_dest) { 8514f8be175SNeel Natu mda_cluster_id = dest >> 16; 8524f8be175SNeel Natu mda_cluster_ldest = dest & 0xffff; 8534f8be175SNeel Natu } else { 8544f8be175SNeel Natu mda_cluster_id = (dest >> 4) & 0xf; 8554f8be175SNeel Natu mda_cluster_ldest = dest & 0xf; 8564f8be175SNeel Natu } 8574f8be175SNeel Natu 8584f8be175SNeel Natu /* 8594f8be175SNeel Natu * Logical mode: match each APIC that has a bit set 8604f8be175SNeel Natu * in it's LDR that matches a bit in the ldest. 8614f8be175SNeel Natu */ 8624f8be175SNeel Natu CPU_ZERO(dmask); 8634f8be175SNeel Natu amask = vm_active_cpus(vm); 8644f8be175SNeel Natu while ((vcpuid = CPU_FFS(&amask)) != 0) { 8654f8be175SNeel Natu vcpuid--; 8664f8be175SNeel Natu CPU_CLR(vcpuid, &amask); 8674f8be175SNeel Natu 8684f8be175SNeel Natu vlapic = vm_lapic(vm, vcpuid); 8693f0ddc7cSNeel Natu dfr = vlapic->apic_page->dfr; 8703f0ddc7cSNeel Natu ldr = vlapic->apic_page->ldr; 8714f8be175SNeel Natu 8724f8be175SNeel Natu if ((dfr & APIC_DFR_MODEL_MASK) == 8734f8be175SNeel Natu APIC_DFR_MODEL_FLAT) { 8744f8be175SNeel Natu ldest = ldr >> 24; 8754f8be175SNeel Natu mda_ldest = mda_flat_ldest; 8764f8be175SNeel Natu } else if ((dfr & APIC_DFR_MODEL_MASK) == 8774f8be175SNeel Natu APIC_DFR_MODEL_CLUSTER) { 8784f8be175SNeel Natu if (x2apic(vlapic)) { 8794f8be175SNeel Natu cluster = ldr >> 16; 8804f8be175SNeel Natu ldest = ldr & 0xffff; 8814f8be175SNeel Natu } else { 8824f8be175SNeel Natu cluster = ldr >> 28; 8834f8be175SNeel Natu ldest = (ldr >> 24) & 0xf; 8844f8be175SNeel Natu } 8854f8be175SNeel Natu if (cluster != mda_cluster_id) 8864f8be175SNeel Natu continue; 8874f8be175SNeel Natu mda_ldest = mda_cluster_ldest; 8884f8be175SNeel Natu } else { 8894f8be175SNeel Natu /* 8904f8be175SNeel Natu * Guest has configured a bad logical 8914f8be175SNeel Natu * model for this vcpu - skip it. 8924f8be175SNeel Natu */ 8934f8be175SNeel Natu VLAPIC_CTR1(vlapic, "vlapic has bad logical " 8944f8be175SNeel Natu "model %x - cannot deliver interrupt", dfr); 8954f8be175SNeel Natu continue; 8964f8be175SNeel Natu } 8974f8be175SNeel Natu 8984f8be175SNeel Natu if ((mda_ldest & ldest) != 0) { 8994f8be175SNeel Natu CPU_SET(vcpuid, dmask); 9004f8be175SNeel Natu if (lowprio) 9014f8be175SNeel Natu break; 9024f8be175SNeel Natu } 9034f8be175SNeel Natu } 9044f8be175SNeel Natu } 9054f8be175SNeel Natu } 9064f8be175SNeel Natu 9070acb0d84SNeel Natu static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu"); 9080acb0d84SNeel Natu 909fafe8844SNeel Natu int 910fafe8844SNeel Natu vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu) 911366f6083SPeter Grehan { 912366f6083SPeter Grehan int i; 9134f8be175SNeel Natu bool phys; 914a5615c90SPeter Grehan cpuset_t dmask; 915fafe8844SNeel Natu uint64_t icrval; 916366f6083SPeter Grehan uint32_t dest, vec, mode; 917edf89256SNeel Natu struct vlapic *vlapic2; 918edf89256SNeel Natu struct vm_exit *vmexit; 919fafe8844SNeel Natu struct LAPIC *lapic; 920fafe8844SNeel Natu 921fafe8844SNeel Natu lapic = vlapic->apic_page; 922fafe8844SNeel Natu lapic->icr_lo &= ~APIC_DELSTAT_PEND; 923fafe8844SNeel Natu icrval = ((uint64_t)lapic->icr_hi << 32) | lapic->icr_lo; 924366f6083SPeter Grehan 925a2da7af6SNeel Natu if (x2apic(vlapic)) 926366f6083SPeter Grehan dest = icrval >> 32; 927a2da7af6SNeel Natu else 928a2da7af6SNeel Natu dest = icrval >> (32 + 24); 929366f6083SPeter Grehan vec = icrval & APIC_VECTOR_MASK; 930366f6083SPeter Grehan mode = icrval & APIC_DELMODE_MASK; 931366f6083SPeter Grehan 932330baf58SJohn Baldwin if (mode == APIC_DELMODE_FIXED && vec < 16) { 933330baf58SJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); 9344d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec); 935330baf58SJohn Baldwin return (0); 936330baf58SJohn Baldwin } 937330baf58SJohn Baldwin 9384d1e82a8SNeel Natu VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec); 9394d1e82a8SNeel Natu 940366f6083SPeter Grehan if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) { 941366f6083SPeter Grehan switch (icrval & APIC_DEST_MASK) { 942366f6083SPeter Grehan case APIC_DEST_DESTFLD: 9434f8be175SNeel Natu phys = ((icrval & APIC_DESTMODE_LOG) == 0); 9444f8be175SNeel Natu vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false, 9454f8be175SNeel Natu x2apic(vlapic)); 946366f6083SPeter Grehan break; 947366f6083SPeter Grehan case APIC_DEST_SELF: 948a5615c90SPeter Grehan CPU_SETOF(vlapic->vcpuid, &dmask); 949366f6083SPeter Grehan break; 950366f6083SPeter Grehan case APIC_DEST_ALLISELF: 951366f6083SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 952366f6083SPeter Grehan break; 953366f6083SPeter Grehan case APIC_DEST_ALLESELF: 954a5615c90SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 955a5615c90SPeter Grehan CPU_CLR(vlapic->vcpuid, &dmask); 956366f6083SPeter Grehan break; 9571e2751ddSSergey Kandaurov default: 9581e2751ddSSergey Kandaurov CPU_ZERO(&dmask); /* satisfy gcc */ 9591e2751ddSSergey Kandaurov break; 960366f6083SPeter Grehan } 961366f6083SPeter Grehan 96282f2974aSSergey Kandaurov while ((i = CPU_FFS(&dmask)) != 0) { 963a5615c90SPeter Grehan i--; 964a5615c90SPeter Grehan CPU_CLR(i, &dmask); 9650acb0d84SNeel Natu if (mode == APIC_DELMODE_FIXED) { 966b5b28fc9SNeel Natu lapic_intr_edge(vlapic->vm, i, vec); 9670acb0d84SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 9680acb0d84SNeel Natu IPIS_SENT, i, 1); 9694d1e82a8SNeel Natu VLAPIC_CTR2(vlapic, "vlapic sending ipi %d " 9704d1e82a8SNeel Natu "to vcpuid %d", vec, i); 9714d1e82a8SNeel Natu } else { 972366f6083SPeter Grehan vm_inject_nmi(vlapic->vm, i); 9734d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "vlapic sending ipi nmi " 9744d1e82a8SNeel Natu "to vcpuid %d", i); 9754d1e82a8SNeel Natu } 976366f6083SPeter Grehan } 977366f6083SPeter Grehan 978366f6083SPeter Grehan return (0); /* handled completely in the kernel */ 979366f6083SPeter Grehan } 980366f6083SPeter Grehan 981edf89256SNeel Natu if (mode == APIC_DELMODE_INIT) { 982edf89256SNeel Natu if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) 983edf89256SNeel Natu return (0); 984edf89256SNeel Natu 985edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 986edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 987edf89256SNeel Natu 988edf89256SNeel Natu /* move from INIT to waiting-for-SIPI state */ 989edf89256SNeel Natu if (vlapic2->boot_state == BS_INIT) { 990edf89256SNeel Natu vlapic2->boot_state = BS_SIPI; 991edf89256SNeel Natu } 992edf89256SNeel Natu 993edf89256SNeel Natu return (0); 994edf89256SNeel Natu } 995edf89256SNeel Natu } 996edf89256SNeel Natu 997edf89256SNeel Natu if (mode == APIC_DELMODE_STARTUP) { 998edf89256SNeel Natu if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) { 999edf89256SNeel Natu vlapic2 = vm_lapic(vlapic->vm, dest); 1000edf89256SNeel Natu 1001edf89256SNeel Natu /* 1002edf89256SNeel Natu * Ignore SIPIs in any state other than wait-for-SIPI 1003edf89256SNeel Natu */ 1004edf89256SNeel Natu if (vlapic2->boot_state != BS_SIPI) 1005edf89256SNeel Natu return (0); 1006edf89256SNeel Natu 1007edf89256SNeel Natu vlapic2->boot_state = BS_RUNNING; 1008edf89256SNeel Natu 1009becd9849SNeel Natu *retu = true; 1010becd9849SNeel Natu vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); 1011becd9849SNeel Natu vmexit->exitcode = VM_EXITCODE_SPINUP_AP; 1012becd9849SNeel Natu vmexit->u.spinup_ap.vcpu = dest; 1013becd9849SNeel Natu vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT; 1014becd9849SNeel Natu 1015edf89256SNeel Natu return (0); 1016edf89256SNeel Natu } 1017edf89256SNeel Natu } 1018366f6083SPeter Grehan 1019366f6083SPeter Grehan /* 1020366f6083SPeter Grehan * This will cause a return to userland. 1021366f6083SPeter Grehan */ 1022366f6083SPeter Grehan return (1); 1023366f6083SPeter Grehan } 1024366f6083SPeter Grehan 1025159dd56fSNeel Natu void 1026294d0d88SNeel Natu vlapic_self_ipi_handler(struct vlapic *vlapic, uint64_t val) 1027294d0d88SNeel Natu { 1028294d0d88SNeel Natu int vec; 1029294d0d88SNeel Natu 1030159dd56fSNeel Natu KASSERT(x2apic(vlapic), ("SELF_IPI does not exist in xAPIC mode")); 1031159dd56fSNeel Natu 1032294d0d88SNeel Natu vec = val & 0xff; 1033294d0d88SNeel Natu lapic_intr_edge(vlapic->vm, vlapic->vcpuid, vec); 1034294d0d88SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, IPIS_SENT, 1035294d0d88SNeel Natu vlapic->vcpuid, 1); 1036294d0d88SNeel Natu VLAPIC_CTR1(vlapic, "vlapic self-ipi %d", vec); 1037294d0d88SNeel Natu } 1038294d0d88SNeel Natu 1039366f6083SPeter Grehan int 10404d1e82a8SNeel Natu vlapic_pending_intr(struct vlapic *vlapic, int *vecptr) 1041366f6083SPeter Grehan { 1042de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1043366f6083SPeter Grehan int idx, i, bitpos, vector; 1044366f6083SPeter Grehan uint32_t *irrptr, val; 1045366f6083SPeter Grehan 104688c4b8d1SNeel Natu if (vlapic->ops.pending_intr) 104788c4b8d1SNeel Natu return ((*vlapic->ops.pending_intr)(vlapic, vecptr)); 104888c4b8d1SNeel Natu 1049366f6083SPeter Grehan irrptr = &lapic->irr0; 1050366f6083SPeter Grehan 1051366f6083SPeter Grehan /* 1052366f6083SPeter Grehan * The x86 architecture reserves the the first 32 vectors for use 1053366f6083SPeter Grehan * by the processor. 1054366f6083SPeter Grehan */ 1055366f6083SPeter Grehan for (i = 7; i > 0; i--) { 1056366f6083SPeter Grehan idx = i * 4; 1057366f6083SPeter Grehan val = atomic_load_acq_int(&irrptr[idx]); 1058366f6083SPeter Grehan bitpos = fls(val); 1059366f6083SPeter Grehan if (bitpos != 0) { 1060366f6083SPeter Grehan vector = i * 32 + (bitpos - 1); 1061366f6083SPeter Grehan if (PRIO(vector) > PRIO(lapic->ppr)) { 1062366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "pending intr %d", vector); 10634d1e82a8SNeel Natu if (vecptr != NULL) 10644d1e82a8SNeel Natu *vecptr = vector; 10654d1e82a8SNeel Natu return (1); 1066366f6083SPeter Grehan } else 1067366f6083SPeter Grehan break; 1068366f6083SPeter Grehan } 1069366f6083SPeter Grehan } 10704d1e82a8SNeel Natu return (0); 1071366f6083SPeter Grehan } 1072366f6083SPeter Grehan 1073366f6083SPeter Grehan void 1074366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector) 1075366f6083SPeter Grehan { 1076de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1077366f6083SPeter Grehan uint32_t *irrptr, *isrptr; 1078366f6083SPeter Grehan int idx, stk_top; 1079366f6083SPeter Grehan 108088c4b8d1SNeel Natu if (vlapic->ops.intr_accepted) 108188c4b8d1SNeel Natu return ((*vlapic->ops.intr_accepted)(vlapic, vector)); 108288c4b8d1SNeel Natu 1083366f6083SPeter Grehan /* 1084366f6083SPeter Grehan * clear the ready bit for vector being accepted in irr 1085366f6083SPeter Grehan * and set the vector as in service in isr. 1086366f6083SPeter Grehan */ 1087366f6083SPeter Grehan idx = (vector / 32) * 4; 1088366f6083SPeter Grehan 1089366f6083SPeter Grehan irrptr = &lapic->irr0; 1090366f6083SPeter Grehan atomic_clear_int(&irrptr[idx], 1 << (vector % 32)); 1091366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted"); 1092366f6083SPeter Grehan 1093366f6083SPeter Grehan isrptr = &lapic->isr0; 1094366f6083SPeter Grehan isrptr[idx] |= 1 << (vector % 32); 1095366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted"); 1096366f6083SPeter Grehan 1097366f6083SPeter Grehan /* 1098366f6083SPeter Grehan * Update the PPR 1099366f6083SPeter Grehan */ 1100366f6083SPeter Grehan vlapic->isrvec_stk_top++; 1101366f6083SPeter Grehan 1102366f6083SPeter Grehan stk_top = vlapic->isrvec_stk_top; 1103366f6083SPeter Grehan if (stk_top >= ISRVEC_STK_SIZE) 1104366f6083SPeter Grehan panic("isrvec_stk_top overflow %d", stk_top); 1105366f6083SPeter Grehan 1106366f6083SPeter Grehan vlapic->isrvec_stk[stk_top] = vector; 1107366f6083SPeter Grehan vlapic_update_ppr(vlapic); 1108366f6083SPeter Grehan } 1109366f6083SPeter Grehan 11102c52dcd9SNeel Natu void 11112c52dcd9SNeel Natu vlapic_svr_write_handler(struct vlapic *vlapic) 11121c052192SNeel Natu { 11131c052192SNeel Natu struct LAPIC *lapic; 11142c52dcd9SNeel Natu uint32_t old, new, changed; 11151c052192SNeel Natu 1116de5ea6b6SNeel Natu lapic = vlapic->apic_page; 11172c52dcd9SNeel Natu 11182c52dcd9SNeel Natu new = lapic->svr; 11192c52dcd9SNeel Natu old = vlapic->svr_last; 11202c52dcd9SNeel Natu vlapic->svr_last = new; 11212c52dcd9SNeel Natu 11221c052192SNeel Natu changed = old ^ new; 11231c052192SNeel Natu if ((changed & APIC_SVR_ENABLE) != 0) { 11241c052192SNeel Natu if ((new & APIC_SVR_ENABLE) == 0) { 1125fb03ca4eSNeel Natu /* 11262c52dcd9SNeel Natu * The apic is now disabled so stop the apic timer 11272c52dcd9SNeel Natu * and mask all the LVT entries. 1128fb03ca4eSNeel Natu */ 11291c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-disabled"); 1130fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 1131fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 1132fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 11332c52dcd9SNeel Natu vlapic_mask_lvts(vlapic); 11341c052192SNeel Natu } else { 1135fb03ca4eSNeel Natu /* 1136fb03ca4eSNeel Natu * The apic is now enabled so restart the apic timer 1137fb03ca4eSNeel Natu * if it is configured in periodic mode. 1138fb03ca4eSNeel Natu */ 11391c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-enabled"); 1140fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) 1141fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 11421c052192SNeel Natu } 11431c052192SNeel Natu } 11441c052192SNeel Natu } 11451c052192SNeel Natu 1146366f6083SPeter Grehan int 114752e5c8a2SNeel Natu vlapic_read(struct vlapic *vlapic, int mmio_access, uint64_t offset, 114852e5c8a2SNeel Natu uint64_t *data, bool *retu) 1149366f6083SPeter Grehan { 1150de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1151366f6083SPeter Grehan uint32_t *reg; 1152366f6083SPeter Grehan int i; 1153366f6083SPeter Grehan 115452e5c8a2SNeel Natu /* Ignore MMIO accesses in x2APIC mode */ 115552e5c8a2SNeel Natu if (x2apic(vlapic) && mmio_access) { 115652e5c8a2SNeel Natu VLAPIC_CTR1(vlapic, "MMIO read from offset %#lx in x2APIC mode", 115752e5c8a2SNeel Natu offset); 115852e5c8a2SNeel Natu *data = 0; 115952e5c8a2SNeel Natu goto done; 116052e5c8a2SNeel Natu } 116152e5c8a2SNeel Natu 116252e5c8a2SNeel Natu if (!x2apic(vlapic) && !mmio_access) { 116352e5c8a2SNeel Natu /* 116452e5c8a2SNeel Natu * XXX Generate GP fault for MSR accesses in xAPIC mode 116552e5c8a2SNeel Natu */ 116652e5c8a2SNeel Natu VLAPIC_CTR1(vlapic, "x2APIC MSR read from offset %#lx in " 116752e5c8a2SNeel Natu "xAPIC mode", offset); 116852e5c8a2SNeel Natu *data = 0; 116952e5c8a2SNeel Natu goto done; 117052e5c8a2SNeel Natu } 117152e5c8a2SNeel Natu 1172366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1173366f6083SPeter Grehan *data = 0; 11741c052192SNeel Natu goto done; 1175366f6083SPeter Grehan } 1176366f6083SPeter Grehan 1177366f6083SPeter Grehan offset &= ~3; 1178366f6083SPeter Grehan switch(offset) 1179366f6083SPeter Grehan { 1180366f6083SPeter Grehan case APIC_OFFSET_ID: 11813f0ddc7cSNeel Natu *data = lapic->id; 1182366f6083SPeter Grehan break; 1183366f6083SPeter Grehan case APIC_OFFSET_VER: 1184366f6083SPeter Grehan *data = lapic->version; 1185366f6083SPeter Grehan break; 1186366f6083SPeter Grehan case APIC_OFFSET_TPR: 1187*594db002STycho Nightingale *data = vlapic_get_tpr(vlapic); 1188366f6083SPeter Grehan break; 1189366f6083SPeter Grehan case APIC_OFFSET_APR: 1190366f6083SPeter Grehan *data = lapic->apr; 1191366f6083SPeter Grehan break; 1192366f6083SPeter Grehan case APIC_OFFSET_PPR: 1193366f6083SPeter Grehan *data = lapic->ppr; 1194366f6083SPeter Grehan break; 1195366f6083SPeter Grehan case APIC_OFFSET_EOI: 1196366f6083SPeter Grehan *data = lapic->eoi; 1197366f6083SPeter Grehan break; 1198366f6083SPeter Grehan case APIC_OFFSET_LDR: 11993f0ddc7cSNeel Natu *data = lapic->ldr; 1200366f6083SPeter Grehan break; 1201366f6083SPeter Grehan case APIC_OFFSET_DFR: 12023f0ddc7cSNeel Natu *data = lapic->dfr; 1203366f6083SPeter Grehan break; 1204366f6083SPeter Grehan case APIC_OFFSET_SVR: 1205366f6083SPeter Grehan *data = lapic->svr; 1206366f6083SPeter Grehan break; 1207366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1208366f6083SPeter Grehan i = (offset - APIC_OFFSET_ISR0) >> 2; 1209366f6083SPeter Grehan reg = &lapic->isr0; 1210366f6083SPeter Grehan *data = *(reg + i); 1211366f6083SPeter Grehan break; 1212366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1213366f6083SPeter Grehan i = (offset - APIC_OFFSET_TMR0) >> 2; 1214366f6083SPeter Grehan reg = &lapic->tmr0; 1215366f6083SPeter Grehan *data = *(reg + i); 1216366f6083SPeter Grehan break; 1217366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1218366f6083SPeter Grehan i = (offset - APIC_OFFSET_IRR0) >> 2; 1219366f6083SPeter Grehan reg = &lapic->irr0; 1220366f6083SPeter Grehan *data = atomic_load_acq_int(reg + i); 1221366f6083SPeter Grehan break; 1222366f6083SPeter Grehan case APIC_OFFSET_ESR: 1223366f6083SPeter Grehan *data = lapic->esr; 1224366f6083SPeter Grehan break; 1225366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1226366f6083SPeter Grehan *data = lapic->icr_lo; 1227fafe8844SNeel Natu if (x2apic(vlapic)) 1228fafe8844SNeel Natu *data |= (uint64_t)lapic->icr_hi << 32; 1229366f6083SPeter Grehan break; 1230366f6083SPeter Grehan case APIC_OFFSET_ICR_HI: 1231366f6083SPeter Grehan *data = lapic->icr_hi; 1232366f6083SPeter Grehan break; 1233330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1234366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 1235fb03ca4eSNeel Natu *data = vlapic_get_lvt(vlapic, offset); 12367c05bc31SNeel Natu #ifdef INVARIANTS 12377c05bc31SNeel Natu reg = vlapic_get_lvtptr(vlapic, offset); 12387c05bc31SNeel Natu KASSERT(*data == *reg, ("inconsistent lvt value at " 12397c05bc31SNeel Natu "offset %#lx: %#lx/%#x", offset, *data, *reg)); 12407c05bc31SNeel Natu #endif 1241366f6083SPeter Grehan break; 1242de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1243366f6083SPeter Grehan *data = lapic->icr_timer; 1244366f6083SPeter Grehan break; 1245de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1246366f6083SPeter Grehan *data = vlapic_get_ccr(vlapic); 1247366f6083SPeter Grehan break; 1248de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1249366f6083SPeter Grehan *data = lapic->dcr_timer; 1250366f6083SPeter Grehan break; 1251294d0d88SNeel Natu case APIC_OFFSET_SELF_IPI: 1252294d0d88SNeel Natu /* 1253294d0d88SNeel Natu * XXX generate a GP fault if vlapic is in x2apic mode 1254294d0d88SNeel Natu */ 1255294d0d88SNeel Natu *data = 0; 1256294d0d88SNeel Natu break; 1257366f6083SPeter Grehan case APIC_OFFSET_RRR: 1258366f6083SPeter Grehan default: 1259366f6083SPeter Grehan *data = 0; 1260366f6083SPeter Grehan break; 1261366f6083SPeter Grehan } 12621c052192SNeel Natu done: 12631c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic read offset %#x, data %#lx", offset, *data); 1264366f6083SPeter Grehan return 0; 1265366f6083SPeter Grehan } 1266366f6083SPeter Grehan 1267366f6083SPeter Grehan int 126852e5c8a2SNeel Natu vlapic_write(struct vlapic *vlapic, int mmio_access, uint64_t offset, 126952e5c8a2SNeel Natu uint64_t data, bool *retu) 1270366f6083SPeter Grehan { 1271de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 12727c05bc31SNeel Natu uint32_t *regptr; 1273366f6083SPeter Grehan int retval; 1274366f6083SPeter Grehan 12753f0ddc7cSNeel Natu KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE, 12763f0ddc7cSNeel Natu ("vlapic_write: invalid offset %#lx", offset)); 12773f0ddc7cSNeel Natu 127852e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "vlapic write offset %#lx, data %#lx", 127952e5c8a2SNeel Natu offset, data); 12801c052192SNeel Natu 128152e5c8a2SNeel Natu if (offset > sizeof(*lapic)) 128252e5c8a2SNeel Natu return (0); 128352e5c8a2SNeel Natu 128452e5c8a2SNeel Natu /* Ignore MMIO accesses in x2APIC mode */ 128552e5c8a2SNeel Natu if (x2apic(vlapic) && mmio_access) { 128652e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "MMIO write of %#lx to offset %#lx " 128752e5c8a2SNeel Natu "in x2APIC mode", data, offset); 128852e5c8a2SNeel Natu return (0); 128952e5c8a2SNeel Natu } 129052e5c8a2SNeel Natu 129152e5c8a2SNeel Natu /* 129252e5c8a2SNeel Natu * XXX Generate GP fault for MSR accesses in xAPIC mode 129352e5c8a2SNeel Natu */ 129452e5c8a2SNeel Natu if (!x2apic(vlapic) && !mmio_access) { 129552e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "x2APIC MSR write of %#lx to offset %#lx " 129652e5c8a2SNeel Natu "in xAPIC mode", data, offset); 129752e5c8a2SNeel Natu return (0); 1298366f6083SPeter Grehan } 1299366f6083SPeter Grehan 1300366f6083SPeter Grehan retval = 0; 1301366f6083SPeter Grehan switch(offset) 1302366f6083SPeter Grehan { 1303366f6083SPeter Grehan case APIC_OFFSET_ID: 13043f0ddc7cSNeel Natu lapic->id = data; 13053f0ddc7cSNeel Natu vlapic_id_write_handler(vlapic); 1306366f6083SPeter Grehan break; 1307366f6083SPeter Grehan case APIC_OFFSET_TPR: 1308*594db002STycho Nightingale vlapic_set_tpr(vlapic, data & 0xff); 1309366f6083SPeter Grehan break; 1310366f6083SPeter Grehan case APIC_OFFSET_EOI: 1311366f6083SPeter Grehan vlapic_process_eoi(vlapic); 1312366f6083SPeter Grehan break; 1313366f6083SPeter Grehan case APIC_OFFSET_LDR: 13143f0ddc7cSNeel Natu lapic->ldr = data; 13153f0ddc7cSNeel Natu vlapic_ldr_write_handler(vlapic); 1316366f6083SPeter Grehan break; 1317366f6083SPeter Grehan case APIC_OFFSET_DFR: 13183f0ddc7cSNeel Natu lapic->dfr = data; 13193f0ddc7cSNeel Natu vlapic_dfr_write_handler(vlapic); 1320366f6083SPeter Grehan break; 1321366f6083SPeter Grehan case APIC_OFFSET_SVR: 13222c52dcd9SNeel Natu lapic->svr = data; 13232c52dcd9SNeel Natu vlapic_svr_write_handler(vlapic); 1324366f6083SPeter Grehan break; 1325366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1326fafe8844SNeel Natu lapic->icr_lo = data; 1327fafe8844SNeel Natu if (x2apic(vlapic)) 1328fafe8844SNeel Natu lapic->icr_hi = data >> 32; 1329fafe8844SNeel Natu retval = vlapic_icrlo_write_handler(vlapic, retu); 1330366f6083SPeter Grehan break; 1331a2da7af6SNeel Natu case APIC_OFFSET_ICR_HI: 1332a2da7af6SNeel Natu lapic->icr_hi = data; 1333a2da7af6SNeel Natu break; 1334330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1335366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 13367c05bc31SNeel Natu regptr = vlapic_get_lvtptr(vlapic, offset); 13377c05bc31SNeel Natu *regptr = data; 13387c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, offset); 1339366f6083SPeter Grehan break; 1340de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1341fafe8844SNeel Natu lapic->icr_timer = data; 1342fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 1343366f6083SPeter Grehan break; 1344366f6083SPeter Grehan 1345de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1346fafe8844SNeel Natu lapic->dcr_timer = data; 1347fafe8844SNeel Natu vlapic_dcr_write_handler(vlapic); 1348366f6083SPeter Grehan break; 1349366f6083SPeter Grehan 1350366f6083SPeter Grehan case APIC_OFFSET_ESR: 1351fafe8844SNeel Natu vlapic_esr_write_handler(vlapic); 1352366f6083SPeter Grehan break; 1353294d0d88SNeel Natu 1354294d0d88SNeel Natu case APIC_OFFSET_SELF_IPI: 1355294d0d88SNeel Natu if (x2apic(vlapic)) 1356294d0d88SNeel Natu vlapic_self_ipi_handler(vlapic, data); 1357294d0d88SNeel Natu break; 1358294d0d88SNeel Natu 1359366f6083SPeter Grehan case APIC_OFFSET_VER: 1360366f6083SPeter Grehan case APIC_OFFSET_APR: 1361366f6083SPeter Grehan case APIC_OFFSET_PPR: 1362366f6083SPeter Grehan case APIC_OFFSET_RRR: 1363366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1364366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1365366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1366de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1367366f6083SPeter Grehan default: 1368366f6083SPeter Grehan // Read only. 1369366f6083SPeter Grehan break; 1370366f6083SPeter Grehan } 1371366f6083SPeter Grehan 1372366f6083SPeter Grehan return (retval); 1373366f6083SPeter Grehan } 1374366f6083SPeter Grehan 13757c05bc31SNeel Natu static void 13767c05bc31SNeel Natu vlapic_reset(struct vlapic *vlapic) 13777c05bc31SNeel Natu { 13787c05bc31SNeel Natu struct LAPIC *lapic; 13797c05bc31SNeel Natu 13807c05bc31SNeel Natu lapic = vlapic->apic_page; 13817c05bc31SNeel Natu bzero(lapic, sizeof(struct LAPIC)); 13827c05bc31SNeel Natu 13837c05bc31SNeel Natu lapic->id = vlapic_get_id(vlapic); 13847c05bc31SNeel Natu lapic->version = VLAPIC_VERSION; 13857c05bc31SNeel Natu lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT); 13867c05bc31SNeel Natu lapic->dfr = 0xffffffff; 13877c05bc31SNeel Natu lapic->svr = APIC_SVR_VECTOR; 13887c05bc31SNeel Natu vlapic_mask_lvts(vlapic); 138930b94db8SNeel Natu vlapic_reset_tmr(vlapic); 13907c05bc31SNeel Natu 13917c05bc31SNeel Natu lapic->dcr_timer = 0; 13927c05bc31SNeel Natu vlapic_dcr_write_handler(vlapic); 13937c05bc31SNeel Natu 13947c05bc31SNeel Natu if (vlapic->vcpuid == 0) 13957c05bc31SNeel Natu vlapic->boot_state = BS_RUNNING; /* BSP */ 13967c05bc31SNeel Natu else 13977c05bc31SNeel Natu vlapic->boot_state = BS_INIT; /* AP */ 13987c05bc31SNeel Natu 13997c05bc31SNeel Natu vlapic->svr_last = lapic->svr; 14007c05bc31SNeel Natu } 14017c05bc31SNeel Natu 1402de5ea6b6SNeel Natu void 1403de5ea6b6SNeel Natu vlapic_init(struct vlapic *vlapic) 1404366f6083SPeter Grehan { 1405de5ea6b6SNeel Natu KASSERT(vlapic->vm != NULL, ("vlapic_init: vm is not initialized")); 1406de5ea6b6SNeel Natu KASSERT(vlapic->vcpuid >= 0 && vlapic->vcpuid < VM_MAXCPU, 1407de5ea6b6SNeel Natu ("vlapic_init: vcpuid is not initialized")); 1408de5ea6b6SNeel Natu KASSERT(vlapic->apic_page != NULL, ("vlapic_init: apic_page is not " 1409de5ea6b6SNeel Natu "initialized")); 14102d3a73edSNeel Natu 1411becd9849SNeel Natu /* 1412becd9849SNeel Natu * If the vlapic is configured in x2apic mode then it will be 1413becd9849SNeel Natu * accessed in the critical section via the MSR emulation code. 1414becd9849SNeel Natu * 1415becd9849SNeel Natu * Therefore the timer mutex must be a spinlock because blockable 1416becd9849SNeel Natu * mutexes cannot be acquired in a critical section. 1417becd9849SNeel Natu */ 1418becd9849SNeel Natu mtx_init(&vlapic->timer_mtx, "vlapic timer mtx", NULL, MTX_SPIN); 1419fb03ca4eSNeel Natu callout_init(&vlapic->callout, 1); 1420fb03ca4eSNeel Natu 1421a2da7af6SNeel Natu vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; 14222d3a73edSNeel Natu 1423de5ea6b6SNeel Natu if (vlapic->vcpuid == 0) 14242d3a73edSNeel Natu vlapic->msr_apicbase |= APICBASE_BSP; 14252d3a73edSNeel Natu 142603cd0501SNeel Natu vlapic_reset(vlapic); 1427366f6083SPeter Grehan } 1428366f6083SPeter Grehan 1429366f6083SPeter Grehan void 1430366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic) 1431366f6083SPeter Grehan { 143203cd0501SNeel Natu 1433fb03ca4eSNeel Natu callout_drain(&vlapic->callout); 1434366f6083SPeter Grehan } 14352d3a73edSNeel Natu 14362d3a73edSNeel Natu uint64_t 14372d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic) 14382d3a73edSNeel Natu { 14392d3a73edSNeel Natu 14402d3a73edSNeel Natu return (vlapic->msr_apicbase); 14412d3a73edSNeel Natu } 14422d3a73edSNeel Natu 144352e5c8a2SNeel Natu int 14443f0ddc7cSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t new) 14452d3a73edSNeel Natu { 1446a2da7af6SNeel Natu 144752e5c8a2SNeel Natu if (vlapic->msr_apicbase != new) { 144852e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "Changing APIC_BASE MSR from %#lx to %#lx " 144952e5c8a2SNeel Natu "not supported", vlapic->msr_apicbase, new); 145052e5c8a2SNeel Natu return (-1); 145152e5c8a2SNeel Natu } 145252e5c8a2SNeel Natu 145352e5c8a2SNeel Natu return (0); 145452e5c8a2SNeel Natu } 145552e5c8a2SNeel Natu 145652e5c8a2SNeel Natu void 145752e5c8a2SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 145852e5c8a2SNeel Natu { 145952e5c8a2SNeel Natu struct vlapic *vlapic; 146052e5c8a2SNeel Natu struct LAPIC *lapic; 146152e5c8a2SNeel Natu 146252e5c8a2SNeel Natu vlapic = vm_lapic(vm, vcpuid); 1463a2da7af6SNeel Natu 1464a2da7af6SNeel Natu if (state == X2APIC_DISABLED) 146552e5c8a2SNeel Natu vlapic->msr_apicbase &= ~APICBASE_X2APIC; 146652e5c8a2SNeel Natu else 146752e5c8a2SNeel Natu vlapic->msr_apicbase |= APICBASE_X2APIC; 14683f0ddc7cSNeel Natu 14693f0ddc7cSNeel Natu /* 147052e5c8a2SNeel Natu * Reset the local APIC registers whose values are mode-dependent. 147152e5c8a2SNeel Natu * 147252e5c8a2SNeel Natu * XXX this works because the APIC mode can be changed only at vcpu 147352e5c8a2SNeel Natu * initialization time. 14743f0ddc7cSNeel Natu */ 14753f0ddc7cSNeel Natu lapic = vlapic->apic_page; 14763f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 14773f0ddc7cSNeel Natu if (x2apic(vlapic)) { 14783f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 14793f0ddc7cSNeel Natu lapic->dfr = 0; 14803f0ddc7cSNeel Natu } else { 14813f0ddc7cSNeel Natu lapic->ldr = 0; 14823f0ddc7cSNeel Natu lapic->dfr = 0xffffffff; 14833f0ddc7cSNeel Natu } 1484159dd56fSNeel Natu 1485159dd56fSNeel Natu if (state == X2APIC_ENABLED) { 1486159dd56fSNeel Natu if (vlapic->ops.enable_x2apic_mode) 1487159dd56fSNeel Natu (*vlapic->ops.enable_x2apic_mode)(vlapic); 1488159dd56fSNeel Natu } 14893f0ddc7cSNeel Natu } 14901c052192SNeel Natu 14914f8be175SNeel Natu void 14924f8be175SNeel Natu vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, 14934f8be175SNeel Natu int delmode, int vec) 14944f8be175SNeel Natu { 14954f8be175SNeel Natu bool lowprio; 14964f8be175SNeel Natu int vcpuid; 14974f8be175SNeel Natu cpuset_t dmask; 14984f8be175SNeel Natu 1499762fd208STycho Nightingale if (delmode != IOART_DELFIXED && 1500762fd208STycho Nightingale delmode != IOART_DELLOPRI && 1501762fd208STycho Nightingale delmode != IOART_DELEXINT) { 15024f8be175SNeel Natu VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode); 15034f8be175SNeel Natu return; 15044f8be175SNeel Natu } 1505762fd208STycho Nightingale lowprio = (delmode == IOART_DELLOPRI); 15064f8be175SNeel Natu 15074f8be175SNeel Natu /* 15084f8be175SNeel Natu * We don't provide any virtual interrupt redirection hardware so 15094f8be175SNeel Natu * all interrupts originating from the ioapic or MSI specify the 15104f8be175SNeel Natu * 'dest' in the legacy xAPIC format. 15114f8be175SNeel Natu */ 15124f8be175SNeel Natu vlapic_calcdest(vm, &dmask, dest, phys, lowprio, false); 15134f8be175SNeel Natu 15144f8be175SNeel Natu while ((vcpuid = CPU_FFS(&dmask)) != 0) { 15154f8be175SNeel Natu vcpuid--; 15164f8be175SNeel Natu CPU_CLR(vcpuid, &dmask); 1517762fd208STycho Nightingale if (delmode == IOART_DELEXINT) { 15180775fbb4STycho Nightingale vm_inject_extint(vm, vcpuid); 1519762fd208STycho Nightingale } else { 15204f8be175SNeel Natu lapic_set_intr(vm, vcpuid, vec, level); 15214f8be175SNeel Natu } 15224f8be175SNeel Natu } 1523762fd208STycho Nightingale } 15244f8be175SNeel Natu 1525de5ea6b6SNeel Natu void 1526add611fdSNeel Natu vlapic_post_intr(struct vlapic *vlapic, int hostcpu, int ipinum) 1527de5ea6b6SNeel Natu { 1528de5ea6b6SNeel Natu /* 1529de5ea6b6SNeel Natu * Post an interrupt to the vcpu currently running on 'hostcpu'. 1530de5ea6b6SNeel Natu * 1531de5ea6b6SNeel Natu * This is done by leveraging features like Posted Interrupts (Intel) 1532de5ea6b6SNeel Natu * Doorbell MSR (AMD AVIC) that avoid a VM exit. 1533de5ea6b6SNeel Natu * 1534de5ea6b6SNeel Natu * If neither of these features are available then fallback to 1535de5ea6b6SNeel Natu * sending an IPI to 'hostcpu'. 1536de5ea6b6SNeel Natu */ 153788c4b8d1SNeel Natu if (vlapic->ops.post_intr) 153888c4b8d1SNeel Natu (*vlapic->ops.post_intr)(vlapic, hostcpu); 153988c4b8d1SNeel Natu else 1540add611fdSNeel Natu ipi_cpu(hostcpu, ipinum); 1541de5ea6b6SNeel Natu } 1542de5ea6b6SNeel Natu 15431c052192SNeel Natu bool 15441c052192SNeel Natu vlapic_enabled(struct vlapic *vlapic) 15451c052192SNeel Natu { 1546de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 15471c052192SNeel Natu 15481c052192SNeel Natu if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && 15491c052192SNeel Natu (lapic->svr & APIC_SVR_ENABLE) != 0) 15501c052192SNeel Natu return (true); 15511c052192SNeel Natu else 15521c052192SNeel Natu return (false); 15531c052192SNeel Natu } 15545b8a8cd1SNeel Natu 155530b94db8SNeel Natu static void 155630b94db8SNeel Natu vlapic_set_tmr(struct vlapic *vlapic, int vector, bool level) 155730b94db8SNeel Natu { 155830b94db8SNeel Natu struct LAPIC *lapic; 155930b94db8SNeel Natu uint32_t *tmrptr, mask; 156030b94db8SNeel Natu int idx; 156130b94db8SNeel Natu 156230b94db8SNeel Natu lapic = vlapic->apic_page; 156330b94db8SNeel Natu tmrptr = &lapic->tmr0; 156430b94db8SNeel Natu idx = (vector / 32) * 4; 156530b94db8SNeel Natu mask = 1 << (vector % 32); 156630b94db8SNeel Natu if (level) 156730b94db8SNeel Natu tmrptr[idx] |= mask; 156830b94db8SNeel Natu else 156930b94db8SNeel Natu tmrptr[idx] &= ~mask; 157030b94db8SNeel Natu 157130b94db8SNeel Natu if (vlapic->ops.set_tmr != NULL) 157230b94db8SNeel Natu (*vlapic->ops.set_tmr)(vlapic, vector, level); 157330b94db8SNeel Natu } 157430b94db8SNeel Natu 15755b8a8cd1SNeel Natu void 15765b8a8cd1SNeel Natu vlapic_reset_tmr(struct vlapic *vlapic) 15775b8a8cd1SNeel Natu { 157830b94db8SNeel Natu int vector; 15795b8a8cd1SNeel Natu 15805b8a8cd1SNeel Natu VLAPIC_CTR0(vlapic, "vlapic resetting all vectors to edge-triggered"); 15815b8a8cd1SNeel Natu 158230b94db8SNeel Natu for (vector = 0; vector <= 255; vector++) 158330b94db8SNeel Natu vlapic_set_tmr(vlapic, vector, false); 15845b8a8cd1SNeel Natu } 15855b8a8cd1SNeel Natu 15865b8a8cd1SNeel Natu void 15875b8a8cd1SNeel Natu vlapic_set_tmr_level(struct vlapic *vlapic, uint32_t dest, bool phys, 15885b8a8cd1SNeel Natu int delmode, int vector) 15895b8a8cd1SNeel Natu { 15905b8a8cd1SNeel Natu cpuset_t dmask; 15915b8a8cd1SNeel Natu bool lowprio; 15925b8a8cd1SNeel Natu 15935b8a8cd1SNeel Natu KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector)); 15945b8a8cd1SNeel Natu 15955b8a8cd1SNeel Natu /* 15965b8a8cd1SNeel Natu * A level trigger is valid only for fixed and lowprio delivery modes. 15975b8a8cd1SNeel Natu */ 15985b8a8cd1SNeel Natu if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) { 15995b8a8cd1SNeel Natu VLAPIC_CTR1(vlapic, "Ignoring level trigger-mode for " 16005b8a8cd1SNeel Natu "delivery-mode %d", delmode); 16015b8a8cd1SNeel Natu return; 16025b8a8cd1SNeel Natu } 16035b8a8cd1SNeel Natu 16045b8a8cd1SNeel Natu lowprio = (delmode == APIC_DELMODE_LOWPRIO); 16055b8a8cd1SNeel Natu vlapic_calcdest(vlapic->vm, &dmask, dest, phys, lowprio, false); 16065b8a8cd1SNeel Natu 16075b8a8cd1SNeel Natu if (!CPU_ISSET(vlapic->vcpuid, &dmask)) 16085b8a8cd1SNeel Natu return; 16095b8a8cd1SNeel Natu 16105b8a8cd1SNeel Natu VLAPIC_CTR1(vlapic, "vector %d set to level-triggered", vector); 161130b94db8SNeel Natu vlapic_set_tmr(vlapic, vector, true); 16125b8a8cd1SNeel Natu } 1613*594db002STycho Nightingale 1614*594db002STycho Nightingale void 1615*594db002STycho Nightingale vlapic_set_tpr(struct vlapic *vlapic, uint8_t val) 1616*594db002STycho Nightingale { 1617*594db002STycho Nightingale struct LAPIC *lapic = vlapic->apic_page; 1618*594db002STycho Nightingale 1619*594db002STycho Nightingale lapic->tpr = val; 1620*594db002STycho Nightingale vlapic_update_ppr(vlapic); 1621*594db002STycho Nightingale } 1622*594db002STycho Nightingale 1623*594db002STycho Nightingale uint8_t 1624*594db002STycho Nightingale vlapic_get_tpr(struct vlapic *vlapic) 1625*594db002STycho Nightingale { 1626*594db002STycho Nightingale struct LAPIC *lapic = vlapic->apic_page; 1627*594db002STycho Nightingale 1628*594db002STycho Nightingale return (lapic->tpr); 1629*594db002STycho Nightingale } 1630