xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vioapic.c (revision d515dd77)
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>
59*d515dd77SPatrick 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 }
455*d515dd77SPatrick Mooney 
456*d515dd77SPatrick Mooney static int
457*d515dd77SPatrick Mooney vioapic_data_read(void *datap, const vmm_data_req_t *req)
458*d515dd77SPatrick Mooney {
459*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
460*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 1);
461*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_len, ==, sizeof (struct vdi_ioapic_v1));
462*d515dd77SPatrick Mooney 
463*d515dd77SPatrick Mooney 	struct vioapic *vioapic = datap;
464*d515dd77SPatrick Mooney 	struct vdi_ioapic_v1 *out = req->vdr_data;
465*d515dd77SPatrick Mooney 
466*d515dd77SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
467*d515dd77SPatrick Mooney 	out->vi_id = vioapic->id;
468*d515dd77SPatrick Mooney 	out->vi_reg_sel = vioapic->ioregsel;
469*d515dd77SPatrick Mooney 	for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
470*d515dd77SPatrick Mooney 		out->vi_pin_reg[i] = vioapic->rtbl[i].reg;
471*d515dd77SPatrick Mooney 		out->vi_pin_level[i] = vioapic->rtbl[i].acnt;
472*d515dd77SPatrick Mooney 	}
473*d515dd77SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
474*d515dd77SPatrick Mooney 
475*d515dd77SPatrick Mooney 	return (0);
476*d515dd77SPatrick Mooney }
477*d515dd77SPatrick Mooney 
478*d515dd77SPatrick Mooney static int
479*d515dd77SPatrick Mooney vioapic_data_write(void *datap, const vmm_data_req_t *req)
480*d515dd77SPatrick Mooney {
481*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_IOAPIC);
482*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 1);
483*d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_len, ==, sizeof (struct vdi_ioapic_v1));
484*d515dd77SPatrick Mooney 
485*d515dd77SPatrick Mooney 	struct vioapic *vioapic = datap;
486*d515dd77SPatrick Mooney 	const struct vdi_ioapic_v1 *src = req->vdr_data;
487*d515dd77SPatrick Mooney 
488*d515dd77SPatrick Mooney 	VIOAPIC_LOCK(vioapic);
489*d515dd77SPatrick Mooney 	vioapic->id = src->vi_id;
490*d515dd77SPatrick Mooney 	vioapic->ioregsel = src->vi_reg_sel;
491*d515dd77SPatrick Mooney 	for (uint_t i = 0; i < REDIR_ENTRIES; i++) {
492*d515dd77SPatrick Mooney 		vioapic->rtbl[i].reg = src->vi_pin_reg[i] & ~RTBL_RO_BITS;
493*d515dd77SPatrick Mooney 		vioapic->rtbl[i].acnt = src->vi_pin_level[i];
494*d515dd77SPatrick Mooney 	}
495*d515dd77SPatrick Mooney 	VIOAPIC_UNLOCK(vioapic);
496*d515dd77SPatrick Mooney 
497*d515dd77SPatrick Mooney 	return (0);
498*d515dd77SPatrick Mooney }
499*d515dd77SPatrick Mooney 
500*d515dd77SPatrick Mooney static const vmm_data_version_entry_t ioapic_v1 = {
501*d515dd77SPatrick Mooney 	.vdve_class = VDC_IOAPIC,
502*d515dd77SPatrick Mooney 	.vdve_version = 1,
503*d515dd77SPatrick Mooney 	.vdve_len_expect = sizeof (struct vdi_ioapic_v1),
504*d515dd77SPatrick Mooney 	.vdve_readf = vioapic_data_read,
505*d515dd77SPatrick Mooney 	.vdve_writef = vioapic_data_write,
506*d515dd77SPatrick Mooney };
507*d515dd77SPatrick Mooney VMM_DATA_VERSION(ioapic_v1);
508