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