1366f6083SPeter Grehan /*- 2c49761ddSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3c49761ddSPedro F. Giffuni * 4366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 5366f6083SPeter Grehan * All rights reserved. 66a1e1c2cSJohn Baldwin * Copyright (c) 2019 Joyent, Inc. 7366f6083SPeter Grehan * 8366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 9366f6083SPeter Grehan * modification, are permitted provided that the following conditions 10366f6083SPeter Grehan * are met: 11366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 12366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 13366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 14366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 15366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 16366f6083SPeter Grehan * 17366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27366f6083SPeter Grehan * SUCH DAMAGE. 28366f6083SPeter Grehan * 29366f6083SPeter Grehan * $FreeBSD$ 30366f6083SPeter Grehan */ 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/cdefs.h> 33366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 34366f6083SPeter Grehan 35483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h" 36483d953aSJohn Baldwin 37366f6083SPeter Grehan #include <sys/param.h> 38fb03ca4eSNeel Natu #include <sys/lock.h> 39366f6083SPeter Grehan #include <sys/kernel.h> 40366f6083SPeter Grehan #include <sys/malloc.h> 41fb03ca4eSNeel Natu #include <sys/mutex.h> 42366f6083SPeter Grehan #include <sys/systm.h> 43a5615c90SPeter Grehan #include <sys/smp.h> 44366f6083SPeter Grehan 452d3a73edSNeel Natu #include <x86/specialreg.h> 4634a6b2d6SJohn Baldwin #include <x86/apicreg.h> 47366f6083SPeter Grehan 48de5ea6b6SNeel Natu #include <machine/clock.h> 49de5ea6b6SNeel Natu #include <machine/smp.h> 50de5ea6b6SNeel Natu 51366f6083SPeter Grehan #include <machine/vmm.h> 52483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 53366f6083SPeter Grehan 54366f6083SPeter Grehan #include "vmm_lapic.h" 55366f6083SPeter Grehan #include "vmm_ktr.h" 56de5ea6b6SNeel Natu #include "vmm_stat.h" 57de5ea6b6SNeel Natu 58366f6083SPeter Grehan #include "vlapic.h" 59de5ea6b6SNeel Natu #include "vlapic_priv.h" 60b5b28fc9SNeel Natu #include "vioapic.h" 61366f6083SPeter Grehan 62366f6083SPeter Grehan #define PRIO(x) ((x) >> 4) 63366f6083SPeter Grehan 64*f56801d6SCorvin Köhne #define VLAPIC_VERSION (0x14) 65366f6083SPeter Grehan 66a2da7af6SNeel Natu #define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0) 672d3a73edSNeel Natu 68fb03ca4eSNeel Natu /* 69fb03ca4eSNeel Natu * The 'vlapic->timer_mtx' is used to provide mutual exclusion between the 70fafe8844SNeel Natu * vlapic_callout_handler() and vcpu accesses to: 71fafe8844SNeel Natu * - timer_freq_bt, timer_period_bt, timer_fire_bt 72fb03ca4eSNeel Natu * - timer LVT register 73fb03ca4eSNeel Natu */ 74becd9849SNeel Natu #define VLAPIC_TIMER_LOCK(vlapic) mtx_lock_spin(&((vlapic)->timer_mtx)) 75becd9849SNeel Natu #define VLAPIC_TIMER_UNLOCK(vlapic) mtx_unlock_spin(&((vlapic)->timer_mtx)) 76fb03ca4eSNeel Natu #define VLAPIC_TIMER_LOCKED(vlapic) mtx_owned(&((vlapic)->timer_mtx)) 77fb03ca4eSNeel Natu 78c5d216b7SNeel Natu /* 79c5d216b7SNeel Natu * APIC timer frequency: 80c5d216b7SNeel Natu * - arbitrary but chosen to be in the ballpark of contemporary hardware. 81c5d216b7SNeel Natu * - power-of-two to avoid loss of precision when converted to a bintime. 82c5d216b7SNeel Natu */ 83c5d216b7SNeel Natu #define VLAPIC_BUS_FREQ (128 * 1024 * 1024) 842e25737aSNeel Natu 856a1e1c2cSJohn Baldwin static void vlapic_set_error(struct vlapic *, uint32_t, bool); 864c812fe6SMark Johnston static void vlapic_callout_handler(void *arg); 870bda8d3eSCorvin Köhne static void vlapic_reset(struct vlapic *vlapic); 886a1e1c2cSJohn Baldwin 894f8be175SNeel Natu static __inline uint32_t 904f8be175SNeel Natu vlapic_get_id(struct vlapic *vlapic) 914f8be175SNeel Natu { 924f8be175SNeel Natu 934f8be175SNeel Natu if (x2apic(vlapic)) 944f8be175SNeel Natu return (vlapic->vcpuid); 954f8be175SNeel Natu else 964f8be175SNeel Natu return (vlapic->vcpuid << 24); 974f8be175SNeel Natu } 984f8be175SNeel Natu 993f0ddc7cSNeel Natu static uint32_t 1003f0ddc7cSNeel Natu x2apic_ldr(struct vlapic *vlapic) 1014f8be175SNeel Natu { 1024f8be175SNeel Natu int apicid; 1034f8be175SNeel Natu uint32_t ldr; 1044f8be175SNeel Natu 1054f8be175SNeel Natu apicid = vlapic_get_id(vlapic); 1064f8be175SNeel Natu ldr = 1 << (apicid & 0xf); 1074f8be175SNeel Natu ldr |= (apicid & 0xffff0) << 12; 1084f8be175SNeel Natu return (ldr); 1094f8be175SNeel Natu } 1104f8be175SNeel Natu 1113f0ddc7cSNeel Natu void 1123f0ddc7cSNeel Natu vlapic_dfr_write_handler(struct vlapic *vlapic) 1134f8be175SNeel Natu { 1144f8be175SNeel Natu struct LAPIC *lapic; 1154f8be175SNeel Natu 116de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1174f8be175SNeel Natu if (x2apic(vlapic)) { 1183f0ddc7cSNeel Natu VM_CTR1(vlapic->vm, "ignoring write to DFR in x2apic mode: %#x", 1193f0ddc7cSNeel Natu lapic->dfr); 1203f0ddc7cSNeel Natu lapic->dfr = 0; 1214f8be175SNeel Natu return; 1224f8be175SNeel Natu } 1234f8be175SNeel Natu 1243f0ddc7cSNeel Natu lapic->dfr &= APIC_DFR_MODEL_MASK; 1253f0ddc7cSNeel Natu lapic->dfr |= APIC_DFR_RESERVED; 1263f0ddc7cSNeel Natu 1273f0ddc7cSNeel Natu if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT) 1284f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model"); 1293f0ddc7cSNeel Natu else if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER) 1304f8be175SNeel Natu VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model"); 1314f8be175SNeel Natu else 1323f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "DFR in Unknown Model %#x", lapic->dfr); 1334f8be175SNeel Natu } 1344f8be175SNeel Natu 1353f0ddc7cSNeel Natu void 1363f0ddc7cSNeel Natu vlapic_ldr_write_handler(struct vlapic *vlapic) 1374f8be175SNeel Natu { 1384f8be175SNeel Natu struct LAPIC *lapic; 1394f8be175SNeel Natu 1403f0ddc7cSNeel Natu lapic = vlapic->apic_page; 1413f0ddc7cSNeel Natu 1424f8be175SNeel Natu /* LDR is read-only in x2apic mode */ 1434f8be175SNeel Natu if (x2apic(vlapic)) { 1443f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "ignoring write to LDR in x2apic mode: %#x", 1453f0ddc7cSNeel Natu lapic->ldr); 1463f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 1473f0ddc7cSNeel Natu } else { 1483f0ddc7cSNeel Natu lapic->ldr &= ~APIC_LDR_RESERVED; 1493f0ddc7cSNeel Natu VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr); 1503f0ddc7cSNeel Natu } 1514f8be175SNeel Natu } 1524f8be175SNeel Natu 1533f0ddc7cSNeel Natu void 1543f0ddc7cSNeel Natu vlapic_id_write_handler(struct vlapic *vlapic) 1553f0ddc7cSNeel Natu { 1563f0ddc7cSNeel Natu struct LAPIC *lapic; 1573f0ddc7cSNeel Natu 1583f0ddc7cSNeel Natu /* 1593f0ddc7cSNeel Natu * We don't allow the ID register to be modified so reset it back to 1603f0ddc7cSNeel Natu * its default value. 1613f0ddc7cSNeel Natu */ 162de5ea6b6SNeel Natu lapic = vlapic->apic_page; 1633f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 1644f8be175SNeel Natu } 1654f8be175SNeel Natu 1662e25737aSNeel Natu static int 1672e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr) 1682e25737aSNeel Natu { 1692e25737aSNeel Natu switch (dcr & 0xB) { 170117e8f37SPeter Grehan case APIC_TDCR_1: 171117e8f37SPeter Grehan return (1); 1722e25737aSNeel Natu case APIC_TDCR_2: 1732e25737aSNeel Natu return (2); 1742e25737aSNeel Natu case APIC_TDCR_4: 1752e25737aSNeel Natu return (4); 1762e25737aSNeel Natu case APIC_TDCR_8: 1772e25737aSNeel Natu return (8); 1782e25737aSNeel Natu case APIC_TDCR_16: 1792e25737aSNeel Natu return (16); 1802e25737aSNeel Natu case APIC_TDCR_32: 1812e25737aSNeel Natu return (32); 1822e25737aSNeel Natu case APIC_TDCR_64: 1832e25737aSNeel Natu return (64); 1842e25737aSNeel Natu case APIC_TDCR_128: 1852e25737aSNeel Natu return (128); 1862e25737aSNeel Natu default: 1872e25737aSNeel Natu panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr); 1882e25737aSNeel Natu } 1892e25737aSNeel Natu } 1902e25737aSNeel Natu 191366f6083SPeter Grehan #if 0 192366f6083SPeter Grehan static inline void 193366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt) 194366f6083SPeter Grehan { 195366f6083SPeter Grehan printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset, 196366f6083SPeter Grehan *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS, 197366f6083SPeter Grehan *lvt & APIC_LVTT_M); 198366f6083SPeter Grehan } 199366f6083SPeter Grehan #endif 200366f6083SPeter Grehan 201fb03ca4eSNeel Natu static uint32_t 202366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic) 203366f6083SPeter Grehan { 204fb03ca4eSNeel Natu struct bintime bt_now, bt_rem; 2052062ce99SRobert Wing struct LAPIC *lapic __diagused; 206fb03ca4eSNeel Natu uint32_t ccr; 207fb03ca4eSNeel Natu 208fb03ca4eSNeel Natu ccr = 0; 209de5ea6b6SNeel Natu lapic = vlapic->apic_page; 210fb03ca4eSNeel Natu 211fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 212fb03ca4eSNeel Natu if (callout_active(&vlapic->callout)) { 213fb03ca4eSNeel Natu /* 214fb03ca4eSNeel Natu * If the timer is scheduled to expire in the future then 215fb03ca4eSNeel Natu * compute the value of 'ccr' based on the remaining time. 216fb03ca4eSNeel Natu */ 217fb03ca4eSNeel Natu binuptime(&bt_now); 218fb03ca4eSNeel Natu if (bintime_cmp(&vlapic->timer_fire_bt, &bt_now, >)) { 219fb03ca4eSNeel Natu bt_rem = vlapic->timer_fire_bt; 220fb03ca4eSNeel Natu bintime_sub(&bt_rem, &bt_now); 221fb03ca4eSNeel Natu ccr += bt_rem.sec * BT2FREQ(&vlapic->timer_freq_bt); 222fb03ca4eSNeel Natu ccr += bt_rem.frac / vlapic->timer_freq_bt.frac; 223fb03ca4eSNeel Natu } 224fb03ca4eSNeel Natu } 225fb03ca4eSNeel Natu KASSERT(ccr <= lapic->icr_timer, ("vlapic_get_ccr: invalid ccr %#x, " 226fb03ca4eSNeel Natu "icr_timer is %#x", ccr, lapic->icr_timer)); 227fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic ccr_timer = %#x, icr_timer = %#x", 228fb03ca4eSNeel Natu ccr, lapic->icr_timer); 229fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 230fb03ca4eSNeel Natu return (ccr); 231fb03ca4eSNeel Natu } 232fb03ca4eSNeel Natu 233fafe8844SNeel Natu void 234fafe8844SNeel Natu vlapic_dcr_write_handler(struct vlapic *vlapic) 235fb03ca4eSNeel Natu { 236fb03ca4eSNeel Natu struct LAPIC *lapic; 237fb03ca4eSNeel Natu int divisor; 238fb03ca4eSNeel Natu 239de5ea6b6SNeel Natu lapic = vlapic->apic_page; 240fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 241fb03ca4eSNeel Natu 242fafe8844SNeel Natu divisor = vlapic_timer_divisor(lapic->dcr_timer); 243fafe8844SNeel Natu VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", 244fafe8844SNeel Natu lapic->dcr_timer, divisor); 245fb03ca4eSNeel Natu 246fb03ca4eSNeel Natu /* 247fb03ca4eSNeel Natu * Update the timer frequency and the timer period. 248fb03ca4eSNeel Natu * 249fb03ca4eSNeel Natu * XXX changes to the frequency divider will not take effect until 250fb03ca4eSNeel Natu * the timer is reloaded. 251fb03ca4eSNeel Natu */ 252fb03ca4eSNeel Natu FREQ2BT(VLAPIC_BUS_FREQ / divisor, &vlapic->timer_freq_bt); 253fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 254fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 255fb03ca4eSNeel Natu 256fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 257366f6083SPeter Grehan } 258366f6083SPeter Grehan 259fafe8844SNeel Natu void 260fafe8844SNeel Natu vlapic_esr_write_handler(struct vlapic *vlapic) 261366f6083SPeter Grehan { 262de5ea6b6SNeel Natu struct LAPIC *lapic; 263de5ea6b6SNeel Natu 264de5ea6b6SNeel Natu lapic = vlapic->apic_page; 265330baf58SJohn Baldwin lapic->esr = vlapic->esr_pending; 266330baf58SJohn Baldwin vlapic->esr_pending = 0; 267366f6083SPeter Grehan } 268366f6083SPeter Grehan 2694d1e82a8SNeel Natu int 270b5b28fc9SNeel Natu vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level) 271366f6083SPeter Grehan { 2724d1e82a8SNeel Natu struct LAPIC *lapic; 273b5b28fc9SNeel Natu uint32_t *irrptr, *tmrptr, mask; 274366f6083SPeter Grehan int idx; 275366f6083SPeter Grehan 2764d1e82a8SNeel Natu KASSERT(vector >= 0 && vector < 256, ("invalid vector %d", vector)); 277366f6083SPeter Grehan 2784d1e82a8SNeel Natu lapic = vlapic->apic_page; 2791c052192SNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) { 2801c052192SNeel Natu VLAPIC_CTR1(vlapic, "vlapic is software disabled, ignoring " 2811c052192SNeel Natu "interrupt %d", vector); 2824d1e82a8SNeel Natu return (0); 2831c052192SNeel Natu } 2841c052192SNeel Natu 285330baf58SJohn Baldwin if (vector < 16) { 2866a1e1c2cSJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR, 2876a1e1c2cSJohn Baldwin false); 2884d1e82a8SNeel Natu VLAPIC_CTR1(vlapic, "vlapic ignoring interrupt to vector %d", 2894d1e82a8SNeel Natu vector); 2904d1e82a8SNeel Natu return (1); 291330baf58SJohn Baldwin } 292330baf58SJohn Baldwin 29388c4b8d1SNeel Natu if (vlapic->ops.set_intr_ready) 29488c4b8d1SNeel Natu return ((*vlapic->ops.set_intr_ready)(vlapic, vector, level)); 29588c4b8d1SNeel Natu 296366f6083SPeter Grehan idx = (vector / 32) * 4; 297b5b28fc9SNeel Natu mask = 1 << (vector % 32); 298b5b28fc9SNeel Natu 299366f6083SPeter Grehan irrptr = &lapic->irr0; 300b5b28fc9SNeel Natu atomic_set_int(&irrptr[idx], mask); 301b5b28fc9SNeel Natu 302b5b28fc9SNeel Natu /* 3035b8a8cd1SNeel Natu * Verify that the trigger-mode of the interrupt matches with 3045b8a8cd1SNeel Natu * the vlapic TMR registers. 305b5b28fc9SNeel Natu */ 306b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 307294d0d88SNeel Natu if ((tmrptr[idx] & mask) != (level ? mask : 0)) { 308294d0d88SNeel Natu VLAPIC_CTR3(vlapic, "vlapic TMR[%d] is 0x%08x but " 309294d0d88SNeel Natu "interrupt is %s-triggered", idx / 4, tmrptr[idx], 310294d0d88SNeel Natu level ? "level" : "edge"); 311294d0d88SNeel Natu } 312b5b28fc9SNeel Natu 313366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready"); 3144d1e82a8SNeel Natu return (1); 315366f6083SPeter Grehan } 316366f6083SPeter Grehan 317366f6083SPeter Grehan static __inline uint32_t * 318fb03ca4eSNeel Natu vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset) 319366f6083SPeter Grehan { 320de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 321366f6083SPeter Grehan int i; 322366f6083SPeter Grehan 323330baf58SJohn Baldwin switch (offset) { 324330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 325330baf58SJohn Baldwin return (&lapic->lvt_cmci); 326330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 327366f6083SPeter Grehan i = (offset - APIC_OFFSET_TIMER_LVT) >> 2; 328ba084c18SEd Maste return ((&lapic->lvt_timer) + i); 329330baf58SJohn Baldwin default: 330330baf58SJohn Baldwin panic("vlapic_get_lvt: invalid LVT\n"); 331330baf58SJohn Baldwin } 332366f6083SPeter Grehan } 333366f6083SPeter Grehan 3347c05bc31SNeel Natu static __inline int 3357c05bc31SNeel Natu lvt_off_to_idx(uint32_t offset) 3367c05bc31SNeel Natu { 3377c05bc31SNeel Natu int index; 3387c05bc31SNeel Natu 3397c05bc31SNeel Natu switch (offset) { 3407c05bc31SNeel Natu case APIC_OFFSET_CMCI_LVT: 3417c05bc31SNeel Natu index = APIC_LVT_CMCI; 3427c05bc31SNeel Natu break; 3437c05bc31SNeel Natu case APIC_OFFSET_TIMER_LVT: 3447c05bc31SNeel Natu index = APIC_LVT_TIMER; 3457c05bc31SNeel Natu break; 3467c05bc31SNeel Natu case APIC_OFFSET_THERM_LVT: 3477c05bc31SNeel Natu index = APIC_LVT_THERMAL; 3487c05bc31SNeel Natu break; 3497c05bc31SNeel Natu case APIC_OFFSET_PERF_LVT: 3507c05bc31SNeel Natu index = APIC_LVT_PMC; 3517c05bc31SNeel Natu break; 3527c05bc31SNeel Natu case APIC_OFFSET_LINT0_LVT: 3537c05bc31SNeel Natu index = APIC_LVT_LINT0; 3547c05bc31SNeel Natu break; 3557c05bc31SNeel Natu case APIC_OFFSET_LINT1_LVT: 3567c05bc31SNeel Natu index = APIC_LVT_LINT1; 3577c05bc31SNeel Natu break; 3587c05bc31SNeel Natu case APIC_OFFSET_ERROR_LVT: 3597c05bc31SNeel Natu index = APIC_LVT_ERROR; 3607c05bc31SNeel Natu break; 3617c05bc31SNeel Natu default: 3627c05bc31SNeel Natu index = -1; 3637c05bc31SNeel Natu break; 3647c05bc31SNeel Natu } 3657c05bc31SNeel Natu KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: " 3667c05bc31SNeel Natu "invalid lvt index %d for offset %#x", index, offset)); 3677c05bc31SNeel Natu 3687c05bc31SNeel Natu return (index); 3697c05bc31SNeel Natu } 3707c05bc31SNeel Natu 371fb03ca4eSNeel Natu static __inline uint32_t 372fb03ca4eSNeel Natu vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset) 373fb03ca4eSNeel Natu { 3747c05bc31SNeel Natu int idx; 3757c05bc31SNeel Natu uint32_t val; 376fb03ca4eSNeel Natu 3777c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 3787c05bc31SNeel Natu val = atomic_load_acq_32(&vlapic->lvt_last[idx]); 3797c05bc31SNeel Natu return (val); 380fb03ca4eSNeel Natu } 381fb03ca4eSNeel Natu 3827c05bc31SNeel Natu void 3837c05bc31SNeel Natu vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset) 384fb03ca4eSNeel Natu { 3857c05bc31SNeel Natu uint32_t *lvtptr, mask, val; 386fb03ca4eSNeel Natu struct LAPIC *lapic; 3877c05bc31SNeel Natu int idx; 388fb03ca4eSNeel Natu 389de5ea6b6SNeel Natu lapic = vlapic->apic_page; 390fb03ca4eSNeel Natu lvtptr = vlapic_get_lvtptr(vlapic, offset); 3917c05bc31SNeel Natu val = *lvtptr; 3927c05bc31SNeel Natu idx = lvt_off_to_idx(offset); 393fb03ca4eSNeel Natu 394fb03ca4eSNeel Natu if (!(lapic->svr & APIC_SVR_ENABLE)) 395fb03ca4eSNeel Natu val |= APIC_LVT_M; 396330baf58SJohn Baldwin mask = APIC_LVT_M | APIC_LVT_DS | APIC_LVT_VECTOR; 397330baf58SJohn Baldwin switch (offset) { 398330baf58SJohn Baldwin case APIC_OFFSET_TIMER_LVT: 399330baf58SJohn Baldwin mask |= APIC_LVTT_TM; 400330baf58SJohn Baldwin break; 401330baf58SJohn Baldwin case APIC_OFFSET_ERROR_LVT: 402330baf58SJohn Baldwin break; 403330baf58SJohn Baldwin case APIC_OFFSET_LINT0_LVT: 404330baf58SJohn Baldwin case APIC_OFFSET_LINT1_LVT: 405330baf58SJohn Baldwin mask |= APIC_LVT_TM | APIC_LVT_RIRR | APIC_LVT_IIPP; 406330baf58SJohn Baldwin /* FALLTHROUGH */ 407330baf58SJohn Baldwin default: 408330baf58SJohn Baldwin mask |= APIC_LVT_DM; 409330baf58SJohn Baldwin break; 410330baf58SJohn Baldwin } 4117c05bc31SNeel Natu val &= mask; 4127c05bc31SNeel Natu *lvtptr = val; 4137c05bc31SNeel Natu atomic_store_rel_32(&vlapic->lvt_last[idx], val); 4147c05bc31SNeel Natu } 415fb03ca4eSNeel Natu 4167c05bc31SNeel Natu static void 4177c05bc31SNeel Natu vlapic_mask_lvts(struct vlapic *vlapic) 4187c05bc31SNeel Natu { 4197c05bc31SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 4207c05bc31SNeel Natu 4217c05bc31SNeel Natu lapic->lvt_cmci |= APIC_LVT_M; 4227c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT); 4237c05bc31SNeel Natu 4247c05bc31SNeel Natu lapic->lvt_timer |= APIC_LVT_M; 4257c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT); 4267c05bc31SNeel Natu 4277c05bc31SNeel Natu lapic->lvt_thermal |= APIC_LVT_M; 4287c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT); 4297c05bc31SNeel Natu 4307c05bc31SNeel Natu lapic->lvt_pcint |= APIC_LVT_M; 4317c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT); 4327c05bc31SNeel Natu 4337c05bc31SNeel Natu lapic->lvt_lint0 |= APIC_LVT_M; 4347c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT); 4357c05bc31SNeel Natu 4367c05bc31SNeel Natu lapic->lvt_lint1 |= APIC_LVT_M; 4377c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT); 4387c05bc31SNeel Natu 4397c05bc31SNeel Natu lapic->lvt_error |= APIC_LVT_M; 4407c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT); 441fb03ca4eSNeel Natu } 442fb03ca4eSNeel Natu 443330baf58SJohn Baldwin static int 4446a1e1c2cSJohn Baldwin vlapic_fire_lvt(struct vlapic *vlapic, u_int lvt) 445330baf58SJohn Baldwin { 4466a1e1c2cSJohn Baldwin uint32_t mode, reg, vec; 447330baf58SJohn Baldwin 4486a1e1c2cSJohn Baldwin reg = atomic_load_acq_32(&vlapic->lvt_last[lvt]); 4496a1e1c2cSJohn Baldwin 4506a1e1c2cSJohn Baldwin if (reg & APIC_LVT_M) 451330baf58SJohn Baldwin return (0); 4526a1e1c2cSJohn Baldwin vec = reg & APIC_LVT_VECTOR; 4536a1e1c2cSJohn Baldwin mode = reg & APIC_LVT_DM; 454330baf58SJohn Baldwin 455330baf58SJohn Baldwin switch (mode) { 456330baf58SJohn Baldwin case APIC_LVT_DM_FIXED: 457330baf58SJohn Baldwin if (vec < 16) { 4586a1e1c2cSJohn Baldwin vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, 4596a1e1c2cSJohn Baldwin lvt == APIC_LVT_ERROR); 460330baf58SJohn Baldwin return (0); 461330baf58SJohn Baldwin } 4624d1e82a8SNeel Natu if (vlapic_set_intr_ready(vlapic, vec, false)) 463de5ea6b6SNeel Natu vcpu_notify_event(vlapic->vm, vlapic->vcpuid, true); 464330baf58SJohn Baldwin break; 465330baf58SJohn Baldwin case APIC_LVT_DM_NMI: 466330baf58SJohn Baldwin vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 467330baf58SJohn Baldwin break; 468762fd208STycho Nightingale case APIC_LVT_DM_EXTINT: 4690775fbb4STycho Nightingale vm_inject_extint(vlapic->vm, vlapic->vcpuid); 470762fd208STycho Nightingale break; 471330baf58SJohn Baldwin default: 472330baf58SJohn Baldwin // Other modes ignored 473330baf58SJohn Baldwin return (0); 474330baf58SJohn Baldwin } 475330baf58SJohn Baldwin return (1); 476330baf58SJohn Baldwin } 477330baf58SJohn Baldwin 478366f6083SPeter Grehan #if 1 479366f6083SPeter Grehan static void 480366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic) 481366f6083SPeter Grehan { 482366f6083SPeter Grehan int i; 483366f6083SPeter Grehan uint32_t *isrptr; 484366f6083SPeter Grehan 485de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 486366f6083SPeter Grehan for (i = 0; i < 8; i++) 487366f6083SPeter Grehan printf("ISR%d 0x%08x\n", i, isrptr[i * 4]); 488366f6083SPeter Grehan 489366f6083SPeter Grehan for (i = 0; i <= vlapic->isrvec_stk_top; i++) 490366f6083SPeter Grehan printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]); 491366f6083SPeter Grehan } 492366f6083SPeter Grehan #endif 493366f6083SPeter Grehan 494366f6083SPeter Grehan /* 495366f6083SPeter Grehan * Algorithm adopted from section "Interrupt, Task and Processor Priority" 496366f6083SPeter Grehan * in Intel Architecture Manual Vol 3a. 497366f6083SPeter Grehan */ 498366f6083SPeter Grehan static void 499366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic) 500366f6083SPeter Grehan { 501366f6083SPeter Grehan int isrvec, tpr, ppr; 502366f6083SPeter Grehan 503366f6083SPeter Grehan /* 504366f6083SPeter Grehan * Note that the value on the stack at index 0 is always 0. 505366f6083SPeter Grehan * 506366f6083SPeter Grehan * This is a placeholder for the value of ISRV when none of the 507366f6083SPeter Grehan * bits is set in the ISRx registers. 508366f6083SPeter Grehan */ 509366f6083SPeter Grehan isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top]; 510de5ea6b6SNeel Natu tpr = vlapic->apic_page->tpr; 511366f6083SPeter Grehan 512366f6083SPeter Grehan #if 1 513366f6083SPeter Grehan { 514366f6083SPeter Grehan int i, lastprio, curprio, vector, idx; 515366f6083SPeter Grehan uint32_t *isrptr; 516366f6083SPeter Grehan 517366f6083SPeter Grehan if (vlapic->isrvec_stk_top == 0 && isrvec != 0) 518366f6083SPeter Grehan panic("isrvec_stk is corrupted: %d", isrvec); 519366f6083SPeter Grehan 520366f6083SPeter Grehan /* 521366f6083SPeter Grehan * Make sure that the priority of the nested interrupts is 522366f6083SPeter Grehan * always increasing. 523366f6083SPeter Grehan */ 524366f6083SPeter Grehan lastprio = -1; 525366f6083SPeter Grehan for (i = 1; i <= vlapic->isrvec_stk_top; i++) { 526366f6083SPeter Grehan curprio = PRIO(vlapic->isrvec_stk[i]); 527366f6083SPeter Grehan if (curprio <= lastprio) { 528366f6083SPeter Grehan dump_isrvec_stk(vlapic); 529366f6083SPeter Grehan panic("isrvec_stk does not satisfy invariant"); 530366f6083SPeter Grehan } 531366f6083SPeter Grehan lastprio = curprio; 532366f6083SPeter Grehan } 533366f6083SPeter Grehan 534366f6083SPeter Grehan /* 535366f6083SPeter Grehan * Make sure that each bit set in the ISRx registers has a 536366f6083SPeter Grehan * corresponding entry on the isrvec stack. 537366f6083SPeter Grehan */ 538366f6083SPeter Grehan i = 1; 539de5ea6b6SNeel Natu isrptr = &vlapic->apic_page->isr0; 540366f6083SPeter Grehan for (vector = 0; vector < 256; vector++) { 541366f6083SPeter Grehan idx = (vector / 32) * 4; 542366f6083SPeter Grehan if (isrptr[idx] & (1 << (vector % 32))) { 543366f6083SPeter Grehan if (i > vlapic->isrvec_stk_top || 544366f6083SPeter Grehan vlapic->isrvec_stk[i] != vector) { 545366f6083SPeter Grehan dump_isrvec_stk(vlapic); 546366f6083SPeter Grehan panic("ISR and isrvec_stk out of sync"); 547366f6083SPeter Grehan } 548366f6083SPeter Grehan i++; 549366f6083SPeter Grehan } 550366f6083SPeter Grehan } 551366f6083SPeter Grehan } 552366f6083SPeter Grehan #endif 553366f6083SPeter Grehan 554366f6083SPeter Grehan if (PRIO(tpr) >= PRIO(isrvec)) 555366f6083SPeter Grehan ppr = tpr; 556366f6083SPeter Grehan else 557366f6083SPeter Grehan ppr = isrvec & 0xf0; 558366f6083SPeter Grehan 559de5ea6b6SNeel Natu vlapic->apic_page->ppr = ppr; 560366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr); 561366f6083SPeter Grehan } 562366f6083SPeter Grehan 5631bc51badSMichael Reifenberger void 5641bc51badSMichael Reifenberger vlapic_sync_tpr(struct vlapic *vlapic) 5651bc51badSMichael Reifenberger { 5661bc51badSMichael Reifenberger vlapic_update_ppr(vlapic); 5671bc51badSMichael Reifenberger } 5681bc51badSMichael Reifenberger 56944e2f0feSNeel Natu static VMM_STAT(VLAPIC_GRATUITOUS_EOI, "EOI without any in-service interrupt"); 57044e2f0feSNeel Natu 571366f6083SPeter Grehan static void 572366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic) 573366f6083SPeter Grehan { 574de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 575b5b28fc9SNeel Natu uint32_t *isrptr, *tmrptr; 576b5b28fc9SNeel Natu int i, idx, bitpos, vector; 577366f6083SPeter Grehan 578366f6083SPeter Grehan isrptr = &lapic->isr0; 579b5b28fc9SNeel Natu tmrptr = &lapic->tmr0; 580366f6083SPeter Grehan 58144e2f0feSNeel Natu for (i = 7; i >= 0; i--) { 582366f6083SPeter Grehan idx = i * 4; 583366f6083SPeter Grehan bitpos = fls(isrptr[idx]); 584b5b28fc9SNeel Natu if (bitpos-- != 0) { 585366f6083SPeter Grehan if (vlapic->isrvec_stk_top <= 0) { 586366f6083SPeter Grehan panic("invalid vlapic isrvec_stk_top %d", 587366f6083SPeter Grehan vlapic->isrvec_stk_top); 588366f6083SPeter Grehan } 589b5b28fc9SNeel Natu isrptr[idx] &= ~(1 << bitpos); 59044e2f0feSNeel Natu vector = i * 32 + bitpos; 59144e2f0feSNeel Natu VCPU_CTR1(vlapic->vm, vlapic->vcpuid, "EOI vector %d", 59244e2f0feSNeel Natu vector); 593366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi"); 594366f6083SPeter Grehan vlapic->isrvec_stk_top--; 595366f6083SPeter Grehan vlapic_update_ppr(vlapic); 596b5b28fc9SNeel Natu if ((tmrptr[idx] & (1 << bitpos)) != 0) { 597b5b28fc9SNeel Natu vioapic_process_eoi(vlapic->vm, vlapic->vcpuid, 598b5b28fc9SNeel Natu vector); 599b5b28fc9SNeel Natu } 600366f6083SPeter Grehan return; 601366f6083SPeter Grehan } 602366f6083SPeter Grehan } 60344e2f0feSNeel Natu VCPU_CTR0(vlapic->vm, vlapic->vcpuid, "Gratuitous EOI"); 60444e2f0feSNeel Natu vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_GRATUITOUS_EOI, 1); 605366f6083SPeter Grehan } 606366f6083SPeter Grehan 607366f6083SPeter Grehan static __inline int 608fb03ca4eSNeel Natu vlapic_get_lvt_field(uint32_t lvt, uint32_t mask) 609366f6083SPeter Grehan { 610fb03ca4eSNeel Natu 611fb03ca4eSNeel Natu return (lvt & mask); 612366f6083SPeter Grehan } 613366f6083SPeter Grehan 614366f6083SPeter Grehan static __inline int 615366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic) 616366f6083SPeter Grehan { 617fb03ca4eSNeel Natu uint32_t lvt; 618366f6083SPeter Grehan 619366f6083SPeter Grehan lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT); 620366f6083SPeter Grehan 621366f6083SPeter Grehan return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC)); 622366f6083SPeter Grehan } 623366f6083SPeter Grehan 624330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_ERROR, "error interrupts generated by vlapic"); 625330baf58SJohn Baldwin 6266a1e1c2cSJohn Baldwin static void 6276a1e1c2cSJohn Baldwin vlapic_set_error(struct vlapic *vlapic, uint32_t mask, bool lvt_error) 628330baf58SJohn Baldwin { 629330baf58SJohn Baldwin 630330baf58SJohn Baldwin vlapic->esr_pending |= mask; 631330baf58SJohn Baldwin 6326a1e1c2cSJohn Baldwin /* 6336a1e1c2cSJohn Baldwin * Avoid infinite recursion if the error LVT itself is configured with 6346a1e1c2cSJohn Baldwin * an illegal vector. 6356a1e1c2cSJohn Baldwin */ 6366a1e1c2cSJohn Baldwin if (lvt_error) 6376a1e1c2cSJohn Baldwin return; 6386a1e1c2cSJohn Baldwin 6396a1e1c2cSJohn Baldwin if (vlapic_fire_lvt(vlapic, APIC_LVT_ERROR)) { 640330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_ERROR, 1); 641330baf58SJohn Baldwin } 642330baf58SJohn Baldwin } 643330baf58SJohn Baldwin 64477d8fd9bSNeel Natu static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic"); 64577d8fd9bSNeel Natu 646366f6083SPeter Grehan static void 647366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic) 648366f6083SPeter Grehan { 649fb03ca4eSNeel Natu 650fb03ca4eSNeel Natu KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked")); 651366f6083SPeter Grehan 6526a1e1c2cSJohn Baldwin if (vlapic_fire_lvt(vlapic, APIC_LVT_TIMER)) { 6539d8d8e3eSNeel Natu VLAPIC_CTR0(vlapic, "vlapic timer fired"); 65477d8fd9bSNeel Natu vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1); 655366f6083SPeter Grehan } 656366f6083SPeter Grehan } 657366f6083SPeter Grehan 658330baf58SJohn Baldwin static VMM_STAT(VLAPIC_INTR_CMC, 659330baf58SJohn Baldwin "corrected machine check interrupts generated by vlapic"); 660330baf58SJohn Baldwin 661330baf58SJohn Baldwin void 662330baf58SJohn Baldwin vlapic_fire_cmci(struct vlapic *vlapic) 663330baf58SJohn Baldwin { 664330baf58SJohn Baldwin 6656a1e1c2cSJohn Baldwin if (vlapic_fire_lvt(vlapic, APIC_LVT_CMCI)) { 666330baf58SJohn Baldwin vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_CMC, 1); 667330baf58SJohn Baldwin } 668330baf58SJohn Baldwin } 669330baf58SJohn Baldwin 6707c05bc31SNeel Natu static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1, 671330baf58SJohn Baldwin "lvts triggered"); 672330baf58SJohn Baldwin 673330baf58SJohn Baldwin int 674330baf58SJohn Baldwin vlapic_trigger_lvt(struct vlapic *vlapic, int vector) 675330baf58SJohn Baldwin { 676330baf58SJohn Baldwin 677762fd208STycho Nightingale if (vlapic_enabled(vlapic) == false) { 678762fd208STycho Nightingale /* 679762fd208STycho Nightingale * When the local APIC is global/hardware disabled, 680762fd208STycho Nightingale * LINT[1:0] pins are configured as INTR and NMI pins, 681762fd208STycho Nightingale * respectively. 682762fd208STycho Nightingale */ 683762fd208STycho Nightingale switch (vector) { 684762fd208STycho Nightingale case APIC_LVT_LINT0: 6850775fbb4STycho Nightingale vm_inject_extint(vlapic->vm, vlapic->vcpuid); 686762fd208STycho Nightingale break; 687762fd208STycho Nightingale case APIC_LVT_LINT1: 688762fd208STycho Nightingale vm_inject_nmi(vlapic->vm, vlapic->vcpuid); 689762fd208STycho Nightingale break; 690762fd208STycho Nightingale default: 691762fd208STycho Nightingale break; 692762fd208STycho Nightingale } 693762fd208STycho Nightingale return (0); 694762fd208STycho Nightingale } 695762fd208STycho Nightingale 696330baf58SJohn Baldwin switch (vector) { 697330baf58SJohn Baldwin case APIC_LVT_LINT0: 698330baf58SJohn Baldwin case APIC_LVT_LINT1: 699330baf58SJohn Baldwin case APIC_LVT_TIMER: 700330baf58SJohn Baldwin case APIC_LVT_ERROR: 701330baf58SJohn Baldwin case APIC_LVT_PMC: 702330baf58SJohn Baldwin case APIC_LVT_THERMAL: 703330baf58SJohn Baldwin case APIC_LVT_CMCI: 7046a1e1c2cSJohn Baldwin if (vlapic_fire_lvt(vlapic, vector)) { 7056a1e1c2cSJohn Baldwin vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 7066a1e1c2cSJohn Baldwin LVTS_TRIGGERRED, vector, 1); 7076a1e1c2cSJohn Baldwin } 708330baf58SJohn Baldwin break; 709330baf58SJohn Baldwin default: 710330baf58SJohn Baldwin return (EINVAL); 711330baf58SJohn Baldwin } 712330baf58SJohn Baldwin return (0); 713330baf58SJohn Baldwin } 714330baf58SJohn Baldwin 715fb03ca4eSNeel Natu static void 7164c812fe6SMark Johnston vlapic_callout_reset(struct vlapic *vlapic, sbintime_t t) 7174c812fe6SMark Johnston { 7184c812fe6SMark Johnston callout_reset_sbt_curcpu(&vlapic->callout, t, 0, 7194c812fe6SMark Johnston vlapic_callout_handler, vlapic, 0); 7204c812fe6SMark Johnston } 7214c812fe6SMark Johnston 7224c812fe6SMark Johnston static void 723fb03ca4eSNeel Natu vlapic_callout_handler(void *arg) 724fb03ca4eSNeel Natu { 725fb03ca4eSNeel Natu struct vlapic *vlapic; 726fb03ca4eSNeel Natu struct bintime bt, btnow; 727fb03ca4eSNeel Natu sbintime_t rem_sbt; 728fb03ca4eSNeel Natu 729fb03ca4eSNeel Natu vlapic = arg; 730fb03ca4eSNeel Natu 731fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 732fb03ca4eSNeel Natu if (callout_pending(&vlapic->callout)) /* callout was reset */ 733fb03ca4eSNeel Natu goto done; 734fb03ca4eSNeel Natu 735fb03ca4eSNeel Natu if (!callout_active(&vlapic->callout)) /* callout was stopped */ 736fb03ca4eSNeel Natu goto done; 737fb03ca4eSNeel Natu 738fb03ca4eSNeel Natu callout_deactivate(&vlapic->callout); 739fb03ca4eSNeel Natu 740fb03ca4eSNeel Natu vlapic_fire_timer(vlapic); 741fb03ca4eSNeel Natu 742fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) { 743fb03ca4eSNeel Natu binuptime(&btnow); 744fb03ca4eSNeel Natu KASSERT(bintime_cmp(&btnow, &vlapic->timer_fire_bt, >=), 745fb03ca4eSNeel Natu ("vlapic callout at %#lx.%#lx, expected at %#lx.#%lx", 746fb03ca4eSNeel Natu btnow.sec, btnow.frac, vlapic->timer_fire_bt.sec, 747fb03ca4eSNeel Natu vlapic->timer_fire_bt.frac)); 748fb03ca4eSNeel Natu 749fb03ca4eSNeel Natu /* 750fb03ca4eSNeel Natu * Compute the delta between when the timer was supposed to 751fb03ca4eSNeel Natu * fire and the present time. 752fb03ca4eSNeel Natu */ 753fb03ca4eSNeel Natu bt = btnow; 754fb03ca4eSNeel Natu bintime_sub(&bt, &vlapic->timer_fire_bt); 755fb03ca4eSNeel Natu 756fb03ca4eSNeel Natu rem_sbt = bttosbt(vlapic->timer_period_bt); 757fb03ca4eSNeel Natu if (bintime_cmp(&bt, &vlapic->timer_period_bt, <)) { 758fb03ca4eSNeel Natu /* 759fb03ca4eSNeel Natu * Adjust the time until the next countdown downward 760fb03ca4eSNeel Natu * to account for the lost time. 761fb03ca4eSNeel Natu */ 762fb03ca4eSNeel Natu rem_sbt -= bttosbt(bt); 763fb03ca4eSNeel Natu } else { 764fb03ca4eSNeel Natu /* 765fb03ca4eSNeel Natu * If the delta is greater than the timer period then 766fb03ca4eSNeel Natu * just reset our time base instead of trying to catch 767fb03ca4eSNeel Natu * up. 768fb03ca4eSNeel Natu */ 769fb03ca4eSNeel Natu vlapic->timer_fire_bt = btnow; 770fb03ca4eSNeel Natu VLAPIC_CTR2(vlapic, "vlapic timer lagging by %lu " 771fb03ca4eSNeel Natu "usecs, period is %lu usecs - resetting time base", 772fb03ca4eSNeel Natu bttosbt(bt) / SBT_1US, 773fb03ca4eSNeel Natu bttosbt(vlapic->timer_period_bt) / SBT_1US); 774fb03ca4eSNeel Natu } 775fb03ca4eSNeel Natu 776fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 7774c812fe6SMark Johnston vlapic_callout_reset(vlapic, rem_sbt); 778fb03ca4eSNeel Natu } 779fb03ca4eSNeel Natu done: 780fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 781fb03ca4eSNeel Natu } 782fb03ca4eSNeel Natu 783fafe8844SNeel Natu void 784fafe8844SNeel Natu vlapic_icrtmr_write_handler(struct vlapic *vlapic) 785fb03ca4eSNeel Natu { 786fb03ca4eSNeel Natu struct LAPIC *lapic; 787fb03ca4eSNeel Natu sbintime_t sbt; 788fafe8844SNeel Natu uint32_t icr_timer; 789fb03ca4eSNeel Natu 790fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 791fb03ca4eSNeel Natu 792de5ea6b6SNeel Natu lapic = vlapic->apic_page; 793fafe8844SNeel Natu icr_timer = lapic->icr_timer; 794fb03ca4eSNeel Natu 795fb03ca4eSNeel Natu vlapic->timer_period_bt = vlapic->timer_freq_bt; 796fb03ca4eSNeel Natu bintime_mul(&vlapic->timer_period_bt, icr_timer); 797fb03ca4eSNeel Natu 798fb03ca4eSNeel Natu if (icr_timer != 0) { 799fb03ca4eSNeel Natu binuptime(&vlapic->timer_fire_bt); 800fb03ca4eSNeel Natu bintime_add(&vlapic->timer_fire_bt, &vlapic->timer_period_bt); 801fb03ca4eSNeel Natu 802fb03ca4eSNeel Natu sbt = bttosbt(vlapic->timer_period_bt); 8034c812fe6SMark Johnston vlapic_callout_reset(vlapic, sbt); 804fb03ca4eSNeel Natu } else 805fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 806fb03ca4eSNeel Natu 807fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 808fb03ca4eSNeel Natu } 809fb03ca4eSNeel Natu 8104f8be175SNeel Natu /* 8114f8be175SNeel Natu * This function populates 'dmask' with the set of vcpus that match the 8124f8be175SNeel Natu * addressing specified by the (dest, phys, lowprio) tuple. 8134f8be175SNeel Natu * 8144f8be175SNeel Natu * 'x2apic_dest' specifies whether 'dest' is interpreted as x2APIC (32-bit) 8154f8be175SNeel Natu * or xAPIC (8-bit) destination field. 8164f8be175SNeel Natu */ 8174f8be175SNeel Natu static void 8184f8be175SNeel Natu vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys, 8194f8be175SNeel Natu bool lowprio, bool x2apic_dest) 8204f8be175SNeel Natu { 8214f8be175SNeel Natu struct vlapic *vlapic; 8224f8be175SNeel Natu uint32_t dfr, ldr, ldest, cluster; 8234f8be175SNeel Natu uint32_t mda_flat_ldest, mda_cluster_ldest, mda_ldest, mda_cluster_id; 8244f8be175SNeel Natu cpuset_t amask; 8254f8be175SNeel Natu int vcpuid; 8264f8be175SNeel Natu 8274f8be175SNeel Natu if ((x2apic_dest && dest == 0xffffffff) || 8284f8be175SNeel Natu (!x2apic_dest && dest == 0xff)) { 8294f8be175SNeel Natu /* 8304f8be175SNeel Natu * Broadcast in both logical and physical modes. 8314f8be175SNeel Natu */ 8324f8be175SNeel Natu *dmask = vm_active_cpus(vm); 8334f8be175SNeel Natu return; 8344f8be175SNeel Natu } 8354f8be175SNeel Natu 8364f8be175SNeel Natu if (phys) { 8374f8be175SNeel Natu /* 8384f8be175SNeel Natu * Physical mode: destination is APIC ID. 8394f8be175SNeel Natu */ 8404f8be175SNeel Natu CPU_ZERO(dmask); 8414f8be175SNeel Natu vcpuid = vm_apicid2vcpuid(vm, dest); 842e5506316SKonstantin Belousov amask = vm_active_cpus(vm); 843e5506316SKonstantin Belousov if (vcpuid < vm_get_maxcpus(vm) && CPU_ISSET(vcpuid, &amask)) 8444f8be175SNeel Natu CPU_SET(vcpuid, dmask); 8454f8be175SNeel Natu } else { 8464f8be175SNeel Natu /* 8474f8be175SNeel Natu * In the "Flat Model" the MDA is interpreted as an 8-bit wide 848500eb14aSPedro F. Giffuni * bitmask. This model is only available in the xAPIC mode. 8494f8be175SNeel Natu */ 8504f8be175SNeel Natu mda_flat_ldest = dest & 0xff; 8514f8be175SNeel Natu 8524f8be175SNeel Natu /* 8534f8be175SNeel Natu * In the "Cluster Model" the MDA is used to identify a 8544f8be175SNeel Natu * specific cluster and a set of APICs in that cluster. 8554f8be175SNeel Natu */ 8564f8be175SNeel Natu if (x2apic_dest) { 8574f8be175SNeel Natu mda_cluster_id = dest >> 16; 8584f8be175SNeel Natu mda_cluster_ldest = dest & 0xffff; 8594f8be175SNeel Natu } else { 8604f8be175SNeel Natu mda_cluster_id = (dest >> 4) & 0xf; 8614f8be175SNeel Natu mda_cluster_ldest = dest & 0xf; 8624f8be175SNeel Natu } 8634f8be175SNeel Natu 8644f8be175SNeel Natu /* 8654f8be175SNeel Natu * Logical mode: match each APIC that has a bit set 86628323addSBryan Drewery * in its LDR that matches a bit in the ldest. 8674f8be175SNeel Natu */ 8684f8be175SNeel Natu CPU_ZERO(dmask); 8694f8be175SNeel Natu amask = vm_active_cpus(vm); 870de855429SMark Johnston CPU_FOREACH_ISSET(vcpuid, &amask) { 8714f8be175SNeel Natu vlapic = vm_lapic(vm, vcpuid); 8723f0ddc7cSNeel Natu dfr = vlapic->apic_page->dfr; 8733f0ddc7cSNeel Natu ldr = vlapic->apic_page->ldr; 8744f8be175SNeel Natu 8754f8be175SNeel Natu if ((dfr & APIC_DFR_MODEL_MASK) == 8764f8be175SNeel Natu APIC_DFR_MODEL_FLAT) { 8774f8be175SNeel Natu ldest = ldr >> 24; 8784f8be175SNeel Natu mda_ldest = mda_flat_ldest; 8794f8be175SNeel Natu } else if ((dfr & APIC_DFR_MODEL_MASK) == 8804f8be175SNeel Natu APIC_DFR_MODEL_CLUSTER) { 8814f8be175SNeel Natu if (x2apic(vlapic)) { 8824f8be175SNeel Natu cluster = ldr >> 16; 8834f8be175SNeel Natu ldest = ldr & 0xffff; 8844f8be175SNeel Natu } else { 8854f8be175SNeel Natu cluster = ldr >> 28; 8864f8be175SNeel Natu ldest = (ldr >> 24) & 0xf; 8874f8be175SNeel Natu } 8884f8be175SNeel Natu if (cluster != mda_cluster_id) 8894f8be175SNeel Natu continue; 8904f8be175SNeel Natu mda_ldest = mda_cluster_ldest; 8914f8be175SNeel Natu } else { 8924f8be175SNeel Natu /* 8934f8be175SNeel Natu * Guest has configured a bad logical 8944f8be175SNeel Natu * model for this vcpu - skip it. 8954f8be175SNeel Natu */ 8964f8be175SNeel Natu VLAPIC_CTR1(vlapic, "vlapic has bad logical " 8974f8be175SNeel Natu "model %x - cannot deliver interrupt", dfr); 8984f8be175SNeel Natu continue; 8994f8be175SNeel Natu } 9004f8be175SNeel Natu 9014f8be175SNeel Natu if ((mda_ldest & ldest) != 0) { 9024f8be175SNeel Natu CPU_SET(vcpuid, dmask); 9034f8be175SNeel Natu if (lowprio) 9044f8be175SNeel Natu break; 9054f8be175SNeel Natu } 9064f8be175SNeel Natu } 9074f8be175SNeel Natu } 9084f8be175SNeel Natu } 9094f8be175SNeel Natu 9100acb0d84SNeel Natu static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu"); 9110acb0d84SNeel Natu 912051f2bd1SNeel Natu static void 913051f2bd1SNeel Natu vlapic_set_tpr(struct vlapic *vlapic, uint8_t val) 914051f2bd1SNeel Natu { 915051f2bd1SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 916051f2bd1SNeel Natu 91779ad53fbSNeel Natu if (lapic->tpr != val) { 91879ad53fbSNeel Natu VCPU_CTR2(vlapic->vm, vlapic->vcpuid, "vlapic TPR changed " 91979ad53fbSNeel Natu "from %#x to %#x", lapic->tpr, val); 920051f2bd1SNeel Natu lapic->tpr = val; 921051f2bd1SNeel Natu vlapic_update_ppr(vlapic); 922051f2bd1SNeel Natu } 92379ad53fbSNeel Natu } 924051f2bd1SNeel Natu 925051f2bd1SNeel Natu static uint8_t 926051f2bd1SNeel Natu vlapic_get_tpr(struct vlapic *vlapic) 927051f2bd1SNeel Natu { 928051f2bd1SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 929051f2bd1SNeel Natu 930051f2bd1SNeel Natu return (lapic->tpr); 931051f2bd1SNeel Natu } 932051f2bd1SNeel Natu 933051f2bd1SNeel Natu void 934051f2bd1SNeel Natu vlapic_set_cr8(struct vlapic *vlapic, uint64_t val) 935051f2bd1SNeel Natu { 936051f2bd1SNeel Natu uint8_t tpr; 937051f2bd1SNeel Natu 938051f2bd1SNeel Natu if (val & ~0xf) { 939051f2bd1SNeel Natu vm_inject_gp(vlapic->vm, vlapic->vcpuid); 940051f2bd1SNeel Natu return; 941051f2bd1SNeel Natu } 942051f2bd1SNeel Natu 943051f2bd1SNeel Natu tpr = val << 4; 944051f2bd1SNeel Natu vlapic_set_tpr(vlapic, tpr); 945051f2bd1SNeel Natu } 946051f2bd1SNeel Natu 947051f2bd1SNeel Natu uint64_t 948051f2bd1SNeel Natu vlapic_get_cr8(struct vlapic *vlapic) 949051f2bd1SNeel Natu { 950051f2bd1SNeel Natu uint8_t tpr; 951051f2bd1SNeel Natu 952051f2bd1SNeel Natu tpr = vlapic_get_tpr(vlapic); 953051f2bd1SNeel Natu return (tpr >> 4); 954051f2bd1SNeel Natu } 955051f2bd1SNeel Natu 956fafe8844SNeel Natu int 957fafe8844SNeel Natu vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu) 958366f6083SPeter Grehan { 959366f6083SPeter Grehan int i; 9604f8be175SNeel Natu bool phys; 9610bda8d3eSCorvin Köhne cpuset_t dmask, ipimask; 962fafe8844SNeel Natu uint64_t icrval; 9630bda8d3eSCorvin Köhne uint32_t dest, vec, mode, shorthand; 964edf89256SNeel Natu struct vlapic *vlapic2; 965edf89256SNeel Natu struct vm_exit *vmexit; 966fafe8844SNeel Natu struct LAPIC *lapic; 967fafe8844SNeel Natu 968fafe8844SNeel Natu lapic = vlapic->apic_page; 969fafe8844SNeel Natu lapic->icr_lo &= ~APIC_DELSTAT_PEND; 970fafe8844SNeel Natu icrval = ((uint64_t)lapic->icr_hi << 32) | lapic->icr_lo; 971366f6083SPeter Grehan 972a2da7af6SNeel Natu if (x2apic(vlapic)) 973366f6083SPeter Grehan dest = icrval >> 32; 974a2da7af6SNeel Natu else 975a2da7af6SNeel Natu dest = icrval >> (32 + 24); 976366f6083SPeter Grehan vec = icrval & APIC_VECTOR_MASK; 977366f6083SPeter Grehan mode = icrval & APIC_DELMODE_MASK; 9780bda8d3eSCorvin Köhne phys = (icrval & APIC_DESTMODE_LOG) == 0; 9790bda8d3eSCorvin Köhne shorthand = icrval & APIC_DEST_MASK; 98083b65d0aSEmmanuel Vadot 9814d1e82a8SNeel Natu VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec); 9824d1e82a8SNeel Natu 9830bda8d3eSCorvin Köhne switch (shorthand) { 984366f6083SPeter Grehan case APIC_DEST_DESTFLD: 9850bda8d3eSCorvin Köhne vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false, x2apic(vlapic)); 986366f6083SPeter Grehan break; 987366f6083SPeter Grehan case APIC_DEST_SELF: 988a5615c90SPeter Grehan CPU_SETOF(vlapic->vcpuid, &dmask); 989366f6083SPeter Grehan break; 990366f6083SPeter Grehan case APIC_DEST_ALLISELF: 991366f6083SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 992366f6083SPeter Grehan break; 993366f6083SPeter Grehan case APIC_DEST_ALLESELF: 994a5615c90SPeter Grehan dmask = vm_active_cpus(vlapic->vm); 995a5615c90SPeter Grehan CPU_CLR(vlapic->vcpuid, &dmask); 996366f6083SPeter Grehan break; 9971e2751ddSSergey Kandaurov default: 9980bda8d3eSCorvin Köhne __assert_unreachable(); 9990bda8d3eSCorvin Köhne } 10000bda8d3eSCorvin Köhne 10010bda8d3eSCorvin Köhne /* 10020bda8d3eSCorvin Köhne * ipimask is a set of vCPUs needing userland handling of the current 10030bda8d3eSCorvin Köhne * IPI. 10040bda8d3eSCorvin Köhne */ 10050bda8d3eSCorvin Köhne CPU_ZERO(&ipimask); 10060bda8d3eSCorvin Köhne 10070bda8d3eSCorvin Köhne switch (mode) { 10080bda8d3eSCorvin Köhne case APIC_DELMODE_FIXED: 10090bda8d3eSCorvin Köhne if (vec < 16) { 10100bda8d3eSCorvin Köhne vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, 10110bda8d3eSCorvin Köhne false); 10120bda8d3eSCorvin Köhne VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec); 10130bda8d3eSCorvin Köhne return (0); 1014366f6083SPeter Grehan } 1015366f6083SPeter Grehan 1016de855429SMark Johnston CPU_FOREACH_ISSET(i, &dmask) { 1017b5b28fc9SNeel Natu lapic_intr_edge(vlapic->vm, i, vec); 10180acb0d84SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, 10190acb0d84SNeel Natu IPIS_SENT, i, 1); 10200bda8d3eSCorvin Köhne VLAPIC_CTR2(vlapic, 10210bda8d3eSCorvin Köhne "vlapic sending ipi %d to vcpuid %d", vec, i); 10220bda8d3eSCorvin Köhne } 10230bda8d3eSCorvin Köhne 10240bda8d3eSCorvin Köhne break; 10250bda8d3eSCorvin Köhne case APIC_DELMODE_NMI: 10260bda8d3eSCorvin Köhne CPU_FOREACH_ISSET(i, &dmask) { 1027366f6083SPeter Grehan vm_inject_nmi(vlapic->vm, i); 10280bda8d3eSCorvin Köhne VLAPIC_CTR1(vlapic, 10290bda8d3eSCorvin Köhne "vlapic sending ipi nmi to vcpuid %d", i); 1030366f6083SPeter Grehan } 1031366f6083SPeter Grehan 10320bda8d3eSCorvin Köhne break; 10330bda8d3eSCorvin Köhne case APIC_DELMODE_INIT: 1034edf89256SNeel Natu if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) 10350bda8d3eSCorvin Köhne break; 1036edf89256SNeel Natu 10370bda8d3eSCorvin Köhne CPU_FOREACH_ISSET(i, &dmask) { 10380bda8d3eSCorvin Köhne /* 10390bda8d3eSCorvin Köhne * Userland which doesn't support the IPI exit requires 10400bda8d3eSCorvin Köhne * that the boot state is set to SIPI here. 10410bda8d3eSCorvin Köhne */ 10420bda8d3eSCorvin Köhne vlapic2 = vm_lapic(vlapic->vm, i); 1043edf89256SNeel Natu vlapic2->boot_state = BS_SIPI; 10440bda8d3eSCorvin Köhne CPU_SET(i, &ipimask); 1045edf89256SNeel Natu } 1046edf89256SNeel Natu 10470bda8d3eSCorvin Köhne break; 10480bda8d3eSCorvin Köhne case APIC_DELMODE_STARTUP: 10490bda8d3eSCorvin Köhne CPU_FOREACH_ISSET(i, &dmask) { 10500bda8d3eSCorvin Köhne vlapic2 = vm_lapic(vlapic->vm, i); 1051edf89256SNeel Natu /* 1052edf89256SNeel Natu * Ignore SIPIs in any state other than wait-for-SIPI 1053edf89256SNeel Natu */ 1054edf89256SNeel Natu if (vlapic2->boot_state != BS_SIPI) 10550bda8d3eSCorvin Köhne continue; 1056edf89256SNeel Natu vlapic2->boot_state = BS_RUNNING; 10570bda8d3eSCorvin Köhne CPU_SET(i, &ipimask); 10580bda8d3eSCorvin Köhne } 10590bda8d3eSCorvin Köhne 10600bda8d3eSCorvin Köhne break; 10610bda8d3eSCorvin Köhne default: 10620bda8d3eSCorvin Köhne return (1); 10630bda8d3eSCorvin Köhne } 10640bda8d3eSCorvin Köhne 10650bda8d3eSCorvin Köhne if (!CPU_EMPTY(&ipimask)) { 10660bda8d3eSCorvin Köhne vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); 10670bda8d3eSCorvin Köhne vmexit->exitcode = VM_EXITCODE_IPI; 10680bda8d3eSCorvin Köhne vmexit->u.ipi.mode = mode; 10690bda8d3eSCorvin Köhne vmexit->u.ipi.vector = vec; 10700bda8d3eSCorvin Köhne vmexit->u.ipi.dmask = dmask; 1071edf89256SNeel Natu 1072becd9849SNeel Natu *retu = true; 10730bda8d3eSCorvin Köhne 10740bda8d3eSCorvin Köhne /* 10750bda8d3eSCorvin Köhne * Old bhyve versions don't support the IPI exit. Translate it 10760bda8d3eSCorvin Köhne * into the old style. 10770bda8d3eSCorvin Köhne */ 10780bda8d3eSCorvin Köhne if (!vlapic->ipi_exit) { 10790bda8d3eSCorvin Köhne if (mode == APIC_DELMODE_STARTUP) { 1080a5a918b7SCorvin Köhne vmexit->exitcode = VM_EXITCODE_SPINUP_AP; 10810bda8d3eSCorvin Köhne vmexit->u.spinup_ap.vcpu = CPU_FFS(&ipimask) - 1; 1082a5a918b7SCorvin Köhne vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT; 10830bda8d3eSCorvin Köhne } else { 10840bda8d3eSCorvin Köhne *retu = false; 10850bda8d3eSCorvin Köhne } 10860bda8d3eSCorvin Köhne } 10870bda8d3eSCorvin Köhne } 1088a5a918b7SCorvin Köhne 1089a5a918b7SCorvin Köhne return (0); 1090366f6083SPeter Grehan } 10910bda8d3eSCorvin Köhne 10920bda8d3eSCorvin Köhne static void 10930bda8d3eSCorvin Köhne vlapic_handle_init(struct vm *vm, int vcpuid, void *arg) 10940bda8d3eSCorvin Köhne { 10950bda8d3eSCorvin Köhne struct vlapic *vlapic = vm_lapic(vm, vcpuid); 10960bda8d3eSCorvin Köhne 10970bda8d3eSCorvin Köhne vlapic_reset(vlapic); 10980bda8d3eSCorvin Köhne 10990bda8d3eSCorvin Köhne /* vlapic_reset modifies the boot state. */ 11000bda8d3eSCorvin Köhne vlapic->boot_state = BS_SIPI; 11013fc17484SEmmanuel Vadot } 11023fc17484SEmmanuel Vadot 11030bda8d3eSCorvin Köhne int 11040bda8d3eSCorvin Köhne vm_handle_ipi(struct vm *vm, int vcpuid, struct vm_exit *vme, bool *retu) 11050bda8d3eSCorvin Köhne { 11060bda8d3eSCorvin Köhne *retu = true; 11070bda8d3eSCorvin Köhne switch (vme->u.ipi.mode) { 11080bda8d3eSCorvin Köhne case APIC_DELMODE_INIT: 11090bda8d3eSCorvin Köhne vm_smp_rendezvous(vm, vcpuid, vme->u.ipi.dmask, 11100bda8d3eSCorvin Köhne vlapic_handle_init, NULL); 11110bda8d3eSCorvin Köhne break; 11120bda8d3eSCorvin Köhne case APIC_DELMODE_STARTUP: 11130bda8d3eSCorvin Köhne break; 11140bda8d3eSCorvin Köhne default: 11153fc17484SEmmanuel Vadot return (1); 11163fc17484SEmmanuel Vadot } 1117366f6083SPeter Grehan 11180bda8d3eSCorvin Köhne return (0); 11190bda8d3eSCorvin Köhne } 11200bda8d3eSCorvin Köhne 1121159dd56fSNeel Natu void 1122294d0d88SNeel Natu vlapic_self_ipi_handler(struct vlapic *vlapic, uint64_t val) 1123294d0d88SNeel Natu { 1124294d0d88SNeel Natu int vec; 1125294d0d88SNeel Natu 1126159dd56fSNeel Natu KASSERT(x2apic(vlapic), ("SELF_IPI does not exist in xAPIC mode")); 1127159dd56fSNeel Natu 1128294d0d88SNeel Natu vec = val & 0xff; 1129294d0d88SNeel Natu lapic_intr_edge(vlapic->vm, vlapic->vcpuid, vec); 1130294d0d88SNeel Natu vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid, IPIS_SENT, 1131294d0d88SNeel Natu vlapic->vcpuid, 1); 1132294d0d88SNeel Natu VLAPIC_CTR1(vlapic, "vlapic self-ipi %d", vec); 1133294d0d88SNeel Natu } 1134294d0d88SNeel Natu 1135366f6083SPeter Grehan int 11364d1e82a8SNeel Natu vlapic_pending_intr(struct vlapic *vlapic, int *vecptr) 1137366f6083SPeter Grehan { 1138de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1139366f6083SPeter Grehan int idx, i, bitpos, vector; 1140366f6083SPeter Grehan uint32_t *irrptr, val; 1141366f6083SPeter Grehan 11421bc51badSMichael Reifenberger vlapic_update_ppr(vlapic); 11431bc51badSMichael Reifenberger 114488c4b8d1SNeel Natu if (vlapic->ops.pending_intr) 114588c4b8d1SNeel Natu return ((*vlapic->ops.pending_intr)(vlapic, vecptr)); 114688c4b8d1SNeel Natu 1147366f6083SPeter Grehan irrptr = &lapic->irr0; 1148366f6083SPeter Grehan 114944e2f0feSNeel Natu for (i = 7; i >= 0; i--) { 1150366f6083SPeter Grehan idx = i * 4; 1151366f6083SPeter Grehan val = atomic_load_acq_int(&irrptr[idx]); 1152366f6083SPeter Grehan bitpos = fls(val); 1153366f6083SPeter Grehan if (bitpos != 0) { 1154366f6083SPeter Grehan vector = i * 32 + (bitpos - 1); 1155366f6083SPeter Grehan if (PRIO(vector) > PRIO(lapic->ppr)) { 1156366f6083SPeter Grehan VLAPIC_CTR1(vlapic, "pending intr %d", vector); 11574d1e82a8SNeel Natu if (vecptr != NULL) 11584d1e82a8SNeel Natu *vecptr = vector; 11594d1e82a8SNeel Natu return (1); 1160366f6083SPeter Grehan } else 1161366f6083SPeter Grehan break; 1162366f6083SPeter Grehan } 1163366f6083SPeter Grehan } 11644d1e82a8SNeel Natu return (0); 1165366f6083SPeter Grehan } 1166366f6083SPeter Grehan 1167366f6083SPeter Grehan void 1168366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector) 1169366f6083SPeter Grehan { 1170de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1171366f6083SPeter Grehan uint32_t *irrptr, *isrptr; 1172366f6083SPeter Grehan int idx, stk_top; 1173366f6083SPeter Grehan 117488c4b8d1SNeel Natu if (vlapic->ops.intr_accepted) 117588c4b8d1SNeel Natu return ((*vlapic->ops.intr_accepted)(vlapic, vector)); 117688c4b8d1SNeel Natu 1177366f6083SPeter Grehan /* 1178366f6083SPeter Grehan * clear the ready bit for vector being accepted in irr 1179366f6083SPeter Grehan * and set the vector as in service in isr. 1180366f6083SPeter Grehan */ 1181366f6083SPeter Grehan idx = (vector / 32) * 4; 1182366f6083SPeter Grehan 1183366f6083SPeter Grehan irrptr = &lapic->irr0; 1184366f6083SPeter Grehan atomic_clear_int(&irrptr[idx], 1 << (vector % 32)); 1185366f6083SPeter Grehan VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted"); 1186366f6083SPeter Grehan 1187366f6083SPeter Grehan isrptr = &lapic->isr0; 1188366f6083SPeter Grehan isrptr[idx] |= 1 << (vector % 32); 1189366f6083SPeter Grehan VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted"); 1190366f6083SPeter Grehan 1191366f6083SPeter Grehan /* 1192366f6083SPeter Grehan * Update the PPR 1193366f6083SPeter Grehan */ 1194366f6083SPeter Grehan vlapic->isrvec_stk_top++; 1195366f6083SPeter Grehan 1196366f6083SPeter Grehan stk_top = vlapic->isrvec_stk_top; 1197366f6083SPeter Grehan if (stk_top >= ISRVEC_STK_SIZE) 1198366f6083SPeter Grehan panic("isrvec_stk_top overflow %d", stk_top); 1199366f6083SPeter Grehan 1200366f6083SPeter Grehan vlapic->isrvec_stk[stk_top] = vector; 1201366f6083SPeter Grehan } 1202366f6083SPeter Grehan 12032c52dcd9SNeel Natu void 12042c52dcd9SNeel Natu vlapic_svr_write_handler(struct vlapic *vlapic) 12051c052192SNeel Natu { 12061c052192SNeel Natu struct LAPIC *lapic; 12072c52dcd9SNeel Natu uint32_t old, new, changed; 12081c052192SNeel Natu 1209de5ea6b6SNeel Natu lapic = vlapic->apic_page; 12102c52dcd9SNeel Natu 12112c52dcd9SNeel Natu new = lapic->svr; 12122c52dcd9SNeel Natu old = vlapic->svr_last; 12132c52dcd9SNeel Natu vlapic->svr_last = new; 12142c52dcd9SNeel Natu 12151c052192SNeel Natu changed = old ^ new; 12161c052192SNeel Natu if ((changed & APIC_SVR_ENABLE) != 0) { 12171c052192SNeel Natu if ((new & APIC_SVR_ENABLE) == 0) { 1218fb03ca4eSNeel Natu /* 12192c52dcd9SNeel Natu * The apic is now disabled so stop the apic timer 12202c52dcd9SNeel Natu * and mask all the LVT entries. 1221fb03ca4eSNeel Natu */ 12221c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-disabled"); 1223fb03ca4eSNeel Natu VLAPIC_TIMER_LOCK(vlapic); 1224fb03ca4eSNeel Natu callout_stop(&vlapic->callout); 1225fb03ca4eSNeel Natu VLAPIC_TIMER_UNLOCK(vlapic); 12262c52dcd9SNeel Natu vlapic_mask_lvts(vlapic); 12271c052192SNeel Natu } else { 1228fb03ca4eSNeel Natu /* 1229fb03ca4eSNeel Natu * The apic is now enabled so restart the apic timer 1230fb03ca4eSNeel Natu * if it is configured in periodic mode. 1231fb03ca4eSNeel Natu */ 12321c052192SNeel Natu VLAPIC_CTR0(vlapic, "vlapic is software-enabled"); 1233fb03ca4eSNeel Natu if (vlapic_periodic_timer(vlapic)) 1234fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 12351c052192SNeel Natu } 12361c052192SNeel Natu } 12371c052192SNeel Natu } 12381c052192SNeel Natu 1239366f6083SPeter Grehan int 124052e5c8a2SNeel Natu vlapic_read(struct vlapic *vlapic, int mmio_access, uint64_t offset, 124152e5c8a2SNeel Natu uint64_t *data, bool *retu) 1242366f6083SPeter Grehan { 1243de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 1244366f6083SPeter Grehan uint32_t *reg; 1245366f6083SPeter Grehan int i; 1246366f6083SPeter Grehan 124752e5c8a2SNeel Natu /* Ignore MMIO accesses in x2APIC mode */ 124852e5c8a2SNeel Natu if (x2apic(vlapic) && mmio_access) { 124952e5c8a2SNeel Natu VLAPIC_CTR1(vlapic, "MMIO read from offset %#lx in x2APIC mode", 125052e5c8a2SNeel Natu offset); 125152e5c8a2SNeel Natu *data = 0; 125252e5c8a2SNeel Natu goto done; 125352e5c8a2SNeel Natu } 125452e5c8a2SNeel Natu 125552e5c8a2SNeel Natu if (!x2apic(vlapic) && !mmio_access) { 125652e5c8a2SNeel Natu /* 125752e5c8a2SNeel Natu * XXX Generate GP fault for MSR accesses in xAPIC mode 125852e5c8a2SNeel Natu */ 125952e5c8a2SNeel Natu VLAPIC_CTR1(vlapic, "x2APIC MSR read from offset %#lx in " 126052e5c8a2SNeel Natu "xAPIC mode", offset); 126152e5c8a2SNeel Natu *data = 0; 126252e5c8a2SNeel Natu goto done; 126352e5c8a2SNeel Natu } 126452e5c8a2SNeel Natu 1265366f6083SPeter Grehan if (offset > sizeof(*lapic)) { 1266366f6083SPeter Grehan *data = 0; 12671c052192SNeel Natu goto done; 1268366f6083SPeter Grehan } 1269366f6083SPeter Grehan 1270366f6083SPeter Grehan offset &= ~3; 1271366f6083SPeter Grehan switch(offset) 1272366f6083SPeter Grehan { 1273366f6083SPeter Grehan case APIC_OFFSET_ID: 12743f0ddc7cSNeel Natu *data = lapic->id; 1275366f6083SPeter Grehan break; 1276366f6083SPeter Grehan case APIC_OFFSET_VER: 1277366f6083SPeter Grehan *data = lapic->version; 1278366f6083SPeter Grehan break; 1279366f6083SPeter Grehan case APIC_OFFSET_TPR: 1280594db002STycho Nightingale *data = vlapic_get_tpr(vlapic); 1281366f6083SPeter Grehan break; 1282366f6083SPeter Grehan case APIC_OFFSET_APR: 1283366f6083SPeter Grehan *data = lapic->apr; 1284366f6083SPeter Grehan break; 1285366f6083SPeter Grehan case APIC_OFFSET_PPR: 1286366f6083SPeter Grehan *data = lapic->ppr; 1287366f6083SPeter Grehan break; 1288366f6083SPeter Grehan case APIC_OFFSET_EOI: 1289366f6083SPeter Grehan *data = lapic->eoi; 1290366f6083SPeter Grehan break; 1291366f6083SPeter Grehan case APIC_OFFSET_LDR: 12923f0ddc7cSNeel Natu *data = lapic->ldr; 1293366f6083SPeter Grehan break; 1294366f6083SPeter Grehan case APIC_OFFSET_DFR: 12953f0ddc7cSNeel Natu *data = lapic->dfr; 1296366f6083SPeter Grehan break; 1297366f6083SPeter Grehan case APIC_OFFSET_SVR: 1298366f6083SPeter Grehan *data = lapic->svr; 1299366f6083SPeter Grehan break; 1300366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1301366f6083SPeter Grehan i = (offset - APIC_OFFSET_ISR0) >> 2; 1302366f6083SPeter Grehan reg = &lapic->isr0; 1303366f6083SPeter Grehan *data = *(reg + i); 1304366f6083SPeter Grehan break; 1305366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1306366f6083SPeter Grehan i = (offset - APIC_OFFSET_TMR0) >> 2; 1307366f6083SPeter Grehan reg = &lapic->tmr0; 1308366f6083SPeter Grehan *data = *(reg + i); 1309366f6083SPeter Grehan break; 1310366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1311366f6083SPeter Grehan i = (offset - APIC_OFFSET_IRR0) >> 2; 1312366f6083SPeter Grehan reg = &lapic->irr0; 1313366f6083SPeter Grehan *data = atomic_load_acq_int(reg + i); 1314366f6083SPeter Grehan break; 1315366f6083SPeter Grehan case APIC_OFFSET_ESR: 1316366f6083SPeter Grehan *data = lapic->esr; 1317366f6083SPeter Grehan break; 1318366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1319366f6083SPeter Grehan *data = lapic->icr_lo; 1320fafe8844SNeel Natu if (x2apic(vlapic)) 1321fafe8844SNeel Natu *data |= (uint64_t)lapic->icr_hi << 32; 1322366f6083SPeter Grehan break; 1323366f6083SPeter Grehan case APIC_OFFSET_ICR_HI: 1324366f6083SPeter Grehan *data = lapic->icr_hi; 1325366f6083SPeter Grehan break; 1326330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1327366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 1328fb03ca4eSNeel Natu *data = vlapic_get_lvt(vlapic, offset); 13297c05bc31SNeel Natu #ifdef INVARIANTS 13307c05bc31SNeel Natu reg = vlapic_get_lvtptr(vlapic, offset); 13317c05bc31SNeel Natu KASSERT(*data == *reg, ("inconsistent lvt value at " 13327c05bc31SNeel Natu "offset %#lx: %#lx/%#x", offset, *data, *reg)); 13337c05bc31SNeel Natu #endif 1334366f6083SPeter Grehan break; 1335de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1336366f6083SPeter Grehan *data = lapic->icr_timer; 1337366f6083SPeter Grehan break; 1338de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1339366f6083SPeter Grehan *data = vlapic_get_ccr(vlapic); 1340366f6083SPeter Grehan break; 1341de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1342366f6083SPeter Grehan *data = lapic->dcr_timer; 1343366f6083SPeter Grehan break; 1344294d0d88SNeel Natu case APIC_OFFSET_SELF_IPI: 1345294d0d88SNeel Natu /* 1346294d0d88SNeel Natu * XXX generate a GP fault if vlapic is in x2apic mode 1347294d0d88SNeel Natu */ 1348294d0d88SNeel Natu *data = 0; 1349294d0d88SNeel Natu break; 1350366f6083SPeter Grehan case APIC_OFFSET_RRR: 1351366f6083SPeter Grehan default: 1352366f6083SPeter Grehan *data = 0; 1353366f6083SPeter Grehan break; 1354366f6083SPeter Grehan } 13551c052192SNeel Natu done: 13561c052192SNeel Natu VLAPIC_CTR2(vlapic, "vlapic read offset %#x, data %#lx", offset, *data); 1357366f6083SPeter Grehan return 0; 1358366f6083SPeter Grehan } 1359366f6083SPeter Grehan 1360366f6083SPeter Grehan int 136152e5c8a2SNeel Natu vlapic_write(struct vlapic *vlapic, int mmio_access, uint64_t offset, 136252e5c8a2SNeel Natu uint64_t data, bool *retu) 1363366f6083SPeter Grehan { 1364de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 13657c05bc31SNeel Natu uint32_t *regptr; 1366366f6083SPeter Grehan int retval; 1367366f6083SPeter Grehan 13683f0ddc7cSNeel Natu KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE, 13693f0ddc7cSNeel Natu ("vlapic_write: invalid offset %#lx", offset)); 13703f0ddc7cSNeel Natu 137152e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "vlapic write offset %#lx, data %#lx", 137252e5c8a2SNeel Natu offset, data); 13731c052192SNeel Natu 137452e5c8a2SNeel Natu if (offset > sizeof(*lapic)) 137552e5c8a2SNeel Natu return (0); 137652e5c8a2SNeel Natu 137752e5c8a2SNeel Natu /* Ignore MMIO accesses in x2APIC mode */ 137852e5c8a2SNeel Natu if (x2apic(vlapic) && mmio_access) { 137952e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "MMIO write of %#lx to offset %#lx " 138052e5c8a2SNeel Natu "in x2APIC mode", data, offset); 138152e5c8a2SNeel Natu return (0); 138252e5c8a2SNeel Natu } 138352e5c8a2SNeel Natu 138452e5c8a2SNeel Natu /* 138552e5c8a2SNeel Natu * XXX Generate GP fault for MSR accesses in xAPIC mode 138652e5c8a2SNeel Natu */ 138752e5c8a2SNeel Natu if (!x2apic(vlapic) && !mmio_access) { 138852e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "x2APIC MSR write of %#lx to offset %#lx " 138952e5c8a2SNeel Natu "in xAPIC mode", data, offset); 139052e5c8a2SNeel Natu return (0); 1391366f6083SPeter Grehan } 1392366f6083SPeter Grehan 1393366f6083SPeter Grehan retval = 0; 1394366f6083SPeter Grehan switch(offset) 1395366f6083SPeter Grehan { 1396366f6083SPeter Grehan case APIC_OFFSET_ID: 13973f0ddc7cSNeel Natu lapic->id = data; 13983f0ddc7cSNeel Natu vlapic_id_write_handler(vlapic); 1399366f6083SPeter Grehan break; 1400366f6083SPeter Grehan case APIC_OFFSET_TPR: 1401594db002STycho Nightingale vlapic_set_tpr(vlapic, data & 0xff); 1402366f6083SPeter Grehan break; 1403366f6083SPeter Grehan case APIC_OFFSET_EOI: 1404366f6083SPeter Grehan vlapic_process_eoi(vlapic); 1405366f6083SPeter Grehan break; 1406366f6083SPeter Grehan case APIC_OFFSET_LDR: 14073f0ddc7cSNeel Natu lapic->ldr = data; 14083f0ddc7cSNeel Natu vlapic_ldr_write_handler(vlapic); 1409366f6083SPeter Grehan break; 1410366f6083SPeter Grehan case APIC_OFFSET_DFR: 14113f0ddc7cSNeel Natu lapic->dfr = data; 14123f0ddc7cSNeel Natu vlapic_dfr_write_handler(vlapic); 1413366f6083SPeter Grehan break; 1414366f6083SPeter Grehan case APIC_OFFSET_SVR: 14152c52dcd9SNeel Natu lapic->svr = data; 14162c52dcd9SNeel Natu vlapic_svr_write_handler(vlapic); 1417366f6083SPeter Grehan break; 1418366f6083SPeter Grehan case APIC_OFFSET_ICR_LOW: 1419fafe8844SNeel Natu lapic->icr_lo = data; 1420fafe8844SNeel Natu if (x2apic(vlapic)) 1421fafe8844SNeel Natu lapic->icr_hi = data >> 32; 1422fafe8844SNeel Natu retval = vlapic_icrlo_write_handler(vlapic, retu); 1423366f6083SPeter Grehan break; 1424a2da7af6SNeel Natu case APIC_OFFSET_ICR_HI: 1425a2da7af6SNeel Natu lapic->icr_hi = data; 1426a2da7af6SNeel Natu break; 1427330baf58SJohn Baldwin case APIC_OFFSET_CMCI_LVT: 1428366f6083SPeter Grehan case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT: 14297c05bc31SNeel Natu regptr = vlapic_get_lvtptr(vlapic, offset); 14307c05bc31SNeel Natu *regptr = data; 14317c05bc31SNeel Natu vlapic_lvt_write_handler(vlapic, offset); 1432366f6083SPeter Grehan break; 1433de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_ICR: 1434fafe8844SNeel Natu lapic->icr_timer = data; 1435fafe8844SNeel Natu vlapic_icrtmr_write_handler(vlapic); 1436366f6083SPeter Grehan break; 1437366f6083SPeter Grehan 1438de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_DCR: 1439fafe8844SNeel Natu lapic->dcr_timer = data; 1440fafe8844SNeel Natu vlapic_dcr_write_handler(vlapic); 1441366f6083SPeter Grehan break; 1442366f6083SPeter Grehan 1443366f6083SPeter Grehan case APIC_OFFSET_ESR: 1444fafe8844SNeel Natu vlapic_esr_write_handler(vlapic); 1445366f6083SPeter Grehan break; 1446294d0d88SNeel Natu 1447294d0d88SNeel Natu case APIC_OFFSET_SELF_IPI: 1448294d0d88SNeel Natu if (x2apic(vlapic)) 1449294d0d88SNeel Natu vlapic_self_ipi_handler(vlapic, data); 1450294d0d88SNeel Natu break; 1451294d0d88SNeel Natu 1452366f6083SPeter Grehan case APIC_OFFSET_VER: 1453366f6083SPeter Grehan case APIC_OFFSET_APR: 1454366f6083SPeter Grehan case APIC_OFFSET_PPR: 1455366f6083SPeter Grehan case APIC_OFFSET_RRR: 1456366f6083SPeter Grehan case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7: 1457366f6083SPeter Grehan case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7: 1458366f6083SPeter Grehan case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7: 1459de5ea6b6SNeel Natu case APIC_OFFSET_TIMER_CCR: 1460366f6083SPeter Grehan default: 1461366f6083SPeter Grehan // Read only. 1462366f6083SPeter Grehan break; 1463366f6083SPeter Grehan } 1464366f6083SPeter Grehan 1465366f6083SPeter Grehan return (retval); 1466366f6083SPeter Grehan } 1467366f6083SPeter Grehan 14687c05bc31SNeel Natu static void 14697c05bc31SNeel Natu vlapic_reset(struct vlapic *vlapic) 14707c05bc31SNeel Natu { 14717c05bc31SNeel Natu struct LAPIC *lapic; 14727c05bc31SNeel Natu 14737c05bc31SNeel Natu lapic = vlapic->apic_page; 14747c05bc31SNeel Natu bzero(lapic, sizeof(struct LAPIC)); 14757c05bc31SNeel Natu 14767c05bc31SNeel Natu lapic->id = vlapic_get_id(vlapic); 14777c05bc31SNeel Natu lapic->version = VLAPIC_VERSION; 14787c05bc31SNeel Natu lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT); 14797c05bc31SNeel Natu lapic->dfr = 0xffffffff; 14807c05bc31SNeel Natu lapic->svr = APIC_SVR_VECTOR; 14817c05bc31SNeel Natu vlapic_mask_lvts(vlapic); 148230b94db8SNeel Natu vlapic_reset_tmr(vlapic); 14837c05bc31SNeel Natu 14847c05bc31SNeel Natu lapic->dcr_timer = 0; 14857c05bc31SNeel Natu vlapic_dcr_write_handler(vlapic); 14867c05bc31SNeel Natu 14877c05bc31SNeel Natu if (vlapic->vcpuid == 0) 14887c05bc31SNeel Natu vlapic->boot_state = BS_RUNNING; /* BSP */ 14897c05bc31SNeel Natu else 14907c05bc31SNeel Natu vlapic->boot_state = BS_INIT; /* AP */ 14917c05bc31SNeel Natu 14927c05bc31SNeel Natu vlapic->svr_last = lapic->svr; 14937c05bc31SNeel Natu } 14947c05bc31SNeel Natu 1495de5ea6b6SNeel Natu void 1496de5ea6b6SNeel Natu vlapic_init(struct vlapic *vlapic) 1497366f6083SPeter Grehan { 1498de5ea6b6SNeel Natu KASSERT(vlapic->vm != NULL, ("vlapic_init: vm is not initialized")); 1499a488c9c9SRodney W. Grimes KASSERT(vlapic->vcpuid >= 0 && 1500a488c9c9SRodney W. Grimes vlapic->vcpuid < vm_get_maxcpus(vlapic->vm), 1501de5ea6b6SNeel Natu ("vlapic_init: vcpuid is not initialized")); 1502de5ea6b6SNeel Natu KASSERT(vlapic->apic_page != NULL, ("vlapic_init: apic_page is not " 1503de5ea6b6SNeel Natu "initialized")); 15042d3a73edSNeel Natu 1505becd9849SNeel Natu /* 1506becd9849SNeel Natu * If the vlapic is configured in x2apic mode then it will be 1507becd9849SNeel Natu * accessed in the critical section via the MSR emulation code. 1508becd9849SNeel Natu * 1509becd9849SNeel Natu * Therefore the timer mutex must be a spinlock because blockable 1510becd9849SNeel Natu * mutexes cannot be acquired in a critical section. 1511becd9849SNeel Natu */ 1512becd9849SNeel Natu mtx_init(&vlapic->timer_mtx, "vlapic timer mtx", NULL, MTX_SPIN); 1513fb03ca4eSNeel Natu callout_init(&vlapic->callout, 1); 1514fb03ca4eSNeel Natu 1515a2da7af6SNeel Natu vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; 15162d3a73edSNeel Natu 1517de5ea6b6SNeel Natu if (vlapic->vcpuid == 0) 15182d3a73edSNeel Natu vlapic->msr_apicbase |= APICBASE_BSP; 15192d3a73edSNeel Natu 15200bda8d3eSCorvin Köhne vlapic->ipi_exit = false; 15210bda8d3eSCorvin Köhne 152203cd0501SNeel Natu vlapic_reset(vlapic); 1523366f6083SPeter Grehan } 1524366f6083SPeter Grehan 1525366f6083SPeter Grehan void 1526366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic) 1527366f6083SPeter Grehan { 152803cd0501SNeel Natu 1529fb03ca4eSNeel Natu callout_drain(&vlapic->callout); 1530366f6083SPeter Grehan } 15312d3a73edSNeel Natu 15322d3a73edSNeel Natu uint64_t 15332d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic) 15342d3a73edSNeel Natu { 15352d3a73edSNeel Natu 15362d3a73edSNeel Natu return (vlapic->msr_apicbase); 15372d3a73edSNeel Natu } 15382d3a73edSNeel Natu 153952e5c8a2SNeel Natu int 15403f0ddc7cSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t new) 15412d3a73edSNeel Natu { 1542a2da7af6SNeel Natu 154352e5c8a2SNeel Natu if (vlapic->msr_apicbase != new) { 154452e5c8a2SNeel Natu VLAPIC_CTR2(vlapic, "Changing APIC_BASE MSR from %#lx to %#lx " 154552e5c8a2SNeel Natu "not supported", vlapic->msr_apicbase, new); 154652e5c8a2SNeel Natu return (-1); 154752e5c8a2SNeel Natu } 154852e5c8a2SNeel Natu 154952e5c8a2SNeel Natu return (0); 155052e5c8a2SNeel Natu } 155152e5c8a2SNeel Natu 155252e5c8a2SNeel Natu void 155352e5c8a2SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 155452e5c8a2SNeel Natu { 155552e5c8a2SNeel Natu struct vlapic *vlapic; 155652e5c8a2SNeel Natu struct LAPIC *lapic; 155752e5c8a2SNeel Natu 155852e5c8a2SNeel Natu vlapic = vm_lapic(vm, vcpuid); 1559a2da7af6SNeel Natu 1560a2da7af6SNeel Natu if (state == X2APIC_DISABLED) 156152e5c8a2SNeel Natu vlapic->msr_apicbase &= ~APICBASE_X2APIC; 156252e5c8a2SNeel Natu else 156352e5c8a2SNeel Natu vlapic->msr_apicbase |= APICBASE_X2APIC; 15643f0ddc7cSNeel Natu 15653f0ddc7cSNeel Natu /* 156652e5c8a2SNeel Natu * Reset the local APIC registers whose values are mode-dependent. 156752e5c8a2SNeel Natu * 156852e5c8a2SNeel Natu * XXX this works because the APIC mode can be changed only at vcpu 156952e5c8a2SNeel Natu * initialization time. 15703f0ddc7cSNeel Natu */ 15713f0ddc7cSNeel Natu lapic = vlapic->apic_page; 15723f0ddc7cSNeel Natu lapic->id = vlapic_get_id(vlapic); 15733f0ddc7cSNeel Natu if (x2apic(vlapic)) { 15743f0ddc7cSNeel Natu lapic->ldr = x2apic_ldr(vlapic); 15753f0ddc7cSNeel Natu lapic->dfr = 0; 15763f0ddc7cSNeel Natu } else { 15773f0ddc7cSNeel Natu lapic->ldr = 0; 15783f0ddc7cSNeel Natu lapic->dfr = 0xffffffff; 15793f0ddc7cSNeel Natu } 1580159dd56fSNeel Natu 1581159dd56fSNeel Natu if (state == X2APIC_ENABLED) { 1582159dd56fSNeel Natu if (vlapic->ops.enable_x2apic_mode) 1583159dd56fSNeel Natu (*vlapic->ops.enable_x2apic_mode)(vlapic); 1584159dd56fSNeel Natu } 15853f0ddc7cSNeel Natu } 15861c052192SNeel Natu 15874f8be175SNeel Natu void 15884f8be175SNeel Natu vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys, 15894f8be175SNeel Natu int delmode, int vec) 15904f8be175SNeel Natu { 15914f8be175SNeel Natu bool lowprio; 15924f8be175SNeel Natu int vcpuid; 15934f8be175SNeel Natu cpuset_t dmask; 15944f8be175SNeel Natu 1595762fd208STycho Nightingale if (delmode != IOART_DELFIXED && 1596762fd208STycho Nightingale delmode != IOART_DELLOPRI && 1597762fd208STycho Nightingale delmode != IOART_DELEXINT) { 15984f8be175SNeel Natu VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode); 15994f8be175SNeel Natu return; 16004f8be175SNeel Natu } 1601762fd208STycho Nightingale lowprio = (delmode == IOART_DELLOPRI); 16024f8be175SNeel Natu 16034f8be175SNeel Natu /* 16044f8be175SNeel Natu * We don't provide any virtual interrupt redirection hardware so 16054f8be175SNeel Natu * all interrupts originating from the ioapic or MSI specify the 16064f8be175SNeel Natu * 'dest' in the legacy xAPIC format. 16074f8be175SNeel Natu */ 16084f8be175SNeel Natu vlapic_calcdest(vm, &dmask, dest, phys, lowprio, false); 16094f8be175SNeel Natu 1610de855429SMark Johnston CPU_FOREACH_ISSET(vcpuid, &dmask) { 1611762fd208STycho Nightingale if (delmode == IOART_DELEXINT) { 16120775fbb4STycho Nightingale vm_inject_extint(vm, vcpuid); 1613762fd208STycho Nightingale } else { 16144f8be175SNeel Natu lapic_set_intr(vm, vcpuid, vec, level); 16154f8be175SNeel Natu } 16164f8be175SNeel Natu } 1617762fd208STycho Nightingale } 16184f8be175SNeel Natu 1619de5ea6b6SNeel Natu void 1620add611fdSNeel Natu vlapic_post_intr(struct vlapic *vlapic, int hostcpu, int ipinum) 1621de5ea6b6SNeel Natu { 1622de5ea6b6SNeel Natu /* 1623de5ea6b6SNeel Natu * Post an interrupt to the vcpu currently running on 'hostcpu'. 1624de5ea6b6SNeel Natu * 1625de5ea6b6SNeel Natu * This is done by leveraging features like Posted Interrupts (Intel) 1626de5ea6b6SNeel Natu * Doorbell MSR (AMD AVIC) that avoid a VM exit. 1627de5ea6b6SNeel Natu * 1628de5ea6b6SNeel Natu * If neither of these features are available then fallback to 1629de5ea6b6SNeel Natu * sending an IPI to 'hostcpu'. 1630de5ea6b6SNeel Natu */ 163188c4b8d1SNeel Natu if (vlapic->ops.post_intr) 163288c4b8d1SNeel Natu (*vlapic->ops.post_intr)(vlapic, hostcpu); 163388c4b8d1SNeel Natu else 1634add611fdSNeel Natu ipi_cpu(hostcpu, ipinum); 1635de5ea6b6SNeel Natu } 1636de5ea6b6SNeel Natu 16371c052192SNeel Natu bool 16381c052192SNeel Natu vlapic_enabled(struct vlapic *vlapic) 16391c052192SNeel Natu { 1640de5ea6b6SNeel Natu struct LAPIC *lapic = vlapic->apic_page; 16411c052192SNeel Natu 16421c052192SNeel Natu if ((vlapic->msr_apicbase & APICBASE_ENABLED) != 0 && 16431c052192SNeel Natu (lapic->svr & APIC_SVR_ENABLE) != 0) 16441c052192SNeel Natu return (true); 16451c052192SNeel Natu else 16461c052192SNeel Natu return (false); 16471c052192SNeel Natu } 16485b8a8cd1SNeel Natu 164930b94db8SNeel Natu static void 165030b94db8SNeel Natu vlapic_set_tmr(struct vlapic *vlapic, int vector, bool level) 165130b94db8SNeel Natu { 165230b94db8SNeel Natu struct LAPIC *lapic; 165330b94db8SNeel Natu uint32_t *tmrptr, mask; 165430b94db8SNeel Natu int idx; 165530b94db8SNeel Natu 165630b94db8SNeel Natu lapic = vlapic->apic_page; 165730b94db8SNeel Natu tmrptr = &lapic->tmr0; 165830b94db8SNeel Natu idx = (vector / 32) * 4; 165930b94db8SNeel Natu mask = 1 << (vector % 32); 166030b94db8SNeel Natu if (level) 166130b94db8SNeel Natu tmrptr[idx] |= mask; 166230b94db8SNeel Natu else 166330b94db8SNeel Natu tmrptr[idx] &= ~mask; 166430b94db8SNeel Natu 166530b94db8SNeel Natu if (vlapic->ops.set_tmr != NULL) 166630b94db8SNeel Natu (*vlapic->ops.set_tmr)(vlapic, vector, level); 166730b94db8SNeel Natu } 166830b94db8SNeel Natu 16695b8a8cd1SNeel Natu void 16705b8a8cd1SNeel Natu vlapic_reset_tmr(struct vlapic *vlapic) 16715b8a8cd1SNeel Natu { 167230b94db8SNeel Natu int vector; 16735b8a8cd1SNeel Natu 16745b8a8cd1SNeel Natu VLAPIC_CTR0(vlapic, "vlapic resetting all vectors to edge-triggered"); 16755b8a8cd1SNeel Natu 167630b94db8SNeel Natu for (vector = 0; vector <= 255; vector++) 167730b94db8SNeel Natu vlapic_set_tmr(vlapic, vector, false); 16785b8a8cd1SNeel Natu } 16795b8a8cd1SNeel Natu 16805b8a8cd1SNeel Natu void 16815b8a8cd1SNeel Natu vlapic_set_tmr_level(struct vlapic *vlapic, uint32_t dest, bool phys, 16825b8a8cd1SNeel Natu int delmode, int vector) 16835b8a8cd1SNeel Natu { 16845b8a8cd1SNeel Natu cpuset_t dmask; 16855b8a8cd1SNeel Natu bool lowprio; 16865b8a8cd1SNeel Natu 16875b8a8cd1SNeel Natu KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector)); 16885b8a8cd1SNeel Natu 16895b8a8cd1SNeel Natu /* 16905b8a8cd1SNeel Natu * A level trigger is valid only for fixed and lowprio delivery modes. 16915b8a8cd1SNeel Natu */ 16925b8a8cd1SNeel Natu if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) { 16935b8a8cd1SNeel Natu VLAPIC_CTR1(vlapic, "Ignoring level trigger-mode for " 16945b8a8cd1SNeel Natu "delivery-mode %d", delmode); 16955b8a8cd1SNeel Natu return; 16965b8a8cd1SNeel Natu } 16975b8a8cd1SNeel Natu 16985b8a8cd1SNeel Natu lowprio = (delmode == APIC_DELMODE_LOWPRIO); 16995b8a8cd1SNeel Natu vlapic_calcdest(vlapic->vm, &dmask, dest, phys, lowprio, false); 17005b8a8cd1SNeel Natu 17015b8a8cd1SNeel Natu if (!CPU_ISSET(vlapic->vcpuid, &dmask)) 17025b8a8cd1SNeel Natu return; 17035b8a8cd1SNeel Natu 17045b8a8cd1SNeel Natu VLAPIC_CTR1(vlapic, "vector %d set to level-triggered", vector); 170530b94db8SNeel Natu vlapic_set_tmr(vlapic, vector, true); 17065b8a8cd1SNeel Natu } 1707483d953aSJohn Baldwin 1708483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 1709483d953aSJohn Baldwin static void 1710483d953aSJohn Baldwin vlapic_reset_callout(struct vlapic *vlapic, uint32_t ccr) 1711483d953aSJohn Baldwin { 1712483d953aSJohn Baldwin /* The implementation is similar to the one in the 1713483d953aSJohn Baldwin * `vlapic_icrtmr_write_handler` function 1714483d953aSJohn Baldwin */ 1715483d953aSJohn Baldwin sbintime_t sbt; 1716483d953aSJohn Baldwin struct bintime bt; 1717483d953aSJohn Baldwin 1718483d953aSJohn Baldwin VLAPIC_TIMER_LOCK(vlapic); 1719483d953aSJohn Baldwin 1720483d953aSJohn Baldwin bt = vlapic->timer_freq_bt; 1721483d953aSJohn Baldwin bintime_mul(&bt, ccr); 1722483d953aSJohn Baldwin 1723483d953aSJohn Baldwin if (ccr != 0) { 1724483d953aSJohn Baldwin binuptime(&vlapic->timer_fire_bt); 1725483d953aSJohn Baldwin bintime_add(&vlapic->timer_fire_bt, &bt); 1726483d953aSJohn Baldwin 1727483d953aSJohn Baldwin sbt = bttosbt(bt); 17284c812fe6SMark Johnston vlapic_callout_reset(vlapic, sbt); 1729483d953aSJohn Baldwin } else { 1730483d953aSJohn Baldwin /* even if the CCR was 0, periodic timers should be reset */ 1731483d953aSJohn Baldwin if (vlapic_periodic_timer(vlapic)) { 1732483d953aSJohn Baldwin binuptime(&vlapic->timer_fire_bt); 1733483d953aSJohn Baldwin bintime_add(&vlapic->timer_fire_bt, 1734483d953aSJohn Baldwin &vlapic->timer_period_bt); 1735483d953aSJohn Baldwin sbt = bttosbt(vlapic->timer_period_bt); 1736483d953aSJohn Baldwin 1737483d953aSJohn Baldwin callout_stop(&vlapic->callout); 17384c812fe6SMark Johnston vlapic_callout_reset(vlapic, sbt); 1739483d953aSJohn Baldwin } 1740483d953aSJohn Baldwin } 1741483d953aSJohn Baldwin 1742483d953aSJohn Baldwin VLAPIC_TIMER_UNLOCK(vlapic); 1743483d953aSJohn Baldwin } 1744483d953aSJohn Baldwin 1745483d953aSJohn Baldwin int 1746483d953aSJohn Baldwin vlapic_snapshot(struct vm *vm, struct vm_snapshot_meta *meta) 1747483d953aSJohn Baldwin { 1748483d953aSJohn Baldwin int i, ret; 1749483d953aSJohn Baldwin struct vlapic *vlapic; 1750483d953aSJohn Baldwin struct LAPIC *lapic; 1751483d953aSJohn Baldwin uint32_t ccr; 1752483d953aSJohn Baldwin 1753483d953aSJohn Baldwin KASSERT(vm != NULL, ("%s: arg was NULL", __func__)); 1754483d953aSJohn Baldwin 1755483d953aSJohn Baldwin ret = 0; 1756483d953aSJohn Baldwin 1757483d953aSJohn Baldwin for (i = 0; i < VM_MAXCPU; i++) { 1758483d953aSJohn Baldwin vlapic = vm_lapic(vm, i); 1759483d953aSJohn Baldwin 1760483d953aSJohn Baldwin /* snapshot the page first; timer period depends on icr_timer */ 1761483d953aSJohn Baldwin lapic = vlapic->apic_page; 1762483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(lapic, PAGE_SIZE, meta, ret, done); 1763483d953aSJohn Baldwin 1764483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vlapic->esr_pending, meta, ret, done); 1765483d953aSJohn Baldwin 1766483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vlapic->timer_freq_bt.sec, 1767483d953aSJohn Baldwin meta, ret, done); 1768483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vlapic->timer_freq_bt.frac, 1769483d953aSJohn Baldwin meta, ret, done); 1770483d953aSJohn Baldwin 1771483d953aSJohn Baldwin /* 1772483d953aSJohn Baldwin * Timer period is equal to 'icr_timer' ticks at a frequency of 1773483d953aSJohn Baldwin * 'timer_freq_bt'. 1774483d953aSJohn Baldwin */ 1775483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_RESTORE) { 1776483d953aSJohn Baldwin vlapic->timer_period_bt = vlapic->timer_freq_bt; 1777483d953aSJohn Baldwin bintime_mul(&vlapic->timer_period_bt, lapic->icr_timer); 1778483d953aSJohn Baldwin } 1779483d953aSJohn Baldwin 1780483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(vlapic->isrvec_stk, 1781483d953aSJohn Baldwin sizeof(vlapic->isrvec_stk), 1782483d953aSJohn Baldwin meta, ret, done); 1783483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vlapic->isrvec_stk_top, meta, ret, done); 1784483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vlapic->boot_state, meta, ret, done); 1785483d953aSJohn Baldwin 1786483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(vlapic->lvt_last, 1787483d953aSJohn Baldwin sizeof(vlapic->lvt_last), 1788483d953aSJohn Baldwin meta, ret, done); 1789483d953aSJohn Baldwin 1790483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE) 1791483d953aSJohn Baldwin ccr = vlapic_get_ccr(vlapic); 1792483d953aSJohn Baldwin 1793483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(ccr, meta, ret, done); 1794483d953aSJohn Baldwin 1795c72e914cSVitaliy Gusev if (meta->op == VM_SNAPSHOT_RESTORE && 1796c72e914cSVitaliy Gusev vlapic_enabled(vlapic) && lapic->icr_timer != 0) { 1797483d953aSJohn Baldwin /* Reset the value of the 'timer_fire_bt' and the vlapic 1798483d953aSJohn Baldwin * callout based on the value of the current count 1799c72e914cSVitaliy Gusev * register saved when the VM snapshot was created. 1800c72e914cSVitaliy Gusev * If initial count register is 0, timer is not used. 1801c72e914cSVitaliy Gusev * Look at "10.5.4 APIC Timer" in Software Developer Manual. 1802483d953aSJohn Baldwin */ 1803483d953aSJohn Baldwin vlapic_reset_callout(vlapic, ccr); 1804483d953aSJohn Baldwin } 1805483d953aSJohn Baldwin } 1806483d953aSJohn Baldwin 1807483d953aSJohn Baldwin done: 1808483d953aSJohn Baldwin return (ret); 1809483d953aSJohn Baldwin } 1810483d953aSJohn Baldwin #endif 1811