xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vioapic.c (revision 8130f8e1)
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>
54*8130f8e1SPatrick 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>
597c8c0b82SPatrick Mooney 
607c8c0b82SPatrick Mooney #include "vmm_lapic.h"
617c8c0b82SPatrick Mooney #include "vlapic.h"
627c8c0b82SPatrick Mooney #include "vioapic.h"
637c8c0b82SPatrick Mooney 
647c8c0b82SPatrick Mooney #define	IOREGSEL	0x00
657c8c0b82SPatrick Mooney #define	IOWIN		0x10
667c8c0b82SPatrick Mooney 
677c8c0b82SPatrick Mooney #define	REDIR_ENTRIES	32
687c8c0b82SPatrick Mooney #define	RTBL_RO_BITS	((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
697c8c0b82SPatrick Mooney 
707c8c0b82SPatrick Mooney struct ioapic_stats {
717c8c0b82SPatrick Mooney 	uint64_t	is_interrupts;
727c8c0b82SPatrick Mooney 	uint64_t	is_saturate_low;
737c8c0b82SPatrick Mooney 	uint64_t	is_saturate_high;
747c8c0b82SPatrick Mooney };
757c8c0b82SPatrick Mooney 
767c8c0b82SPatrick Mooney struct vioapic {
777c8c0b82SPatrick Mooney 	struct vm	*vm;
787c8c0b82SPatrick Mooney 	kmutex_t	lock;
797c8c0b82SPatrick Mooney 	uint32_t	id;
807c8c0b82SPatrick Mooney 	uint32_t	ioregsel;
817c8c0b82SPatrick Mooney 	struct {
827c8c0b82SPatrick Mooney 		uint64_t reg;
837c8c0b82SPatrick Mooney 		/*
847c8c0b82SPatrick Mooney 		 * The sum of pin asserts (+1) and deasserts (-1) are tracked in
857c8c0b82SPatrick Mooney 		 * 'acnt'.  It is clamped to prevent overflow or underflow
867c8c0b82SPatrick Mooney 		 * should emulation consumers feed it an invalid set of
877c8c0b82SPatrick Mooney 		 * transitions.
887c8c0b82SPatrick Mooney 		 */
897c8c0b82SPatrick Mooney 		uint_t acnt;
907c8c0b82SPatrick Mooney 	} rtbl[REDIR_ENTRIES];
917c8c0b82SPatrick Mooney 	struct ioapic_stats stats;
927c8c0b82SPatrick Mooney };
937c8c0b82SPatrick Mooney 
947c8c0b82SPatrick Mooney #define	VIOAPIC_LOCK(vioapic)		mutex_enter(&((vioapic)->lock))
957c8c0b82SPatrick Mooney #define	VIOAPIC_UNLOCK(vioapic)		mutex_exit(&((vioapic)->lock))
967c8c0b82SPatrick Mooney #define	VIOAPIC_LOCKED(vioapic)		MUTEX_HELD(&((vioapic)->lock))
977c8c0b82SPatrick Mooney 
987c8c0b82SPatrick Mooney 
997c8c0b82SPatrick Mooney static void
1007c8c0b82SPatrick Mooney vioapic_send_intr(struct vioapic *vioapic, int pin)
1017c8c0b82SPatrick Mooney {
1027c8c0b82SPatrick Mooney 	int vector, delmode;
1037c8c0b82SPatrick Mooney 	uint32_t low, high, dest;
1047c8c0b82SPatrick Mooney 	bool level, phys;
1057c8c0b82SPatrick Mooney 
1067c8c0b82SPatrick Mooney 	VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
1077c8c0b82SPatrick Mooney 	ASSERT(VIOAPIC_LOCKED(vioapic));
1087c8c0b82SPatrick Mooney 
1097c8c0b82SPatrick Mooney 	low = vioapic->rtbl[pin].reg;
1107c8c0b82SPatrick Mooney 	high = vioapic->rtbl[pin].reg >> 32;
1117c8c0b82SPatrick Mooney 
1127c8c0b82SPatrick Mooney 	if ((low & IOART_INTMASK) == IOART_INTMSET) {
113d4f59ae5SPatrick Mooney 		/* Pin is masked */
1147c8c0b82SPatrick Mooney 		return;
1157c8c0b82SPatrick Mooney 	}
1167c8c0b82SPatrick Mooney 
1177c8c0b82SPatrick Mooney 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
1187c8c0b82SPatrick Mooney 	delmode = low & IOART_DELMOD;
1197c8c0b82SPatrick Mooney 	level = low & IOART_TRGRLVL ? true : false;
1207c8c0b82SPatrick Mooney 	if (level) {
1217c8c0b82SPatrick Mooney 		if ((low & IOART_REM_IRR) != 0) {
122d4f59ae5SPatrick Mooney 			/* IRR already pending */
1237c8c0b82SPatrick Mooney 			return;
1247c8c0b82SPatrick Mooney 		}
1257c8c0b82SPatrick Mooney 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
1267c8c0b82SPatrick Mooney 	}
1277c8c0b82SPatrick Mooney 
1287c8c0b82SPatrick Mooney 	vector = low & IOART_INTVEC;
1297c8c0b82SPatrick Mooney 	dest = high >> APIC_ID_SHIFT;
1307c8c0b82SPatrick Mooney 	vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
1317c8c0b82SPatrick Mooney 	vioapic->stats.is_interrupts++;
1327c8c0b82SPatrick Mooney }
1337c8c0b82SPatrick Mooney 
1347c8c0b82SPatrick Mooney static int
1357c8c0b82SPatrick Mooney vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
1367c8c0b82SPatrick Mooney {
1377c8c0b82SPatrick Mooney 	uint_t oldcnt, newcnt;
1387c8c0b82SPatrick Mooney 	bool needintr = false;
1397c8c0b82SPatrick Mooney 	int err = 0;
1407c8c0b82SPatrick Mooney 
1417c8c0b82SPatrick Mooney 	VERIFY(pin >= 0 && pin < REDIR_ENTRIES);
1427c8c0b82SPatrick Mooney 	ASSERT(VIOAPIC_LOCKED(vioapic));
1437c8c0b82SPatrick Mooney 
1447c8c0b82SPatrick Mooney 	oldcnt = newcnt = vioapic->rtbl[pin].acnt;
1457c8c0b82SPatrick Mooney 	if (newstate) {
1467c8c0b82SPatrick Mooney 		if (newcnt != UINT_MAX) {
1477c8c0b82SPatrick Mooney 			newcnt++;
1487c8c0b82SPatrick Mooney 		} else {
1497c8c0b82SPatrick Mooney 			err = E2BIG;
1507c8c0b82SPatrick Mooney 			DTRACE_PROBE2(vioapic__sat_high,
1517c8c0b82SPatrick Mooney 			    struct vioapic *, vioapic, int, pin);
1527c8c0b82SPatrick Mooney 			vioapic->stats.is_saturate_high++;
1537c8c0b82SPatrick Mooney 		}
1547c8c0b82SPatrick Mooney 	} else {
1557c8c0b82SPatrick Mooney 		if (newcnt != 0) {
1567c8c0b82SPatrick Mooney 			newcnt--;
1577c8c0b82SPatrick Mooney 		} else {
1587c8c0b82SPatrick Mooney 			err = ERANGE;
1597c8c0b82SPatrick Mooney 			DTRACE_PROBE2(vioapic__sat_low,
1607c8c0b82SPatrick Mooney 			    struct vioapic *, vioapic, int, pin);
1617c8c0b82SPatrick Mooney 			vioapic->stats.is_saturate_low++;
1627c8c0b82SPatrick Mooney 		}
1637c8c0b82SPatrick Mooney 	}
1647c8c0b82SPatrick Mooney 	vioapic->rtbl[pin].acnt = newcnt;
1657c8c0b82SPatrick Mooney 
1667c8c0b82SPatrick Mooney 	if (oldcnt == 0 && newcnt == 1) {
1677c8c0b82SPatrick Mooney 		needintr = true;
1687c8c0b82SPatrick Mooney 		DTRACE_PROBE2(vioapic__assert, struct vioapic *, vioapic,
1697c8c0b82SPatrick Mooney 		    int, pin);
1707c8c0b82SPatrick Mooney 	} else if (oldcnt == 1 && newcnt == 0) {
1717c8c0b82SPatrick Mooney 		DTRACE_PROBE2(vioapic__deassert, struct vioapic *, vioapic,
1727c8c0b82SPatrick Mooney 		    int, pin);
1737c8c0b82SPatrick Mooney 	}
1747c8c0b82SPatrick Mooney 
1757c8c0b82SPatrick Mooney 	if (needintr) {
1767c8c0b82SPatrick Mooney 		vioapic_send_intr(vioapic, pin);
1777c8c0b82SPatrick Mooney 	}
1787c8c0b82SPatrick Mooney 	return (err);
1797c8c0b82SPatrick Mooney }
1807c8c0b82SPatrick Mooney 
1817c8c0b82SPatrick Mooney enum irqstate {
1827c8c0b82SPatrick Mooney 	IRQSTATE_ASSERT,
1837c8c0b82SPatrick Mooney 	IRQSTATE_DEASSERT,
1847c8c0b82SPatrick Mooney 	IRQSTATE_PULSE
1857c8c0b82SPatrick Mooney };
1867c8c0b82SPatrick Mooney 
1877c8c0b82SPatrick Mooney static int
1887c8c0b82SPatrick Mooney vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
1897c8c0b82SPatrick Mooney {
1907c8c0b82SPatrick Mooney 	struct vioapic *vioapic;
1917c8c0b82SPatrick Mooney 	int err = 0;
1927c8c0b82SPatrick Mooney 
1937c8c0b82SPatrick Mooney 	if (irq < 0 || irq >= REDIR_ENTRIES)
1947c8c0b82SPatrick Mooney 		return (EINVAL);
1957c8c0b82SPatrick Mooney 
1967c8c0b82SPatrick Mooney 	vioapic = vm_ioapic(vm);
1977c8c0b82SPatrick Mooney 
1987c8c0b82SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
1997c8c0b82SPatrick Mooney 	switch (irqstate) {
2007c8c0b82SPatrick Mooney 	case IRQSTATE_ASSERT:
2017c8c0b82SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, true);
2027c8c0b82SPatrick Mooney 		break;
2037c8c0b82SPatrick Mooney 	case IRQSTATE_DEASSERT:
2047c8c0b82SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, false);
2057c8c0b82SPatrick Mooney 		break;
2067c8c0b82SPatrick Mooney 	case IRQSTATE_PULSE:
2077c8c0b82SPatrick Mooney 		err = vioapic_set_pinstate(vioapic, irq, true);
2087c8c0b82SPatrick Mooney 		if (err == 0) {
2097c8c0b82SPatrick Mooney 			err = vioapic_set_pinstate(vioapic, irq, false);
2107c8c0b82SPatrick Mooney 		}
2117c8c0b82SPatrick Mooney 		break;
2127c8c0b82SPatrick Mooney 	default:
2137c8c0b82SPatrick Mooney 		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
2147c8c0b82SPatrick Mooney 	}
2157c8c0b82SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
2167c8c0b82SPatrick Mooney 
2177c8c0b82SPatrick Mooney 	return (err);
2187c8c0b82SPatrick Mooney }
2197c8c0b82SPatrick Mooney 
2207c8c0b82SPatrick Mooney int
2217c8c0b82SPatrick Mooney vioapic_assert_irq(struct vm *vm, int irq)
2227c8c0b82SPatrick Mooney {
2237c8c0b82SPatrick Mooney 
2247c8c0b82SPatrick Mooney 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
2257c8c0b82SPatrick Mooney }
2267c8c0b82SPatrick Mooney 
2277c8c0b82SPatrick Mooney int
2287c8c0b82SPatrick Mooney vioapic_deassert_irq(struct vm *vm, int irq)
2297c8c0b82SPatrick Mooney {
2307c8c0b82SPatrick Mooney 
2317c8c0b82SPatrick Mooney 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
2327c8c0b82SPatrick Mooney }
2337c8c0b82SPatrick Mooney 
2347c8c0b82SPatrick Mooney int
2357c8c0b82SPatrick Mooney vioapic_pulse_irq(struct vm *vm, int irq)
2367c8c0b82SPatrick Mooney {
2377c8c0b82SPatrick Mooney 
2387c8c0b82SPatrick Mooney 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
2397c8c0b82SPatrick Mooney }
2407c8c0b82SPatrick Mooney 
2417c8c0b82SPatrick Mooney static uint32_t
2427c8c0b82SPatrick Mooney vioapic_read(struct vioapic *vioapic, int vcpuid, uint32_t addr)
2437c8c0b82SPatrick Mooney {
2447c8c0b82SPatrick Mooney 	int regnum, pin, rshift;
2457c8c0b82SPatrick Mooney 
2467c8c0b82SPatrick Mooney 	regnum = addr & 0xff;
2477c8c0b82SPatrick Mooney 	switch (regnum) {
2487c8c0b82SPatrick Mooney 	case IOAPIC_ID:
2497c8c0b82SPatrick Mooney 		return (vioapic->id);
2507c8c0b82SPatrick Mooney 		break;
2517c8c0b82SPatrick Mooney 	case IOAPIC_VER:
2527c8c0b82SPatrick Mooney 		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
2537c8c0b82SPatrick Mooney 		break;
2547c8c0b82SPatrick Mooney 	case IOAPIC_ARB:
2557c8c0b82SPatrick Mooney 		return (vioapic->id);
2567c8c0b82SPatrick Mooney 		break;
2577c8c0b82SPatrick Mooney 	default:
2587c8c0b82SPatrick Mooney 		break;
2597c8c0b82SPatrick Mooney 	}
2607c8c0b82SPatrick Mooney 
2617c8c0b82SPatrick Mooney 	/* redirection table entries */
2627c8c0b82SPatrick Mooney 	if (regnum >= IOAPIC_REDTBL &&
2637c8c0b82SPatrick Mooney 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
2647c8c0b82SPatrick Mooney 		pin = (regnum - IOAPIC_REDTBL) / 2;
2657c8c0b82SPatrick Mooney 		if ((regnum - IOAPIC_REDTBL) % 2)
2667c8c0b82SPatrick Mooney 			rshift = 32;
2677c8c0b82SPatrick Mooney 		else
2687c8c0b82SPatrick Mooney 			rshift = 0;
2697c8c0b82SPatrick Mooney 
2707c8c0b82SPatrick Mooney 		return (vioapic->rtbl[pin].reg >> rshift);
2717c8c0b82SPatrick Mooney 	}
2727c8c0b82SPatrick Mooney 
2737c8c0b82SPatrick Mooney 	return (0);
2747c8c0b82SPatrick Mooney }
2757c8c0b82SPatrick Mooney 
2767c8c0b82SPatrick Mooney static void
2777c8c0b82SPatrick Mooney vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
2787c8c0b82SPatrick Mooney {
2797c8c0b82SPatrick Mooney 	uint64_t data64, mask64;
2807c8c0b82SPatrick Mooney 	int regnum, pin, lshift;
2817c8c0b82SPatrick Mooney 
2827c8c0b82SPatrick Mooney 	regnum = addr & 0xff;
2837c8c0b82SPatrick Mooney 	switch (regnum) {
2847c8c0b82SPatrick Mooney 	case IOAPIC_ID:
2857c8c0b82SPatrick Mooney 		vioapic->id = data & APIC_ID_MASK;
2867c8c0b82SPatrick Mooney 		break;
2877c8c0b82SPatrick Mooney 	case IOAPIC_VER:
2887c8c0b82SPatrick Mooney 	case IOAPIC_ARB:
2897c8c0b82SPatrick Mooney 		/* readonly */
2907c8c0b82SPatrick Mooney 		break;
2917c8c0b82SPatrick Mooney 	default:
2927c8c0b82SPatrick Mooney 		break;
2937c8c0b82SPatrick Mooney 	}
2947c8c0b82SPatrick Mooney 
2957c8c0b82SPatrick Mooney 	/* redirection table entries */
2967c8c0b82SPatrick Mooney 	if (regnum >= IOAPIC_REDTBL &&
2977c8c0b82SPatrick Mooney 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
2987c8c0b82SPatrick Mooney 		pin = (regnum - IOAPIC_REDTBL) / 2;
2997c8c0b82SPatrick Mooney 		if ((regnum - IOAPIC_REDTBL) % 2)
3007c8c0b82SPatrick Mooney 			lshift = 32;
3017c8c0b82SPatrick Mooney 		else
3027c8c0b82SPatrick Mooney 			lshift = 0;
3037c8c0b82SPatrick Mooney 
3047c8c0b82SPatrick Mooney 		data64 = (uint64_t)data << lshift;
3057c8c0b82SPatrick Mooney 		mask64 = (uint64_t)0xffffffff << lshift;
3067c8c0b82SPatrick Mooney 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
3077c8c0b82SPatrick Mooney 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
3087c8c0b82SPatrick Mooney 
3097c8c0b82SPatrick Mooney 		/*
3107c8c0b82SPatrick Mooney 		 * Switching from level to edge triggering will clear the IRR
3117c8c0b82SPatrick Mooney 		 * bit. This is what FreeBSD will do in order to EOI an
3127c8c0b82SPatrick Mooney 		 * interrupt when the IO-APIC doesn't support targeted EOI (see
3137c8c0b82SPatrick Mooney 		 * _ioapic_eoi_source).
3147c8c0b82SPatrick Mooney 		 */
3157c8c0b82SPatrick Mooney 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
3167c8c0b82SPatrick Mooney 		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
3177c8c0b82SPatrick Mooney 			vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
3187c8c0b82SPatrick Mooney 
3197c8c0b82SPatrick Mooney 		/*
3207c8c0b82SPatrick Mooney 		 * Generate an interrupt if the following conditions are met:
3217c8c0b82SPatrick Mooney 		 * - pin trigger mode is level
3227c8c0b82SPatrick Mooney 		 * - pin level is asserted
3237c8c0b82SPatrick Mooney 		 */
3247c8c0b82SPatrick Mooney 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
3257c8c0b82SPatrick Mooney 		    (vioapic->rtbl[pin].acnt > 0)) {
3267c8c0b82SPatrick Mooney 			vioapic_send_intr(vioapic, pin);
3277c8c0b82SPatrick Mooney 		}
3287c8c0b82SPatrick Mooney 	}
3297c8c0b82SPatrick Mooney }
3307c8c0b82SPatrick Mooney 
3317c8c0b82SPatrick Mooney static int
3327c8c0b82SPatrick Mooney vioapic_mmio_rw(struct vioapic *vioapic, int vcpuid, uint64_t gpa,
3337c8c0b82SPatrick Mooney     uint64_t *data, int size, bool doread)
3347c8c0b82SPatrick Mooney {
3357c8c0b82SPatrick Mooney 	uint64_t offset;
3367c8c0b82SPatrick Mooney 
3377c8c0b82SPatrick Mooney 	offset = gpa - VIOAPIC_BASE;
3387c8c0b82SPatrick Mooney 
3397c8c0b82SPatrick Mooney 	/*
3407c8c0b82SPatrick Mooney 	 * The IOAPIC specification allows 32-bit wide accesses to the
3417c8c0b82SPatrick Mooney 	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
3427c8c0b82SPatrick Mooney 	 */
3437c8c0b82SPatrick Mooney 	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
3447c8c0b82SPatrick Mooney 		if (doread)
3457c8c0b82SPatrick Mooney 			*data = 0;
3467c8c0b82SPatrick Mooney 		return (0);
3477c8c0b82SPatrick Mooney 	}
3487c8c0b82SPatrick Mooney 
3497c8c0b82SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
3507c8c0b82SPatrick Mooney 	if (offset == IOREGSEL) {
3517c8c0b82SPatrick Mooney 		if (doread)
3527c8c0b82SPatrick Mooney 			*data = vioapic->ioregsel;
3537c8c0b82SPatrick Mooney 		else
3547c8c0b82SPatrick Mooney 			vioapic->ioregsel = *data;
3557c8c0b82SPatrick Mooney 	} else {
3567c8c0b82SPatrick Mooney 		if (doread) {
3577c8c0b82SPatrick Mooney 			*data = vioapic_read(vioapic, vcpuid,
3587c8c0b82SPatrick Mooney 			    vioapic->ioregsel);
3597c8c0b82SPatrick Mooney 		} else {
3607c8c0b82SPatrick Mooney 			vioapic_write(vioapic, vcpuid, vioapic->ioregsel,
3617c8c0b82SPatrick Mooney 			    *data);
3627c8c0b82SPatrick Mooney 		}
3637c8c0b82SPatrick Mooney 	}
3647c8c0b82SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
3657c8c0b82SPatrick Mooney 
3667c8c0b82SPatrick Mooney 	return (0);
3677c8c0b82SPatrick Mooney }
3687c8c0b82SPatrick Mooney 
3697c8c0b82SPatrick Mooney int
3707c8c0b82SPatrick Mooney vioapic_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
3717c8c0b82SPatrick Mooney     int size)
3727c8c0b82SPatrick Mooney {
3737c8c0b82SPatrick Mooney 	int error;
3747c8c0b82SPatrick Mooney 	struct vioapic *vioapic;
3757c8c0b82SPatrick Mooney 
3767c8c0b82SPatrick Mooney 	vioapic = vm_ioapic(vm);
3777c8c0b82SPatrick Mooney 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, rval, size, true);
3787c8c0b82SPatrick Mooney 	return (error);
3797c8c0b82SPatrick Mooney }
3807c8c0b82SPatrick Mooney 
3817c8c0b82SPatrick Mooney int
3827c8c0b82SPatrick Mooney vioapic_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t wval,
3837c8c0b82SPatrick Mooney     int size)
3847c8c0b82SPatrick Mooney {
3857c8c0b82SPatrick Mooney 	int error;
3867c8c0b82SPatrick Mooney 	struct vioapic *vioapic;
3877c8c0b82SPatrick Mooney 
3887c8c0b82SPatrick Mooney 	vioapic = vm_ioapic(vm);
3897c8c0b82SPatrick Mooney 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, &wval, size, false);
3907c8c0b82SPatrick Mooney 	return (error);
3917c8c0b82SPatrick Mooney }
3927c8c0b82SPatrick Mooney 
3937c8c0b82SPatrick Mooney void
3947c8c0b82SPatrick Mooney vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
3957c8c0b82SPatrick Mooney {
3967c8c0b82SPatrick Mooney 	struct vioapic *vioapic;
3977c8c0b82SPatrick Mooney 	int pin;
3987c8c0b82SPatrick Mooney 
3997c8c0b82SPatrick Mooney 	KASSERT(vector >= 0 && vector < 256,
4007c8c0b82SPatrick Mooney 	    ("vioapic_process_eoi: invalid vector %d", vector));
4017c8c0b82SPatrick Mooney 
4027c8c0b82SPatrick Mooney 	vioapic = vm_ioapic(vm);
4037c8c0b82SPatrick Mooney 
4047c8c0b82SPatrick Mooney 	/*
4057c8c0b82SPatrick Mooney 	 * XXX keep track of the pins associated with this vector instead
4067c8c0b82SPatrick Mooney 	 * of iterating on every single pin each time.
4077c8c0b82SPatrick Mooney 	 */
4087c8c0b82SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
4097c8c0b82SPatrick Mooney 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
4107c8c0b82SPatrick Mooney 		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
4117c8c0b82SPatrick Mooney 			continue;
4127c8c0b82SPatrick Mooney 		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
4137c8c0b82SPatrick Mooney 			continue;
4147c8c0b82SPatrick Mooney 		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
4157c8c0b82SPatrick Mooney 		if (vioapic->rtbl[pin].acnt > 0) {
416d4f59ae5SPatrick Mooney 			/* Pin asserted at EOI */
4177c8c0b82SPatrick Mooney 			vioapic_send_intr(vioapic, pin);
4187c8c0b82SPatrick Mooney 		}
4197c8c0b82SPatrick Mooney 	}
4207c8c0b82SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
4217c8c0b82SPatrick Mooney }
4227c8c0b82SPatrick Mooney 
4237c8c0b82SPatrick Mooney struct vioapic *
4247c8c0b82SPatrick Mooney vioapic_init(struct vm *vm)
4257c8c0b82SPatrick Mooney {
4267c8c0b82SPatrick Mooney 	int i;
4277c8c0b82SPatrick Mooney 	struct vioapic *vioapic;
4287c8c0b82SPatrick Mooney 
429*8130f8e1SPatrick Mooney 	vioapic = kmem_zalloc(sizeof (struct vioapic), KM_SLEEP);
4307c8c0b82SPatrick Mooney 
4317c8c0b82SPatrick Mooney 	vioapic->vm = vm;
4327c8c0b82SPatrick Mooney 	mutex_init(&vioapic->lock, NULL, MUTEX_ADAPTIVE, NULL);
4337c8c0b82SPatrick Mooney 
4347c8c0b82SPatrick Mooney 	/* Initialize all redirection entries to mask all interrupts */
4357c8c0b82SPatrick Mooney 	for (i = 0; i < REDIR_ENTRIES; i++)
4367c8c0b82SPatrick Mooney 		vioapic->rtbl[i].reg = 0x0001000000010000UL;
4377c8c0b82SPatrick Mooney 
4387c8c0b82SPatrick Mooney 	return (vioapic);
4397c8c0b82SPatrick Mooney }
4407c8c0b82SPatrick Mooney 
4417c8c0b82SPatrick Mooney void
4427c8c0b82SPatrick Mooney vioapic_cleanup(struct vioapic *vioapic)
4437c8c0b82SPatrick Mooney {
4447c8c0b82SPatrick Mooney 	mutex_destroy(&vioapic->lock);
445*8130f8e1SPatrick Mooney 	kmem_free(vioapic, sizeof (*vioapic));
4467c8c0b82SPatrick Mooney }
4477c8c0b82SPatrick Mooney 
4487c8c0b82SPatrick Mooney int
4497c8c0b82SPatrick Mooney vioapic_pincount(struct vm *vm)
4507c8c0b82SPatrick Mooney {
4517c8c0b82SPatrick Mooney 
4527c8c0b82SPatrick Mooney 	return (REDIR_ENTRIES);
4537c8c0b82SPatrick Mooney }
454