xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vioapic.c (revision 32640292)
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