17c8c0b82SPatrick Mooney /*-
2*32640292SAndy Fiddaman * SPDX-License-Identifier: BSD-2-Clause
37c8c0b82SPatrick Mooney *
47c8c0b82SPatrick Mooney * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
57c8c0b82SPatrick Mooney * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
67c8c0b82SPatrick Mooney * All rights reserved.
77c8c0b82SPatrick Mooney *
87c8c0b82SPatrick Mooney * Redistribution and use in source and binary forms, with or without
97c8c0b82SPatrick Mooney * modification, are permitted provided that the following conditions
107c8c0b82SPatrick Mooney * are met:
117c8c0b82SPatrick Mooney * 1. Redistributions of source code must retain the above copyright
127c8c0b82SPatrick Mooney * notice, this list of conditions and the following disclaimer.
137c8c0b82SPatrick Mooney * 2. Redistributions in binary form must reproduce the above copyright
147c8c0b82SPatrick Mooney * notice, this list of conditions and the following disclaimer in the
157c8c0b82SPatrick Mooney * documentation and/or other materials provided with the distribution.
167c8c0b82SPatrick Mooney *
177c8c0b82SPatrick Mooney * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
187c8c0b82SPatrick Mooney * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
197c8c0b82SPatrick Mooney * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
207c8c0b82SPatrick Mooney * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
217c8c0b82SPatrick Mooney * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
227c8c0b82SPatrick Mooney * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
237c8c0b82SPatrick Mooney * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
247c8c0b82SPatrick Mooney * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
257c8c0b82SPatrick Mooney * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
267c8c0b82SPatrick Mooney * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
277c8c0b82SPatrick Mooney * SUCH DAMAGE.
287c8c0b82SPatrick Mooney */
297c8c0b82SPatrick Mooney /*
307c8c0b82SPatrick Mooney * This file and its contents are supplied under the terms of the
317c8c0b82SPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
327c8c0b82SPatrick Mooney * You may only use this file in accordance with the terms of version
337c8c0b82SPatrick Mooney * 1.0 of the CDDL.
347c8c0b82SPatrick Mooney *
357c8c0b82SPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
367c8c0b82SPatrick Mooney * source. A copy of the CDDL is also available via the Internet at
377c8c0b82SPatrick Mooney * http://www.illumos.org/license/CDDL.
387c8c0b82SPatrick Mooney *
397c8c0b82SPatrick Mooney * Copyright 2014 Pluribus Networks Inc.
407c8c0b82SPatrick Mooney * Copyright 2017 Joyent, Inc.
417c8c0b82SPatrick Mooney * Copyright 2021 Oxide Computer Company
427c8c0b82SPatrick Mooney */
437c8c0b82SPatrick Mooney
447c8c0b82SPatrick Mooney #include <sys/cdefs.h>
457c8c0b82SPatrick Mooney
467c8c0b82SPatrick Mooney #include <sys/param.h>
477c8c0b82SPatrick Mooney #include <sys/queue.h>
487c8c0b82SPatrick Mooney #include <sys/mutex.h>
497c8c0b82SPatrick Mooney #include <sys/systm.h>
507c8c0b82SPatrick Mooney #include <sys/kernel.h>
518130f8e1SPatrick Mooney #include <sys/kmem.h>
527c8c0b82SPatrick Mooney #include <sys/cpuset.h>
537c8c0b82SPatrick Mooney
547c8c0b82SPatrick Mooney #include <x86/apicreg.h>
557c8c0b82SPatrick Mooney #include <machine/vmm.h>
56d515dd77SPatrick Mooney #include <sys/vmm_data.h>
577c8c0b82SPatrick Mooney
587c8c0b82SPatrick Mooney #include "vmm_lapic.h"
597c8c0b82SPatrick Mooney #include "vlapic.h"
607c8c0b82SPatrick Mooney #include "vioapic.h"
617c8c0b82SPatrick Mooney
627c8c0b82SPatrick Mooney #define IOREGSEL 0x00
637c8c0b82SPatrick Mooney #define IOWIN 0x10
647c8c0b82SPatrick Mooney
657c8c0b82SPatrick Mooney #define REDIR_ENTRIES 32
667c8c0b82SPatrick Mooney #define RTBL_RO_BITS ((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
677c8c0b82SPatrick Mooney
687c8c0b82SPatrick Mooney struct ioapic_stats {
697c8c0b82SPatrick Mooney uint64_t is_interrupts;
707c8c0b82SPatrick Mooney uint64_t is_saturate_low;
717c8c0b82SPatrick Mooney uint64_t is_saturate_high;
727c8c0b82SPatrick Mooney };
737c8c0b82SPatrick Mooney
747c8c0b82SPatrick Mooney struct vioapic {
757c8c0b82SPatrick Mooney struct vm *vm;
767c8c0b82SPatrick Mooney kmutex_t lock;
777c8c0b82SPatrick Mooney uint32_t id;
787c8c0b82SPatrick Mooney uint32_t ioregsel;
797c8c0b82SPatrick Mooney struct {
807c8c0b82SPatrick Mooney uint64_t reg;
817c8c0b82SPatrick Mooney /*
827c8c0b82SPatrick Mooney * The sum of pin asserts (+1) and deasserts (-1) are tracked in
837c8c0b82SPatrick Mooney * 'acnt'. It is clamped to prevent overflow or underflow
847c8c0b82SPatrick Mooney * should emulation consumers feed it an invalid set of
857c8c0b82SPatrick Mooney * transitions.
867c8c0b82SPatrick Mooney */
877c8c0b82SPatrick Mooney uint_t acnt;
887c8c0b82SPatrick Mooney } rtbl[REDIR_ENTRIES];
897c8c0b82SPatrick Mooney struct ioapic_stats stats;
907c8c0b82SPatrick Mooney };
917c8c0b82SPatrick Mooney
927c8c0b82SPatrick Mooney #define VIOAPIC_LOCK(vioapic) mutex_enter(&((vioapic)->lock))
937c8c0b82SPatrick Mooney #define VIOAPIC_UNLOCK(vioapic) mutex_exit(&((vioapic)->lock))
947c8c0b82SPatrick Mooney #define VIOAPIC_LOCKED(vioapic) MUTEX_HELD(&((vioapic)->lock))
957c8c0b82SPatrick Mooney
967c8c0b82SPatrick Mooney
977c8c0b82SPatrick Mooney static void
vioapic_send_intr(struct vioapic * vioapic,int pin)987c8c0b82SPatrick Mooney vioapic_send_intr(struct vioapic *vioapic, int pin)
997c8c0b82SPatrick Mooney {
1007c8c0b82SPatrick Mooney int vector, delmode;
1017c8c0b82SPatrick Mooney uint32_t low, high, dest;
1027c8c0b82SPatrick Mooney bool level, phys;
1037c8c0b82SPatrick Mooney
1047c8c0b82SPatrick Mooney VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
1057c8c0b82SPatrick Mooney ASSERT(VIOAPIC_LOCKED(vioapic));
1067c8c0b82SPatrick Mooney
1077c8c0b82SPatrick Mooney low = vioapic->rtbl[pin].reg;
1087c8c0b82SPatrick Mooney high = vioapic->rtbl[pin].reg >> 32;
1097c8c0b82SPatrick Mooney
1107c8c0b82SPatrick Mooney if ((low & IOART_INTMASK) == IOART_INTMSET) {
111d4f59ae5SPatrick Mooney /* Pin is masked */
1127c8c0b82SPatrick Mooney return;
1137c8c0b82SPatrick Mooney }
1147c8c0b82SPatrick Mooney
1157c8c0b82SPatrick Mooney phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
1167c8c0b82SPatrick Mooney delmode = low & IOART_DELMOD;
1177c8c0b82SPatrick Mooney level = low & IOART_TRGRLVL ? true : false;
1187c8c0b82SPatrick Mooney if (level) {
1197c8c0b82SPatrick Mooney if ((low & IOART_REM_IRR) != 0) {
120d4f59ae5SPatrick Mooney /* IRR already pending */
1217c8c0b82SPatrick Mooney return;
1227c8c0b82SPatrick Mooney }
1237c8c0b82SPatrick Mooney vioapic->rtbl[pin].reg |= IOART_REM_IRR;
1247c8c0b82SPatrick Mooney }
1257c8c0b82SPatrick Mooney
1267c8c0b82SPatrick Mooney vector = low & IOART_INTVEC;
1277c8c0b82SPatrick Mooney dest = high >> APIC_ID_SHIFT;
1287c8c0b82SPatrick Mooney vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
1297c8c0b82SPatrick Mooney vioapic->stats.is_interrupts++;
1307c8c0b82SPatrick Mooney }
1317c8c0b82SPatrick Mooney
1327c8c0b82SPatrick Mooney static int
vioapic_set_pinstate(struct vioapic * vioapic,int pin,bool newstate)1337c8c0b82SPatrick Mooney vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
1347c8c0b82SPatrick Mooney {
1357c8c0b82SPatrick Mooney uint_t oldcnt, newcnt;
1367c8c0b82SPatrick Mooney bool needintr = false;
1377c8c0b82SPatrick Mooney int err = 0;
1387c8c0b82SPatrick Mooney
1397c8c0b82SPatrick Mooney VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
1407c8c0b82SPatrick Mooney ASSERT(VIOAPIC_LOCKED(vioapic));
1417c8c0b82SPatrick Mooney
1427c8c0b82SPatrick Mooney oldcnt = newcnt = vioapic->rtbl[pin].acnt;
1437c8c0b82SPatrick Mooney if (newstate) {
1447c8c0b82SPatrick Mooney if (newcnt != UINT_MAX) {
1457c8c0b82SPatrick Mooney newcnt++;
1467c8c0b82SPatrick Mooney } else {
1477c8c0b82SPatrick Mooney err = E2BIG;
1487c8c0b82SPatrick Mooney DTRACE_PROBE2(vioapic__sat_high,
1497c8c0b82SPatrick Mooney struct vioapic *, vioapic, int, pin);
1507c8c0b82SPatrick Mooney vioapic->stats.is_saturate_high++;
1517c8c0b82SPatrick Mooney }
1527c8c0b82SPatrick Mooney } else {
1537c8c0b82SPatrick Mooney if (newcnt != 0) {
1547c8c0b82SPatrick Mooney newcnt--;
1557c8c0b82SPatrick Mooney } else {
1567c8c0b82SPatrick Mooney err = ERANGE;
1577c8c0b82SPatrick Mooney DTRACE_PROBE2(vioapic__sat_low,
1587c8c0b82SPatrick Mooney struct vioapic *, vioapic, int, pin);
1597c8c0b82SPatrick Mooney vioapic->stats.is_saturate_low++;
1607c8c0b82SPatrick Mooney }
1617c8c0b82SPatrick Mooney }
1627c8c0b82SPatrick Mooney vioapic->rtbl[pin].acnt = newcnt;
1637c8c0b82SPatrick Mooney
1647c8c0b82SPatrick Mooney if (oldcnt == 0 && newcnt == 1) {
1657c8c0b82SPatrick Mooney needintr = true;
1667c8c0b82SPatrick Mooney DTRACE_PROBE2(vioapic__assert, struct vioapic *, vioapic,
1677c8c0b82SPatrick Mooney int, pin);
1687c8c0b82SPatrick Mooney } else if (oldcnt == 1 && newcnt == 0) {
1697c8c0b82SPatrick Mooney DTRACE_PROBE2(vioapic__deassert, struct vioapic *, vioapic,
1707c8c0b82SPatrick Mooney int, pin);
1717c8c0b82SPatrick Mooney }
1727c8c0b82SPatrick Mooney
1737c8c0b82SPatrick Mooney if (needintr) {
1747c8c0b82SPatrick Mooney vioapic_send_intr(vioapic, pin);
1757c8c0b82SPatrick Mooney }
1767c8c0b82SPatrick Mooney return (err);
1777c8c0b82SPatrick Mooney }
1787c8c0b82SPatrick Mooney
1797c8c0b82SPatrick Mooney enum irqstate {
1807c8c0b82SPatrick Mooney IRQSTATE_ASSERT,
1817c8c0b82SPatrick Mooney IRQSTATE_DEASSERT,
1827c8c0b82SPatrick Mooney IRQSTATE_PULSE
1837c8c0b82SPatrick Mooney };
1847c8c0b82SPatrick Mooney
1857c8c0b82SPatrick Mooney static int
vioapic_set_irqstate(struct vm * vm,int irq,enum irqstate irqstate)1867c8c0b82SPatrick Mooney vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
1877c8c0b82SPatrick Mooney {
1887c8c0b82SPatrick Mooney struct vioapic *vioapic;
1897c8c0b82SPatrick Mooney int err = 0;
1907c8c0b82SPatrick Mooney
1917c8c0b82SPatrick Mooney if (irq < 0 || irq >= REDIR_ENTRIES)
1927c8c0b82SPatrick Mooney return (EINVAL);
1937c8c0b82SPatrick Mooney
1947c8c0b82SPatrick Mooney vioapic = vm_ioapic(vm);
1957c8c0b82SPatrick Mooney
1967c8c0b82SPatrick Mooney VIOAPIC_LOCK(vioapic);
1977c8c0b82SPatrick Mooney switch (irqstate) {
1987c8c0b82SPatrick Mooney case IRQSTATE_ASSERT:
1997c8c0b82SPatrick Mooney err = vioapic_set_pinstate(vioapic, irq, true);
2007c8c0b82SPatrick Mooney break;
2017c8c0b82SPatrick Mooney case IRQSTATE_DEASSERT:
2027c8c0b82SPatrick Mooney err = vioapic_set_pinstate(vioapic, irq, false);
2037c8c0b82SPatrick Mooney break;
2047c8c0b82SPatrick Mooney case IRQSTATE_PULSE:
2057c8c0b82SPatrick Mooney err = vioapic_set_pinstate(vioapic, irq, true);
2067c8c0b82SPatrick Mooney if (err == 0) {
2077c8c0b82SPatrick Mooney err = vioapic_set_pinstate(vioapic, irq, false);
2087c8c0b82SPatrick Mooney }
2097c8c0b82SPatrick Mooney break;
2107c8c0b82SPatrick Mooney default:
2117c8c0b82SPatrick Mooney panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
2127c8c0b82SPatrick Mooney }
2137c8c0b82SPatrick Mooney VIOAPIC_UNLOCK(vioapic);
2147c8c0b82SPatrick Mooney
2157c8c0b82SPatrick Mooney return (err);
2167c8c0b82SPatrick Mooney }
2177c8c0b82SPatrick Mooney
2187c8c0b82SPatrick Mooney int
vioapic_assert_irq(struct vm * vm,int irq)2197c8c0b82SPatrick Mooney vioapic_assert_irq(struct vm *vm, int irq)
2207c8c0b82SPatrick Mooney {
2217c8c0b82SPatrick Mooney
2227c8c0b82SPatrick Mooney return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
2237c8c0b82SPatrick Mooney }
2247c8c0b82SPatrick Mooney
2257c8c0b82SPatrick Mooney int
vioapic_deassert_irq(struct vm * vm,int irq)2267c8c0b82SPatrick Mooney vioapic_deassert_irq(struct vm *vm, int irq)
2277c8c0b82SPatrick Mooney {
2287c8c0b82SPatrick Mooney
2297c8c0b82SPatrick Mooney return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
2307c8c0b82SPatrick Mooney }
2317c8c0b82SPatrick Mooney
2327c8c0b82SPatrick Mooney int
vioapic_pulse_irq(struct vm * vm,int irq)2337c8c0b82SPatrick Mooney vioapic_pulse_irq(struct vm *vm, int irq)
2347c8c0b82SPatrick Mooney {
2357c8c0b82SPatrick Mooney
2367c8c0b82SPatrick Mooney return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
2377c8c0b82SPatrick Mooney }
2387c8c0b82SPatrick Mooney
2397c8c0b82SPatrick Mooney static uint32_t
vioapic_read(struct vioapic * vioapic,int vcpuid,uint32_t addr)2407c8c0b82SPatrick Mooney vioapic_read(struct vioapic *vioapic, int vcpuid, uint32_t addr)
2417c8c0b82SPatrick Mooney {
2427c8c0b82SPatrick Mooney int regnum, pin, rshift;
2437c8c0b82SPatrick Mooney
2447c8c0b82SPatrick Mooney regnum = addr & 0xff;
2457c8c0b82SPatrick Mooney switch (regnum) {
2467c8c0b82SPatrick Mooney case IOAPIC_ID:
2477c8c0b82SPatrick Mooney return (vioapic->id);
2487c8c0b82SPatrick Mooney break;
2497c8c0b82SPatrick Mooney case IOAPIC_VER:
2507c8c0b82SPatrick Mooney return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
2517c8c0b82SPatrick Mooney break;
2527c8c0b82SPatrick Mooney case IOAPIC_ARB:
2537c8c0b82SPatrick Mooney return (vioapic->id);
2547c8c0b82SPatrick Mooney break;
2557c8c0b82SPatrick Mooney default:
2567c8c0b82SPatrick Mooney break;
2577c8c0b82SPatrick Mooney }
2587c8c0b82SPatrick Mooney
2597c8c0b82SPatrick Mooney /* redirection table entries */
2607c8c0b82SPatrick Mooney if (regnum >= IOAPIC_REDTBL &&
2617c8c0b82SPatrick Mooney regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
2627c8c0b82SPatrick Mooney pin = (regnum - IOAPIC_REDTBL) / 2;
2637c8c0b82SPatrick Mooney if ((regnum - IOAPIC_REDTBL) % 2)
2647c8c0b82SPatrick Mooney rshift = 32;
2657c8c0b82SPatrick Mooney else
2667c8c0b82SPatrick Mooney rshift = 0;
2677c8c0b82SPatrick Mooney
2687c8c0b82SPatrick Mooney return (vioapic->rtbl[pin].reg >> rshift);
2697c8c0b82SPatrick Mooney }
2707c8c0b82SPatrick Mooney
2717c8c0b82SPatrick Mooney return (0);
2727c8c0b82SPatrick Mooney }
2737c8c0b82SPatrick Mooney
2747c8c0b82SPatrick Mooney static void
vioapic_write(struct vioapic * vioapic,int vcpuid,uint32_t addr,uint32_t data)2757c8c0b82SPatrick Mooney vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
2767c8c0b82SPatrick Mooney {
2777c8c0b82SPatrick Mooney uint64_t data64, mask64;
2787c8c0b82SPatrick Mooney int regnum, pin, lshift;
2797c8c0b82SPatrick Mooney
2807c8c0b82SPatrick Mooney regnum = addr & 0xff;
2817c8c0b82SPatrick Mooney switch (regnum) {
2827c8c0b82SPatrick Mooney case IOAPIC_ID:
2837c8c0b82SPatrick Mooney vioapic->id = data & APIC_ID_MASK;
2847c8c0b82SPatrick Mooney break;
2857c8c0b82SPatrick Mooney case IOAPIC_VER:
2867c8c0b82SPatrick Mooney case IOAPIC_ARB:
2877c8c0b82SPatrick Mooney /* readonly */
2887c8c0b82SPatrick Mooney break;
2897c8c0b82SPatrick Mooney default:
2907c8c0b82SPatrick Mooney break;
2917c8c0b82SPatrick Mooney }
2927c8c0b82SPatrick Mooney
2937c8c0b82SPatrick Mooney /* redirection table entries */
2947c8c0b82SPatrick Mooney if (regnum >= IOAPIC_REDTBL &&
2957c8c0b82SPatrick Mooney regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
2967c8c0b82SPatrick Mooney pin = (regnum - IOAPIC_REDTBL) / 2;
2977c8c0b82SPatrick Mooney if ((regnum - IOAPIC_REDTBL) % 2)
2987c8c0b82SPatrick Mooney lshift = 32;
2997c8c0b82SPatrick Mooney else
3007c8c0b82SPatrick Mooney lshift = 0;
3017c8c0b82SPatrick Mooney
3027c8c0b82SPatrick Mooney data64 = (uint64_t)data << lshift;
3037c8c0b82SPatrick Mooney mask64 = (uint64_t)0xffffffff << lshift;
3047c8c0b82SPatrick Mooney vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
3057c8c0b82SPatrick Mooney vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
3067c8c0b82SPatrick Mooney
3077c8c0b82SPatrick Mooney /*
3087c8c0b82SPatrick Mooney * Switching from level to edge triggering will clear the IRR
3097c8c0b82SPatrick Mooney * bit. This is what FreeBSD will do in order to EOI an
3107c8c0b82SPatrick Mooney * interrupt when the IO-APIC doesn't support targeted EOI (see
3117c8c0b82SPatrick Mooney * _ioapic_eoi_source).
3127c8c0b82SPatrick Mooney */
3137c8c0b82SPatrick Mooney if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
3147c8c0b82SPatrick Mooney (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
3157c8c0b82SPatrick Mooney vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
3167c8c0b82SPatrick Mooney
3177c8c0b82SPatrick Mooney /*
3187c8c0b82SPatrick Mooney * Generate an interrupt if the following conditions are met:
3197c8c0b82SPatrick Mooney * - pin trigger mode is level
3207c8c0b82SPatrick Mooney * - pin level is asserted
3217c8c0b82SPatrick Mooney */
3227c8c0b82SPatrick Mooney if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
3237c8c0b82SPatrick Mooney (vioapic->rtbl[pin].acnt > 0)) {
3247c8c0b82SPatrick Mooney vioapic_send_intr(vioapic, pin);
3257c8c0b82SPatrick Mooney }
3267c8c0b82SPatrick Mooney }
3277c8c0b82SPatrick Mooney }
3287c8c0b82SPatrick Mooney
3297c8c0b82SPatrick Mooney static int
vioapic_mmio_rw(struct vioapic * vioapic,int vcpuid,uint64_t gpa,uint64_t * data,int size,bool doread)3307c8c0b82SPatrick Mooney vioapic_mmio_rw(struct vioapic *vioapic, int vcpuid, uint64_t gpa,
3317c8c0b82SPatrick Mooney uint64_t *data, int size, bool doread)
3327c8c0b82SPatrick Mooney {
3337c8c0b82SPatrick Mooney uint64_t offset;
3347c8c0b82SPatrick Mooney
3357c8c0b82SPatrick Mooney offset = gpa - VIOAPIC_BASE;
3367c8c0b82SPatrick Mooney
3377c8c0b82SPatrick Mooney /*
3387c8c0b82SPatrick Mooney * The IOAPIC specification allows 32-bit wide accesses to the
3397c8c0b82SPatrick Mooney * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
3407c8c0b82SPatrick Mooney */
3417c8c0b82SPatrick Mooney if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
3427c8c0b82SPatrick Mooney if (doread)
3437c8c0b82SPatrick Mooney *data = 0;
3447c8c0b82SPatrick Mooney return (0);
3457c8c0b82SPatrick Mooney }
3467c8c0b82SPatrick Mooney
3477c8c0b82SPatrick Mooney VIOAPIC_LOCK(vioapic);
3487c8c0b82SPatrick Mooney if (offset == IOREGSEL) {
3497c8c0b82SPatrick Mooney if (doread)
3507c8c0b82SPatrick Mooney *data = vioapic->ioregsel;
3517c8c0b82SPatrick Mooney else
3527c8c0b82SPatrick Mooney vioapic->ioregsel = *data;
3537c8c0b82SPatrick Mooney } else {
3547c8c0b82SPatrick Mooney if (doread) {
3557c8c0b82SPatrick Mooney *data = vioapic_read(vioapic, vcpuid,
3567c8c0b82SPatrick Mooney vioapic->ioregsel);
3577c8c0b82SPatrick Mooney } else {
3587c8c0b82SPatrick Mooney vioapic_write(vioapic, vcpuid, vioapic->ioregsel,
3597c8c0b82SPatrick Mooney *data);
3607c8c0b82SPatrick Mooney }
3617c8c0b82SPatrick Mooney }
3627c8c0b82SPatrick Mooney VIOAPIC_UNLOCK(vioapic);
3637c8c0b82SPatrick Mooney
3647c8c0b82SPatrick Mooney return (0);
3657c8c0b82SPatrick Mooney }
3667c8c0b82SPatrick Mooney
3677c8c0b82SPatrick Mooney int
vioapic_mmio_read(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t * rval,int size)3687c8c0b82SPatrick Mooney vioapic_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
3697c8c0b82SPatrick Mooney int size)
3707c8c0b82SPatrick Mooney {
3717c8c0b82SPatrick Mooney int error;
3727c8c0b82SPatrick Mooney struct vioapic *vioapic;
3737c8c0b82SPatrick Mooney
3747c8c0b82SPatrick Mooney vioapic = vm_ioapic(vm);
3757c8c0b82SPatrick Mooney error = vioapic_mmio_rw(vioapic, vcpuid, gpa, rval, size, true);
3767c8c0b82SPatrick Mooney return (error);
3777c8c0b82SPatrick Mooney }
3787c8c0b82SPatrick Mooney
3797c8c0b82SPatrick Mooney int
vioapic_mmio_write(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t wval,int size)3807c8c0b82SPatrick Mooney vioapic_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t wval,
3817c8c0b82SPatrick Mooney int size)
3827c8c0b82SPatrick Mooney {
3837c8c0b82SPatrick Mooney int error;
3847c8c0b82SPatrick Mooney struct vioapic *vioapic;
3857c8c0b82SPatrick Mooney
3867c8c0b82SPatrick Mooney vioapic = vm_ioapic(vm);
3877c8c0b82SPatrick Mooney error = vioapic_mmio_rw(vioapic, vcpuid, gpa, &wval, size, false);
3887c8c0b82SPatrick Mooney return (error);
3897c8c0b82SPatrick Mooney }
3907c8c0b82SPatrick Mooney
3917c8c0b82SPatrick Mooney void
vioapic_process_eoi(struct vm * vm,int vcpuid,int vector)3927c8c0b82SPatrick Mooney vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
3937c8c0b82SPatrick Mooney {
3947c8c0b82SPatrick Mooney struct vioapic *vioapic;
3957c8c0b82SPatrick Mooney int pin;
3967c8c0b82SPatrick Mooney
3977c8c0b82SPatrick Mooney KASSERT(vector >= 0 && vector < 256,
3987c8c0b82SPatrick Mooney ("vioapic_process_eoi: invalid vector %d", vector));
3997c8c0b82SPatrick Mooney
4007c8c0b82SPatrick Mooney vioapic = vm_ioapic(vm);
4017c8c0b82SPatrick Mooney
4027c8c0b82SPatrick Mooney /*
4037c8c0b82SPatrick Mooney * XXX keep track of the pins associated with this vector instead
4047c8c0b82SPatrick Mooney * of iterating on every single pin each time.
4057c8c0b82SPatrick Mooney */
4067c8c0b82SPatrick Mooney VIOAPIC_LOCK(vioapic);
4077c8c0b82SPatrick Mooney for (pin = 0; pin < REDIR_ENTRIES; pin++) {
4087c8c0b82SPatrick Mooney if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
4097c8c0b82SPatrick Mooney continue;
4107c8c0b82SPatrick Mooney if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
4117c8c0b82SPatrick Mooney continue;
4127c8c0b82SPatrick Mooney vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
4137c8c0b82SPatrick Mooney if (vioapic->rtbl[pin].acnt > 0) {
414d4f59ae5SPatrick Mooney /* Pin asserted at EOI */
4157c8c0b82SPatrick Mooney vioapic_send_intr(vioapic, pin);
4167c8c0b82SPatrick Mooney }
4177c8c0b82SPatrick Mooney }
4187c8c0b82SPatrick Mooney VIOAPIC_UNLOCK(vioapic);
4197c8c0b82SPatrick Mooney }
4207c8c0b82SPatrick Mooney
4217c8c0b82SPatrick Mooney struct vioapic *
vioapic_init(struct vm * vm)4227c8c0b82SPatrick Mooney vioapic_init(struct vm *vm)
4237c8c0b82SPatrick Mooney {
4247c8c0b82SPatrick Mooney int i;
4257c8c0b82SPatrick Mooney struct vioapic *vioapic;
4267c8c0b82SPatrick Mooney
4278130f8e1SPatrick Mooney vioapic = kmem_zalloc(sizeof (struct vioapic), KM_SLEEP);
4287c8c0b82SPatrick Mooney
4297c8c0b82SPatrick Mooney vioapic->vm = vm;
4307c8c0b82SPatrick Mooney mutex_init(&vioapic->lock, NULL, MUTEX_ADAPTIVE, NULL);
4317c8c0b82SPatrick Mooney
4327c8c0b82SPatrick Mooney /* Initialize all redirection entries to mask all interrupts */
4337c8c0b82SPatrick Mooney for (i = 0; i < REDIR_ENTRIES; i++)
4347c8c0b82SPatrick Mooney vioapic->rtbl[i].reg = 0x0001000000010000UL;
4357c8c0b82SPatrick Mooney
4367c8c0b82SPatrick Mooney return (vioapic);
4377c8c0b82SPatrick Mooney }
4387c8c0b82SPatrick Mooney
4397c8c0b82SPatrick Mooney void
vioapic_cleanup(struct vioapic * vioapic)4407c8c0b82SPatrick Mooney vioapic_cleanup(struct vioapic *vioapic)
4417c8c0b82SPatrick Mooney {
4427c8c0b82SPatrick Mooney mutex_destroy(&vioapic->lock);
4438130f8e1SPatrick Mooney kmem_free(vioapic, sizeof (*vioapic));
4447c8c0b82SPatrick Mooney }
4457c8c0b82SPatrick Mooney
4467c8c0b82SPatrick Mooney int
vioapic_pincount(struct vm * vm)4477c8c0b82SPatrick Mooney vioapic_pincount(struct vm *vm)
4487c8c0b82SPatrick Mooney {
4497c8c0b82SPatrick Mooney
4507c8c0b82SPatrick Mooney return (REDIR_ENTRIES);
4517c8c0b82SPatrick Mooney }
452d515dd77SPatrick Mooney
453d515dd77SPatrick Mooney static int
vioapic_data_read(void * datap,const vmm_data_req_t * req)454d515dd77SPatrick Mooney vioapic_data_read(void *datap, const vmm_data_req_t *req)
455d515dd77SPatrick Mooney {
456d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
457d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
458a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_ioapic_v1));
459d515dd77SPatrick Mooney
460d515dd77SPatrick Mooney struct vioapic *vioapic = datap;
461d515dd77SPatrick Mooney struct vdi_ioapic_v1 *out = req->vdr_data;
462d515dd77SPatrick Mooney
463d515dd77SPatrick Mooney VIOAPIC_LOCK(vioapic);
464d515dd77SPatrick Mooney out->vi_id = vioapic->id;
465d515dd77SPatrick Mooney out->vi_reg_sel = vioapic->ioregsel;
466d515dd77SPatrick Mooney for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
467d515dd77SPatrick Mooney out->vi_pin_reg[i] = vioapic->rtbl[i].reg;
468d515dd77SPatrick Mooney out->vi_pin_level[i] = vioapic->rtbl[i].acnt;
469d515dd77SPatrick Mooney }
470d515dd77SPatrick Mooney VIOAPIC_UNLOCK(vioapic);
471d515dd77SPatrick Mooney
472d515dd77SPatrick Mooney return (0);
473d515dd77SPatrick Mooney }
474d515dd77SPatrick Mooney
475d515dd77SPatrick Mooney static int
vioapic_data_write(void * datap,const vmm_data_req_t * req)476d515dd77SPatrick Mooney vioapic_data_write(void *datap, const vmm_data_req_t *req)
477d515dd77SPatrick Mooney {
478d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
479d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
480a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_ioapic_v1));
481d515dd77SPatrick Mooney
482d515dd77SPatrick Mooney struct vioapic *vioapic = datap;
483d515dd77SPatrick Mooney const struct vdi_ioapic_v1 *src = req->vdr_data;
484d515dd77SPatrick Mooney
485d515dd77SPatrick Mooney VIOAPIC_LOCK(vioapic);
486d515dd77SPatrick Mooney vioapic->id = src->vi_id;
487d515dd77SPatrick Mooney vioapic->ioregsel = src->vi_reg_sel;
488d515dd77SPatrick Mooney for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
489d515dd77SPatrick Mooney vioapic->rtbl[i].reg = src->vi_pin_reg[i] & ~RTBL_RO_BITS;
490d515dd77SPatrick Mooney vioapic->rtbl[i].acnt = src->vi_pin_level[i];
491d515dd77SPatrick Mooney }
492d515dd77SPatrick Mooney VIOAPIC_UNLOCK(vioapic);
493d515dd77SPatrick Mooney
494d515dd77SPatrick Mooney return (0);
495d515dd77SPatrick Mooney }
496d515dd77SPatrick Mooney
497d515dd77SPatrick Mooney static const vmm_data_version_entry_t ioapic_v1 = {
498d515dd77SPatrick Mooney .vdve_class = VDC_IOAPIC,
499d515dd77SPatrick Mooney .vdve_version = 1,
500d515dd77SPatrick Mooney .vdve_len_expect = sizeof (struct vdi_ioapic_v1),
501d515dd77SPatrick Mooney .vdve_readf = vioapic_data_read,
502d515dd77SPatrick Mooney .vdve_writef = vioapic_data_write,
503d515dd77SPatrick Mooney };
504d515dd77SPatrick Mooney VMM_DATA_VERSION(ioapic_v1);
505