1565bbb86SNeel Natu /*- 2c49761ddSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3c49761ddSPedro F. Giffuni * 4565bbb86SNeel Natu * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5565bbb86SNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6565bbb86SNeel Natu * All rights reserved. 7565bbb86SNeel Natu * 8565bbb86SNeel Natu * Redistribution and use in source and binary forms, with or without 9565bbb86SNeel Natu * modification, are permitted provided that the following conditions 10565bbb86SNeel Natu * are met: 11565bbb86SNeel Natu * 1. Redistributions of source code must retain the above copyright 12565bbb86SNeel Natu * notice, this list of conditions and the following disclaimer. 13565bbb86SNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 14565bbb86SNeel Natu * notice, this list of conditions and the following disclaimer in the 15565bbb86SNeel Natu * documentation and/or other materials provided with the distribution. 16565bbb86SNeel Natu * 17565bbb86SNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18565bbb86SNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19565bbb86SNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20565bbb86SNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21565bbb86SNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22565bbb86SNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23565bbb86SNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24565bbb86SNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25565bbb86SNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26565bbb86SNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27565bbb86SNeel Natu * SUCH DAMAGE. 28565bbb86SNeel Natu * 29565bbb86SNeel Natu * $FreeBSD$ 30565bbb86SNeel Natu */ 31565bbb86SNeel Natu 32565bbb86SNeel Natu #include <sys/cdefs.h> 33565bbb86SNeel Natu __FBSDID("$FreeBSD$"); 34565bbb86SNeel Natu 35483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h" 36483d953aSJohn Baldwin 37565bbb86SNeel Natu #include <sys/param.h> 38565bbb86SNeel Natu #include <sys/queue.h> 39565bbb86SNeel Natu #include <sys/lock.h> 40565bbb86SNeel Natu #include <sys/mutex.h> 41565bbb86SNeel Natu #include <sys/systm.h> 42565bbb86SNeel Natu #include <sys/kernel.h> 43565bbb86SNeel Natu #include <sys/malloc.h> 44565bbb86SNeel Natu 45565bbb86SNeel Natu #include <x86/apicreg.h> 46565bbb86SNeel Natu #include <machine/vmm.h> 47483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 48565bbb86SNeel Natu 49565bbb86SNeel Natu #include "vmm_ktr.h" 50565bbb86SNeel Natu #include "vmm_lapic.h" 514f8be175SNeel Natu #include "vlapic.h" 52565bbb86SNeel Natu #include "vioapic.h" 53565bbb86SNeel Natu 54565bbb86SNeel Natu #define IOREGSEL 0x00 55565bbb86SNeel Natu #define IOWIN 0x10 56565bbb86SNeel Natu 574cefe96cSAlexander Motin #define REDIR_ENTRIES 32 58b5b28fc9SNeel Natu #define RTBL_RO_BITS ((uint64_t)(IOART_REM_IRR | IOART_DELIVS)) 59565bbb86SNeel Natu 60565bbb86SNeel Natu struct vioapic { 61565bbb86SNeel Natu struct vm *vm; 62565bbb86SNeel Natu struct mtx mtx; 63565bbb86SNeel Natu uint32_t id; 64565bbb86SNeel Natu uint32_t ioregsel; 65565bbb86SNeel Natu struct { 66565bbb86SNeel Natu uint64_t reg; 67b5b28fc9SNeel Natu int acnt; /* sum of pin asserts (+1) and deasserts (-1) */ 68565bbb86SNeel Natu } rtbl[REDIR_ENTRIES]; 69565bbb86SNeel Natu }; 70565bbb86SNeel Natu 719c43cd07SNeel Natu #define VIOAPIC_LOCK(vioapic) mtx_lock_spin(&((vioapic)->mtx)) 729c43cd07SNeel Natu #define VIOAPIC_UNLOCK(vioapic) mtx_unlock_spin(&((vioapic)->mtx)) 73565bbb86SNeel Natu #define VIOAPIC_LOCKED(vioapic) mtx_owned(&((vioapic)->mtx)) 74565bbb86SNeel Natu 75565bbb86SNeel Natu static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic"); 76565bbb86SNeel Natu 77565bbb86SNeel Natu #define VIOAPIC_CTR1(vioapic, fmt, a1) \ 78565bbb86SNeel Natu VM_CTR1((vioapic)->vm, fmt, a1) 79565bbb86SNeel Natu 80565bbb86SNeel Natu #define VIOAPIC_CTR2(vioapic, fmt, a1, a2) \ 81565bbb86SNeel Natu VM_CTR2((vioapic)->vm, fmt, a1, a2) 82565bbb86SNeel Natu 83565bbb86SNeel Natu #define VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3) \ 84565bbb86SNeel Natu VM_CTR3((vioapic)->vm, fmt, a1, a2, a3) 85565bbb86SNeel Natu 86b5b28fc9SNeel Natu #define VIOAPIC_CTR4(vioapic, fmt, a1, a2, a3, a4) \ 87b5b28fc9SNeel Natu VM_CTR4((vioapic)->vm, fmt, a1, a2, a3, a4) 88b5b28fc9SNeel Natu 89565bbb86SNeel Natu #ifdef KTR 90565bbb86SNeel Natu static const char * 91565bbb86SNeel Natu pinstate_str(bool asserted) 92565bbb86SNeel Natu { 93565bbb86SNeel Natu 94565bbb86SNeel Natu if (asserted) 95565bbb86SNeel Natu return ("asserted"); 96565bbb86SNeel Natu else 97565bbb86SNeel Natu return ("deasserted"); 98565bbb86SNeel Natu } 99565bbb86SNeel Natu #endif 100565bbb86SNeel Natu 101565bbb86SNeel Natu static void 102b5b28fc9SNeel Natu vioapic_send_intr(struct vioapic *vioapic, int pin) 103565bbb86SNeel Natu { 1044f8be175SNeel Natu int vector, delmode; 1054f8be175SNeel Natu uint32_t low, high, dest; 1064f8be175SNeel Natu bool level, phys; 107565bbb86SNeel Natu 108565bbb86SNeel Natu KASSERT(pin >= 0 && pin < REDIR_ENTRIES, 109565bbb86SNeel Natu ("vioapic_set_pinstate: invalid pin number %d", pin)); 110565bbb86SNeel Natu 111565bbb86SNeel Natu KASSERT(VIOAPIC_LOCKED(vioapic), 112565bbb86SNeel Natu ("vioapic_set_pinstate: vioapic is not locked")); 113565bbb86SNeel Natu 114565bbb86SNeel Natu low = vioapic->rtbl[pin].reg; 115565bbb86SNeel Natu high = vioapic->rtbl[pin].reg >> 32; 116b5b28fc9SNeel Natu 117b5b28fc9SNeel Natu if ((low & IOART_INTMASK) == IOART_INTMSET) { 118b5b28fc9SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin); 119b5b28fc9SNeel Natu return; 120b5b28fc9SNeel Natu } 121b5b28fc9SNeel Natu 1224f8be175SNeel Natu phys = ((low & IOART_DESTMOD) == IOART_DESTPHY); 1234f8be175SNeel Natu delmode = low & IOART_DELMOD; 124b5b28fc9SNeel Natu level = low & IOART_TRGRLVL ? true : false; 1255ea87868SRoger Pau Monné if (level) { 1265ea87868SRoger Pau Monné if ((low & IOART_REM_IRR) != 0) { 1275ea87868SRoger Pau Monné VIOAPIC_CTR1(vioapic, "ioapic pin%d: irr pending", 1285ea87868SRoger Pau Monné pin); 1295ea87868SRoger Pau Monné return; 1305ea87868SRoger Pau Monné } 131b5b28fc9SNeel Natu vioapic->rtbl[pin].reg |= IOART_REM_IRR; 1325ea87868SRoger Pau Monné } 133b5b28fc9SNeel Natu 134565bbb86SNeel Natu vector = low & IOART_INTVEC; 1354f8be175SNeel Natu dest = high >> APIC_ID_SHIFT; 1364f8be175SNeel Natu vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector); 137565bbb86SNeel Natu } 138b5b28fc9SNeel Natu 139b5b28fc9SNeel Natu static void 140b5b28fc9SNeel Natu vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate) 141b5b28fc9SNeel Natu { 142b5b28fc9SNeel Natu int oldcnt, newcnt; 143b5b28fc9SNeel Natu bool needintr; 144b5b28fc9SNeel Natu 145b5b28fc9SNeel Natu KASSERT(pin >= 0 && pin < REDIR_ENTRIES, 146b5b28fc9SNeel Natu ("vioapic_set_pinstate: invalid pin number %d", pin)); 147b5b28fc9SNeel Natu 148b5b28fc9SNeel Natu KASSERT(VIOAPIC_LOCKED(vioapic), 149b5b28fc9SNeel Natu ("vioapic_set_pinstate: vioapic is not locked")); 150b5b28fc9SNeel Natu 151b5b28fc9SNeel Natu oldcnt = vioapic->rtbl[pin].acnt; 152b5b28fc9SNeel Natu if (newstate) 153b5b28fc9SNeel Natu vioapic->rtbl[pin].acnt++; 154b5b28fc9SNeel Natu else 155b5b28fc9SNeel Natu vioapic->rtbl[pin].acnt--; 156b5b28fc9SNeel Natu newcnt = vioapic->rtbl[pin].acnt; 157b5b28fc9SNeel Natu 158b5b28fc9SNeel Natu if (newcnt < 0) { 159b5b28fc9SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d", 160b5b28fc9SNeel Natu pin, newcnt); 161b5b28fc9SNeel Natu } 162b5b28fc9SNeel Natu 163b5b28fc9SNeel Natu needintr = false; 164b5b28fc9SNeel Natu if (oldcnt == 0 && newcnt == 1) { 165b5b28fc9SNeel Natu needintr = true; 166b5b28fc9SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin); 167b5b28fc9SNeel Natu } else if (oldcnt == 1 && newcnt == 0) { 168b5b28fc9SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin); 169b5b28fc9SNeel Natu } else { 170b5b28fc9SNeel Natu VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d", 171b5b28fc9SNeel Natu pin, pinstate_str(newstate), newcnt); 172b5b28fc9SNeel Natu } 173b5b28fc9SNeel Natu 174b5b28fc9SNeel Natu if (needintr) 175b5b28fc9SNeel Natu vioapic_send_intr(vioapic, pin); 176565bbb86SNeel Natu } 177565bbb86SNeel Natu 178ac7304a7SNeel Natu enum irqstate { 179ac7304a7SNeel Natu IRQSTATE_ASSERT, 180ac7304a7SNeel Natu IRQSTATE_DEASSERT, 181ac7304a7SNeel Natu IRQSTATE_PULSE 182ac7304a7SNeel Natu }; 183ac7304a7SNeel Natu 184565bbb86SNeel Natu static int 185ac7304a7SNeel Natu vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate) 186565bbb86SNeel Natu { 187565bbb86SNeel Natu struct vioapic *vioapic; 188565bbb86SNeel Natu 189565bbb86SNeel Natu if (irq < 0 || irq >= REDIR_ENTRIES) 190565bbb86SNeel Natu return (EINVAL); 191565bbb86SNeel Natu 192565bbb86SNeel Natu vioapic = vm_ioapic(vm); 193565bbb86SNeel Natu 194565bbb86SNeel Natu VIOAPIC_LOCK(vioapic); 195ac7304a7SNeel Natu switch (irqstate) { 196ac7304a7SNeel Natu case IRQSTATE_ASSERT: 197ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, true); 198ac7304a7SNeel Natu break; 199ac7304a7SNeel Natu case IRQSTATE_DEASSERT: 200ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, false); 201ac7304a7SNeel Natu break; 202ac7304a7SNeel Natu case IRQSTATE_PULSE: 203ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, true); 204ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, false); 205ac7304a7SNeel Natu break; 206ac7304a7SNeel Natu default: 207ac7304a7SNeel Natu panic("vioapic_set_irqstate: invalid irqstate %d", irqstate); 208ac7304a7SNeel Natu } 209565bbb86SNeel Natu VIOAPIC_UNLOCK(vioapic); 210565bbb86SNeel Natu 211565bbb86SNeel Natu return (0); 212565bbb86SNeel Natu } 213565bbb86SNeel Natu 214565bbb86SNeel Natu int 215565bbb86SNeel Natu vioapic_assert_irq(struct vm *vm, int irq) 216565bbb86SNeel Natu { 217565bbb86SNeel Natu 218ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT)); 219565bbb86SNeel Natu } 220565bbb86SNeel Natu 221565bbb86SNeel Natu int 222565bbb86SNeel Natu vioapic_deassert_irq(struct vm *vm, int irq) 223565bbb86SNeel Natu { 224565bbb86SNeel Natu 225ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT)); 226ac7304a7SNeel Natu } 227ac7304a7SNeel Natu 228ac7304a7SNeel Natu int 229ac7304a7SNeel Natu vioapic_pulse_irq(struct vm *vm, int irq) 230ac7304a7SNeel Natu { 231ac7304a7SNeel Natu 232ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE)); 233565bbb86SNeel Natu } 234565bbb86SNeel Natu 2355b8a8cd1SNeel Natu /* 2365b8a8cd1SNeel Natu * Reset the vlapic's trigger-mode register to reflect the ioapic pin 2375b8a8cd1SNeel Natu * configuration. 2385b8a8cd1SNeel Natu */ 2395b8a8cd1SNeel Natu static void 2405b8a8cd1SNeel Natu vioapic_update_tmr(struct vm *vm, int vcpuid, void *arg) 2415b8a8cd1SNeel Natu { 2425b8a8cd1SNeel Natu struct vioapic *vioapic; 2435b8a8cd1SNeel Natu struct vlapic *vlapic; 2445b8a8cd1SNeel Natu uint32_t low, high, dest; 2455b8a8cd1SNeel Natu int delmode, pin, vector; 2465b8a8cd1SNeel Natu bool level, phys; 2475b8a8cd1SNeel Natu 248*d3956e46SJohn Baldwin vlapic = vm_lapic(vm_vcpu(vm, vcpuid)); 2495b8a8cd1SNeel Natu vioapic = vm_ioapic(vm); 2505b8a8cd1SNeel Natu 2515b8a8cd1SNeel Natu VIOAPIC_LOCK(vioapic); 2525b8a8cd1SNeel Natu /* 2535b8a8cd1SNeel Natu * Reset all vectors to be edge-triggered. 2545b8a8cd1SNeel Natu */ 2555b8a8cd1SNeel Natu vlapic_reset_tmr(vlapic); 2565b8a8cd1SNeel Natu for (pin = 0; pin < REDIR_ENTRIES; pin++) { 2575b8a8cd1SNeel Natu low = vioapic->rtbl[pin].reg; 2585b8a8cd1SNeel Natu high = vioapic->rtbl[pin].reg >> 32; 2595b8a8cd1SNeel Natu 2605b8a8cd1SNeel Natu level = low & IOART_TRGRLVL ? true : false; 2615b8a8cd1SNeel Natu if (!level) 2625b8a8cd1SNeel Natu continue; 2635b8a8cd1SNeel Natu 2645b8a8cd1SNeel Natu /* 2655b8a8cd1SNeel Natu * For a level-triggered 'pin' let the vlapic figure out if 2665b8a8cd1SNeel Natu * an assertion on this 'pin' would result in an interrupt 2675b8a8cd1SNeel Natu * being delivered to it. If yes, then it will modify the 2685b8a8cd1SNeel Natu * TMR bit associated with this vector to level-triggered. 2695b8a8cd1SNeel Natu */ 2705b8a8cd1SNeel Natu phys = ((low & IOART_DESTMOD) == IOART_DESTPHY); 2715b8a8cd1SNeel Natu delmode = low & IOART_DELMOD; 2725b8a8cd1SNeel Natu vector = low & IOART_INTVEC; 2735b8a8cd1SNeel Natu dest = high >> APIC_ID_SHIFT; 2745b8a8cd1SNeel Natu vlapic_set_tmr_level(vlapic, dest, phys, delmode, vector); 2755b8a8cd1SNeel Natu } 2765b8a8cd1SNeel Natu VIOAPIC_UNLOCK(vioapic); 2775b8a8cd1SNeel Natu } 2785b8a8cd1SNeel Natu 279565bbb86SNeel Natu static uint32_t 280*d3956e46SJohn Baldwin vioapic_read(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr) 281565bbb86SNeel Natu { 282565bbb86SNeel Natu int regnum, pin, rshift; 283565bbb86SNeel Natu 284565bbb86SNeel Natu regnum = addr & 0xff; 285565bbb86SNeel Natu switch (regnum) { 286565bbb86SNeel Natu case IOAPIC_ID: 287565bbb86SNeel Natu return (vioapic->id); 288565bbb86SNeel Natu break; 289565bbb86SNeel Natu case IOAPIC_VER: 290b5b28fc9SNeel Natu return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11); 291565bbb86SNeel Natu break; 292565bbb86SNeel Natu case IOAPIC_ARB: 293565bbb86SNeel Natu return (vioapic->id); 294565bbb86SNeel Natu break; 295565bbb86SNeel Natu default: 296565bbb86SNeel Natu break; 297565bbb86SNeel Natu } 298565bbb86SNeel Natu 299565bbb86SNeel Natu /* redirection table entries */ 300565bbb86SNeel Natu if (regnum >= IOAPIC_REDTBL && 301565bbb86SNeel Natu regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 302565bbb86SNeel Natu pin = (regnum - IOAPIC_REDTBL) / 2; 303565bbb86SNeel Natu if ((regnum - IOAPIC_REDTBL) % 2) 304565bbb86SNeel Natu rshift = 32; 305565bbb86SNeel Natu else 306565bbb86SNeel Natu rshift = 0; 307565bbb86SNeel Natu 308565bbb86SNeel Natu return (vioapic->rtbl[pin].reg >> rshift); 309565bbb86SNeel Natu } 310565bbb86SNeel Natu 311565bbb86SNeel Natu return (0); 312565bbb86SNeel Natu } 313565bbb86SNeel Natu 314565bbb86SNeel Natu static void 315*d3956e46SJohn Baldwin vioapic_write(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr, 316*d3956e46SJohn Baldwin uint32_t data) 317565bbb86SNeel Natu { 318b5b28fc9SNeel Natu uint64_t data64, mask64; 3195b8a8cd1SNeel Natu uint64_t last, changed; 320*d3956e46SJohn Baldwin int regnum, pin, lshift, vcpuid; 3215b8a8cd1SNeel Natu cpuset_t allvcpus; 322565bbb86SNeel Natu 323*d3956e46SJohn Baldwin vcpuid = vcpu_vcpuid(vcpu); 324565bbb86SNeel Natu regnum = addr & 0xff; 325565bbb86SNeel Natu switch (regnum) { 326565bbb86SNeel Natu case IOAPIC_ID: 327565bbb86SNeel Natu vioapic->id = data & APIC_ID_MASK; 328565bbb86SNeel Natu break; 329565bbb86SNeel Natu case IOAPIC_VER: 330565bbb86SNeel Natu case IOAPIC_ARB: 331565bbb86SNeel Natu /* readonly */ 332565bbb86SNeel Natu break; 333565bbb86SNeel Natu default: 334565bbb86SNeel Natu break; 335565bbb86SNeel Natu } 336565bbb86SNeel Natu 337565bbb86SNeel Natu /* redirection table entries */ 338565bbb86SNeel Natu if (regnum >= IOAPIC_REDTBL && 339565bbb86SNeel Natu regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 340565bbb86SNeel Natu pin = (regnum - IOAPIC_REDTBL) / 2; 341565bbb86SNeel Natu if ((regnum - IOAPIC_REDTBL) % 2) 342565bbb86SNeel Natu lshift = 32; 343565bbb86SNeel Natu else 344565bbb86SNeel Natu lshift = 0; 345565bbb86SNeel Natu 3465b8a8cd1SNeel Natu last = vioapic->rtbl[pin].reg; 3475b8a8cd1SNeel Natu 348b5b28fc9SNeel Natu data64 = (uint64_t)data << lshift; 349b5b28fc9SNeel Natu mask64 = (uint64_t)0xffffffff << lshift; 350b5b28fc9SNeel Natu vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS; 351b5b28fc9SNeel Natu vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS; 352565bbb86SNeel Natu 3535ea87868SRoger Pau Monné /* 3545ea87868SRoger Pau Monné * Switching from level to edge triggering will clear the IRR 3555ea87868SRoger Pau Monné * bit. This is what FreeBSD will do in order to EOI an 3565ea87868SRoger Pau Monné * interrupt when the IO-APIC doesn't support targeted EOI (see 3575ea87868SRoger Pau Monné * _ioapic_eoi_source). 3585ea87868SRoger Pau Monné */ 3595ea87868SRoger Pau Monné if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG && 3605ea87868SRoger Pau Monné (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0) 3615ea87868SRoger Pau Monné vioapic->rtbl[pin].reg &= ~IOART_REM_IRR; 3625ea87868SRoger Pau Monné 363b5b28fc9SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx", 364565bbb86SNeel Natu pin, vioapic->rtbl[pin].reg); 365565bbb86SNeel Natu 366565bbb86SNeel Natu /* 3675b8a8cd1SNeel Natu * If any fields in the redirection table entry (except mask 3685b8a8cd1SNeel Natu * or polarity) have changed then rendezvous all the vcpus 3695b8a8cd1SNeel Natu * to update their vlapic trigger-mode registers. 3705b8a8cd1SNeel Natu */ 3715b8a8cd1SNeel Natu changed = last ^ vioapic->rtbl[pin].reg; 3725b8a8cd1SNeel Natu if (changed & ~(IOART_INTMASK | IOART_INTPOL)) { 3735b8a8cd1SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d: recalculate " 3745b8a8cd1SNeel Natu "vlapic trigger-mode register", pin); 3755b8a8cd1SNeel Natu VIOAPIC_UNLOCK(vioapic); 3765b8a8cd1SNeel Natu allvcpus = vm_active_cpus(vioapic->vm); 377b837daddSKonstantin Belousov (void)vm_smp_rendezvous(vioapic->vm, vcpuid, allvcpus, 3785b8a8cd1SNeel Natu vioapic_update_tmr, NULL); 3795b8a8cd1SNeel Natu VIOAPIC_LOCK(vioapic); 3805b8a8cd1SNeel Natu } 3815b8a8cd1SNeel Natu 3825b8a8cd1SNeel Natu /* 383b5b28fc9SNeel Natu * Generate an interrupt if the following conditions are met: 384d7d06769SRoger Pau Monné * - pin trigger mode is level 385b5b28fc9SNeel Natu * - pin level is asserted 386565bbb86SNeel Natu */ 3875ea87868SRoger Pau Monné if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL && 388b5b28fc9SNeel Natu (vioapic->rtbl[pin].acnt > 0)) { 389b5b28fc9SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl " 390b5b28fc9SNeel Natu "write, acnt %d", pin, vioapic->rtbl[pin].acnt); 391b5b28fc9SNeel Natu vioapic_send_intr(vioapic, pin); 392565bbb86SNeel Natu } 393565bbb86SNeel Natu } 394565bbb86SNeel Natu } 395565bbb86SNeel Natu 396565bbb86SNeel Natu static int 397*d3956e46SJohn Baldwin vioapic_mmio_rw(struct vioapic *vioapic, struct vcpu *vcpu, uint64_t gpa, 3985b8a8cd1SNeel Natu uint64_t *data, int size, bool doread) 399565bbb86SNeel Natu { 400565bbb86SNeel Natu uint64_t offset; 401565bbb86SNeel Natu 402565bbb86SNeel Natu offset = gpa - VIOAPIC_BASE; 403565bbb86SNeel Natu 404565bbb86SNeel Natu /* 405565bbb86SNeel Natu * The IOAPIC specification allows 32-bit wide accesses to the 406565bbb86SNeel Natu * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 407565bbb86SNeel Natu */ 408565bbb86SNeel Natu if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 409565bbb86SNeel Natu if (doread) 410565bbb86SNeel Natu *data = 0; 411565bbb86SNeel Natu return (0); 412565bbb86SNeel Natu } 413565bbb86SNeel Natu 414565bbb86SNeel Natu VIOAPIC_LOCK(vioapic); 415565bbb86SNeel Natu if (offset == IOREGSEL) { 416565bbb86SNeel Natu if (doread) 417565bbb86SNeel Natu *data = vioapic->ioregsel; 418565bbb86SNeel Natu else 419565bbb86SNeel Natu vioapic->ioregsel = *data; 420565bbb86SNeel Natu } else { 4215b8a8cd1SNeel Natu if (doread) { 422*d3956e46SJohn Baldwin *data = vioapic_read(vioapic, vcpu, 4235b8a8cd1SNeel Natu vioapic->ioregsel); 4245b8a8cd1SNeel Natu } else { 425*d3956e46SJohn Baldwin vioapic_write(vioapic, vcpu, vioapic->ioregsel, 4265b8a8cd1SNeel Natu *data); 4275b8a8cd1SNeel Natu } 428565bbb86SNeel Natu } 429565bbb86SNeel Natu VIOAPIC_UNLOCK(vioapic); 430565bbb86SNeel Natu 431565bbb86SNeel Natu return (0); 432565bbb86SNeel Natu } 433565bbb86SNeel Natu 434565bbb86SNeel Natu int 435*d3956e46SJohn Baldwin vioapic_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, 436565bbb86SNeel Natu int size, void *arg) 437565bbb86SNeel Natu { 438565bbb86SNeel Natu int error; 439565bbb86SNeel Natu struct vioapic *vioapic; 440565bbb86SNeel Natu 441*d3956e46SJohn Baldwin vioapic = vm_ioapic(vcpu_vm(vcpu)); 442*d3956e46SJohn Baldwin error = vioapic_mmio_rw(vioapic, vcpu, gpa, rval, size, true); 443565bbb86SNeel Natu return (error); 444565bbb86SNeel Natu } 445565bbb86SNeel Natu 446565bbb86SNeel Natu int 447*d3956e46SJohn Baldwin vioapic_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, 448565bbb86SNeel Natu int size, void *arg) 449565bbb86SNeel Natu { 450565bbb86SNeel Natu int error; 451565bbb86SNeel Natu struct vioapic *vioapic; 452565bbb86SNeel Natu 453*d3956e46SJohn Baldwin vioapic = vm_ioapic(vcpu_vm(vcpu)); 454*d3956e46SJohn Baldwin error = vioapic_mmio_rw(vioapic, vcpu, gpa, &wval, size, false); 455565bbb86SNeel Natu return (error); 456565bbb86SNeel Natu } 457565bbb86SNeel Natu 458b5b28fc9SNeel Natu void 459b5b28fc9SNeel Natu vioapic_process_eoi(struct vm *vm, int vcpuid, int vector) 460b5b28fc9SNeel Natu { 461b5b28fc9SNeel Natu struct vioapic *vioapic; 462b5b28fc9SNeel Natu int pin; 463b5b28fc9SNeel Natu 464b5b28fc9SNeel Natu KASSERT(vector >= 0 && vector < 256, 465b5b28fc9SNeel Natu ("vioapic_process_eoi: invalid vector %d", vector)); 466b5b28fc9SNeel Natu 467b5b28fc9SNeel Natu vioapic = vm_ioapic(vm); 468b5b28fc9SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector); 469b5b28fc9SNeel Natu 470b5b28fc9SNeel Natu /* 471b5b28fc9SNeel Natu * XXX keep track of the pins associated with this vector instead 472b5b28fc9SNeel Natu * of iterating on every single pin each time. 473b5b28fc9SNeel Natu */ 474b5b28fc9SNeel Natu VIOAPIC_LOCK(vioapic); 475b5b28fc9SNeel Natu for (pin = 0; pin < REDIR_ENTRIES; pin++) { 476b5b28fc9SNeel Natu if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0) 477b5b28fc9SNeel Natu continue; 478b5b28fc9SNeel Natu if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector) 479b5b28fc9SNeel Natu continue; 480b5b28fc9SNeel Natu vioapic->rtbl[pin].reg &= ~IOART_REM_IRR; 481b5b28fc9SNeel Natu if (vioapic->rtbl[pin].acnt > 0) { 482b5b28fc9SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, " 483b5b28fc9SNeel Natu "acnt %d", pin, vioapic->rtbl[pin].acnt); 484b5b28fc9SNeel Natu vioapic_send_intr(vioapic, pin); 485b5b28fc9SNeel Natu } 486b5b28fc9SNeel Natu } 487b5b28fc9SNeel Natu VIOAPIC_UNLOCK(vioapic); 488b5b28fc9SNeel Natu } 489b5b28fc9SNeel Natu 490565bbb86SNeel Natu struct vioapic * 491565bbb86SNeel Natu vioapic_init(struct vm *vm) 492565bbb86SNeel Natu { 493565bbb86SNeel Natu int i; 494565bbb86SNeel Natu struct vioapic *vioapic; 495565bbb86SNeel Natu 496565bbb86SNeel Natu vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO); 497565bbb86SNeel Natu 498565bbb86SNeel Natu vioapic->vm = vm; 4999c43cd07SNeel Natu mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_SPIN); 500565bbb86SNeel Natu 501565bbb86SNeel Natu /* Initialize all redirection entries to mask all interrupts */ 502565bbb86SNeel Natu for (i = 0; i < REDIR_ENTRIES; i++) 503565bbb86SNeel Natu vioapic->rtbl[i].reg = 0x0001000000010000UL; 504565bbb86SNeel Natu 505565bbb86SNeel Natu return (vioapic); 506565bbb86SNeel Natu } 507565bbb86SNeel Natu 508565bbb86SNeel Natu void 509565bbb86SNeel Natu vioapic_cleanup(struct vioapic *vioapic) 510565bbb86SNeel Natu { 511565bbb86SNeel Natu 512565bbb86SNeel Natu free(vioapic, M_VIOAPIC); 513565bbb86SNeel Natu } 514b5b28fc9SNeel Natu 515b5b28fc9SNeel Natu int 516b5b28fc9SNeel Natu vioapic_pincount(struct vm *vm) 517b5b28fc9SNeel Natu { 518b5b28fc9SNeel Natu 519b5b28fc9SNeel Natu return (REDIR_ENTRIES); 520b5b28fc9SNeel Natu } 521483d953aSJohn Baldwin 522483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 523483d953aSJohn Baldwin int 524483d953aSJohn Baldwin vioapic_snapshot(struct vioapic *vioapic, struct vm_snapshot_meta *meta) 525483d953aSJohn Baldwin { 526483d953aSJohn Baldwin int ret; 527483d953aSJohn Baldwin int i; 528483d953aSJohn Baldwin 529483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vioapic->ioregsel, meta, ret, done); 530483d953aSJohn Baldwin 531483d953aSJohn Baldwin for (i = 0; i < nitems(vioapic->rtbl); i++) { 532483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].reg, meta, ret, done); 533483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].acnt, meta, ret, done); 534483d953aSJohn Baldwin } 535483d953aSJohn Baldwin 536483d953aSJohn Baldwin done: 537483d953aSJohn Baldwin return (ret); 538483d953aSJohn Baldwin } 539483d953aSJohn Baldwin #endif 540