xref: /freebsd/sys/amd64/vmm/io/vioapic.c (revision e42c24d56b3d949aafd0c916e30ab91a4fe1e24d)
1565bbb86SNeel Natu /*-
2c49761ddSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3c49761ddSPedro F. Giffuni  *
4565bbb86SNeel Natu  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5565bbb86SNeel Natu  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6565bbb86SNeel Natu  * All rights reserved.
7565bbb86SNeel Natu  *
8565bbb86SNeel Natu  * Redistribution and use in source and binary forms, with or without
9565bbb86SNeel Natu  * modification, are permitted provided that the following conditions
10565bbb86SNeel Natu  * are met:
11565bbb86SNeel Natu  * 1. Redistributions of source code must retain the above copyright
12565bbb86SNeel Natu  *    notice, this list of conditions and the following disclaimer.
13565bbb86SNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
14565bbb86SNeel Natu  *    notice, this list of conditions and the following disclaimer in the
15565bbb86SNeel Natu  *    documentation and/or other materials provided with the distribution.
16565bbb86SNeel Natu  *
17565bbb86SNeel Natu  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18565bbb86SNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19565bbb86SNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20565bbb86SNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21565bbb86SNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22565bbb86SNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23565bbb86SNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24565bbb86SNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25565bbb86SNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26565bbb86SNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27565bbb86SNeel Natu  * SUCH DAMAGE.
28565bbb86SNeel Natu  *
29565bbb86SNeel Natu  * $FreeBSD$
30565bbb86SNeel Natu  */
31565bbb86SNeel Natu 
32565bbb86SNeel Natu #include <sys/cdefs.h>
33565bbb86SNeel Natu __FBSDID("$FreeBSD$");
34565bbb86SNeel Natu 
35483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h"
36483d953aSJohn Baldwin 
37565bbb86SNeel Natu #include <sys/param.h>
38565bbb86SNeel Natu #include <sys/queue.h>
39565bbb86SNeel Natu #include <sys/lock.h>
40565bbb86SNeel Natu #include <sys/mutex.h>
41565bbb86SNeel Natu #include <sys/systm.h>
42565bbb86SNeel Natu #include <sys/kernel.h>
43565bbb86SNeel Natu #include <sys/malloc.h>
44565bbb86SNeel Natu 
45565bbb86SNeel Natu #include <x86/apicreg.h>
46565bbb86SNeel Natu #include <machine/vmm.h>
47483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
48565bbb86SNeel Natu 
49565bbb86SNeel Natu #include "vmm_ktr.h"
50565bbb86SNeel Natu #include "vmm_lapic.h"
514f8be175SNeel Natu #include "vlapic.h"
52565bbb86SNeel Natu #include "vioapic.h"
53565bbb86SNeel Natu 
54565bbb86SNeel Natu #define	IOREGSEL	0x00
55565bbb86SNeel Natu #define	IOWIN		0x10
56565bbb86SNeel Natu 
574cefe96cSAlexander Motin #define	REDIR_ENTRIES	32
58b5b28fc9SNeel Natu #define	RTBL_RO_BITS	((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
59565bbb86SNeel Natu 
60565bbb86SNeel Natu struct vioapic {
61565bbb86SNeel Natu 	struct vm	*vm;
62565bbb86SNeel Natu 	struct mtx	mtx;
63565bbb86SNeel Natu 	uint32_t	id;
64565bbb86SNeel Natu 	uint32_t	ioregsel;
65565bbb86SNeel Natu 	struct {
66565bbb86SNeel Natu 		uint64_t reg;
67b5b28fc9SNeel Natu 		int	 acnt;	/* sum of pin asserts (+1) and deasserts (-1) */
68565bbb86SNeel Natu 	} rtbl[REDIR_ENTRIES];
69565bbb86SNeel Natu };
70565bbb86SNeel Natu 
719c43cd07SNeel Natu #define	VIOAPIC_LOCK(vioapic)		mtx_lock_spin(&((vioapic)->mtx))
729c43cd07SNeel Natu #define	VIOAPIC_UNLOCK(vioapic)		mtx_unlock_spin(&((vioapic)->mtx))
73565bbb86SNeel Natu #define	VIOAPIC_LOCKED(vioapic)		mtx_owned(&((vioapic)->mtx))
74565bbb86SNeel Natu 
75565bbb86SNeel Natu static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
76565bbb86SNeel Natu 
77565bbb86SNeel Natu #define	VIOAPIC_CTR1(vioapic, fmt, a1)					\
78565bbb86SNeel Natu 	VM_CTR1((vioapic)->vm, fmt, a1)
79565bbb86SNeel Natu 
80565bbb86SNeel Natu #define	VIOAPIC_CTR2(vioapic, fmt, a1, a2)				\
81565bbb86SNeel Natu 	VM_CTR2((vioapic)->vm, fmt, a1, a2)
82565bbb86SNeel Natu 
83565bbb86SNeel Natu #define	VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3)				\
84565bbb86SNeel Natu 	VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
85565bbb86SNeel Natu 
86b5b28fc9SNeel Natu #define	VIOAPIC_CTR4(vioapic, fmt, a1, a2, a3, a4)			\
87b5b28fc9SNeel Natu 	VM_CTR4((vioapic)->vm, fmt, a1, a2, a3, a4)
88b5b28fc9SNeel Natu 
89565bbb86SNeel Natu #ifdef KTR
90565bbb86SNeel Natu static const char *
91565bbb86SNeel Natu pinstate_str(bool asserted)
92565bbb86SNeel Natu {
93565bbb86SNeel Natu 
94565bbb86SNeel Natu 	if (asserted)
95565bbb86SNeel Natu 		return ("asserted");
96565bbb86SNeel Natu 	else
97565bbb86SNeel Natu 		return ("deasserted");
98565bbb86SNeel Natu }
99565bbb86SNeel Natu #endif
100565bbb86SNeel Natu 
101565bbb86SNeel Natu static void
102b5b28fc9SNeel Natu vioapic_send_intr(struct vioapic *vioapic, int pin)
103565bbb86SNeel Natu {
1044f8be175SNeel Natu 	int vector, delmode;
1054f8be175SNeel Natu 	uint32_t low, high, dest;
1064f8be175SNeel Natu 	bool level, phys;
107565bbb86SNeel Natu 
108565bbb86SNeel Natu 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
109565bbb86SNeel Natu 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
110565bbb86SNeel Natu 
111565bbb86SNeel Natu 	KASSERT(VIOAPIC_LOCKED(vioapic),
112565bbb86SNeel Natu 	    ("vioapic_set_pinstate: vioapic is not locked"));
113565bbb86SNeel Natu 
114565bbb86SNeel Natu 	low = vioapic->rtbl[pin].reg;
115565bbb86SNeel Natu 	high = vioapic->rtbl[pin].reg >> 32;
116b5b28fc9SNeel Natu 
117b5b28fc9SNeel Natu 	if ((low & IOART_INTMASK) == IOART_INTMSET) {
118b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
119b5b28fc9SNeel Natu 		return;
120b5b28fc9SNeel Natu 	}
121b5b28fc9SNeel Natu 
1224f8be175SNeel Natu 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
1234f8be175SNeel Natu 	delmode = low & IOART_DELMOD;
124b5b28fc9SNeel Natu 	level = low & IOART_TRGRLVL ? true : false;
1255ea87868SRoger Pau Monné 	if (level) {
1265ea87868SRoger Pau Monné 		if ((low & IOART_REM_IRR) != 0) {
1275ea87868SRoger Pau Monné 			VIOAPIC_CTR1(vioapic, "ioapic pin%d: irr pending",
1285ea87868SRoger Pau Monné 			    pin);
1295ea87868SRoger Pau Monné 			return;
1305ea87868SRoger Pau Monné 		}
131b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
1325ea87868SRoger Pau Monné 	}
133b5b28fc9SNeel Natu 
134565bbb86SNeel Natu 	vector = low & IOART_INTVEC;
1354f8be175SNeel Natu 	dest = high >> APIC_ID_SHIFT;
1364f8be175SNeel Natu 	vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
137565bbb86SNeel Natu }
138b5b28fc9SNeel Natu 
139b5b28fc9SNeel Natu static void
140b5b28fc9SNeel Natu vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
141b5b28fc9SNeel Natu {
142b5b28fc9SNeel Natu 	int oldcnt, newcnt;
143b5b28fc9SNeel Natu 	bool needintr;
144b5b28fc9SNeel Natu 
145b5b28fc9SNeel Natu 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
146b5b28fc9SNeel Natu 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
147b5b28fc9SNeel Natu 
148b5b28fc9SNeel Natu 	KASSERT(VIOAPIC_LOCKED(vioapic),
149b5b28fc9SNeel Natu 	    ("vioapic_set_pinstate: vioapic is not locked"));
150b5b28fc9SNeel Natu 
151b5b28fc9SNeel Natu 	oldcnt = vioapic->rtbl[pin].acnt;
152b5b28fc9SNeel Natu 	if (newstate)
153b5b28fc9SNeel Natu 		vioapic->rtbl[pin].acnt++;
154b5b28fc9SNeel Natu 	else
155b5b28fc9SNeel Natu 		vioapic->rtbl[pin].acnt--;
156b5b28fc9SNeel Natu 	newcnt = vioapic->rtbl[pin].acnt;
157b5b28fc9SNeel Natu 
158b5b28fc9SNeel Natu 	if (newcnt < 0) {
159b5b28fc9SNeel Natu 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d",
160b5b28fc9SNeel Natu 		    pin, newcnt);
161b5b28fc9SNeel Natu 	}
162b5b28fc9SNeel Natu 
163b5b28fc9SNeel Natu 	needintr = false;
164b5b28fc9SNeel Natu 	if (oldcnt == 0 && newcnt == 1) {
165b5b28fc9SNeel Natu 		needintr = true;
166b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin);
167b5b28fc9SNeel Natu 	} else if (oldcnt == 1 && newcnt == 0) {
168b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin);
169b5b28fc9SNeel Natu 	} else {
170b5b28fc9SNeel Natu 		VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d",
171b5b28fc9SNeel Natu 		    pin, pinstate_str(newstate), newcnt);
172b5b28fc9SNeel Natu 	}
173b5b28fc9SNeel Natu 
174b5b28fc9SNeel Natu 	if (needintr)
175b5b28fc9SNeel Natu 		vioapic_send_intr(vioapic, pin);
176565bbb86SNeel Natu }
177565bbb86SNeel Natu 
178ac7304a7SNeel Natu enum irqstate {
179ac7304a7SNeel Natu 	IRQSTATE_ASSERT,
180ac7304a7SNeel Natu 	IRQSTATE_DEASSERT,
181ac7304a7SNeel Natu 	IRQSTATE_PULSE
182ac7304a7SNeel Natu };
183ac7304a7SNeel Natu 
184565bbb86SNeel Natu static int
185ac7304a7SNeel Natu vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
186565bbb86SNeel Natu {
187565bbb86SNeel Natu 	struct vioapic *vioapic;
188565bbb86SNeel Natu 
189565bbb86SNeel Natu 	if (irq < 0 || irq >= REDIR_ENTRIES)
190565bbb86SNeel Natu 		return (EINVAL);
191565bbb86SNeel Natu 
192565bbb86SNeel Natu 	vioapic = vm_ioapic(vm);
193565bbb86SNeel Natu 
194565bbb86SNeel Natu 	VIOAPIC_LOCK(vioapic);
195ac7304a7SNeel Natu 	switch (irqstate) {
196ac7304a7SNeel Natu 	case IRQSTATE_ASSERT:
197ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, true);
198ac7304a7SNeel Natu 		break;
199ac7304a7SNeel Natu 	case IRQSTATE_DEASSERT:
200ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, false);
201ac7304a7SNeel Natu 		break;
202ac7304a7SNeel Natu 	case IRQSTATE_PULSE:
203ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, true);
204ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, false);
205ac7304a7SNeel Natu 		break;
206ac7304a7SNeel Natu 	default:
207ac7304a7SNeel Natu 		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
208ac7304a7SNeel Natu 	}
209565bbb86SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
210565bbb86SNeel Natu 
211565bbb86SNeel Natu 	return (0);
212565bbb86SNeel Natu }
213565bbb86SNeel Natu 
214565bbb86SNeel Natu int
215565bbb86SNeel Natu vioapic_assert_irq(struct vm *vm, int irq)
216565bbb86SNeel Natu {
217565bbb86SNeel Natu 
218ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
219565bbb86SNeel Natu }
220565bbb86SNeel Natu 
221565bbb86SNeel Natu int
222565bbb86SNeel Natu vioapic_deassert_irq(struct vm *vm, int irq)
223565bbb86SNeel Natu {
224565bbb86SNeel Natu 
225ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
226ac7304a7SNeel Natu }
227ac7304a7SNeel Natu 
228ac7304a7SNeel Natu int
229ac7304a7SNeel Natu vioapic_pulse_irq(struct vm *vm, int irq)
230ac7304a7SNeel Natu {
231ac7304a7SNeel Natu 
232ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
233565bbb86SNeel Natu }
234565bbb86SNeel Natu 
2355b8a8cd1SNeel Natu /*
2365b8a8cd1SNeel Natu  * Reset the vlapic's trigger-mode register to reflect the ioapic pin
2375b8a8cd1SNeel Natu  * configuration.
2385b8a8cd1SNeel Natu  */
2395b8a8cd1SNeel Natu static void
240d8be3d52SJohn Baldwin vioapic_update_tmr(struct vcpu *vcpu, void *arg)
2415b8a8cd1SNeel Natu {
2425b8a8cd1SNeel Natu 	struct vioapic *vioapic;
2435b8a8cd1SNeel Natu 	struct vlapic *vlapic;
2445b8a8cd1SNeel Natu 	uint32_t low, high, dest;
2455b8a8cd1SNeel Natu 	int delmode, pin, vector;
2465b8a8cd1SNeel Natu 	bool level, phys;
2475b8a8cd1SNeel Natu 
248d8be3d52SJohn Baldwin 	vlapic = vm_lapic(vcpu);
249d8be3d52SJohn Baldwin 	vioapic = vm_ioapic(vcpu_vm(vcpu));
2505b8a8cd1SNeel Natu 
2515b8a8cd1SNeel Natu 	VIOAPIC_LOCK(vioapic);
2525b8a8cd1SNeel Natu 	/*
2535b8a8cd1SNeel Natu 	 * Reset all vectors to be edge-triggered.
2545b8a8cd1SNeel Natu 	 */
2555b8a8cd1SNeel Natu 	vlapic_reset_tmr(vlapic);
2565b8a8cd1SNeel Natu 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
2575b8a8cd1SNeel Natu 		low = vioapic->rtbl[pin].reg;
2585b8a8cd1SNeel Natu 		high = vioapic->rtbl[pin].reg >> 32;
2595b8a8cd1SNeel Natu 
2605b8a8cd1SNeel Natu 		level = low & IOART_TRGRLVL ? true : false;
2615b8a8cd1SNeel Natu 		if (!level)
2625b8a8cd1SNeel Natu 			continue;
2635b8a8cd1SNeel Natu 
2645b8a8cd1SNeel Natu 		/*
2655b8a8cd1SNeel Natu 		 * For a level-triggered 'pin' let the vlapic figure out if
2665b8a8cd1SNeel Natu 		 * an assertion on this 'pin' would result in an interrupt
2675b8a8cd1SNeel Natu 		 * being delivered to it. If yes, then it will modify the
2685b8a8cd1SNeel Natu 		 * TMR bit associated with this vector to level-triggered.
2695b8a8cd1SNeel Natu 		 */
2705b8a8cd1SNeel Natu 		phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
2715b8a8cd1SNeel Natu 		delmode = low & IOART_DELMOD;
2725b8a8cd1SNeel Natu 		vector = low & IOART_INTVEC;
2735b8a8cd1SNeel Natu 		dest = high >> APIC_ID_SHIFT;
2745b8a8cd1SNeel Natu 		vlapic_set_tmr_level(vlapic, dest, phys, delmode, vector);
2755b8a8cd1SNeel Natu 	}
2765b8a8cd1SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
2775b8a8cd1SNeel Natu }
2785b8a8cd1SNeel Natu 
279565bbb86SNeel Natu static uint32_t
280d3956e46SJohn Baldwin vioapic_read(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr)
281565bbb86SNeel Natu {
282565bbb86SNeel Natu 	int regnum, pin, rshift;
283565bbb86SNeel Natu 
284565bbb86SNeel Natu 	regnum = addr & 0xff;
285565bbb86SNeel Natu 	switch (regnum) {
286565bbb86SNeel Natu 	case IOAPIC_ID:
287565bbb86SNeel Natu 		return (vioapic->id);
288565bbb86SNeel Natu 		break;
289565bbb86SNeel Natu 	case IOAPIC_VER:
290b5b28fc9SNeel Natu 		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
291565bbb86SNeel Natu 		break;
292565bbb86SNeel Natu 	case IOAPIC_ARB:
293565bbb86SNeel Natu 		return (vioapic->id);
294565bbb86SNeel Natu 		break;
295565bbb86SNeel Natu 	default:
296565bbb86SNeel Natu 		break;
297565bbb86SNeel Natu 	}
298565bbb86SNeel Natu 
299565bbb86SNeel Natu 	/* redirection table entries */
300565bbb86SNeel Natu 	if (regnum >= IOAPIC_REDTBL &&
301565bbb86SNeel Natu 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
302565bbb86SNeel Natu 		pin = (regnum - IOAPIC_REDTBL) / 2;
303565bbb86SNeel Natu 		if ((regnum - IOAPIC_REDTBL) % 2)
304565bbb86SNeel Natu 			rshift = 32;
305565bbb86SNeel Natu 		else
306565bbb86SNeel Natu 			rshift = 0;
307565bbb86SNeel Natu 
308565bbb86SNeel Natu 		return (vioapic->rtbl[pin].reg >> rshift);
309565bbb86SNeel Natu 	}
310565bbb86SNeel Natu 
311565bbb86SNeel Natu 	return (0);
312565bbb86SNeel Natu }
313565bbb86SNeel Natu 
314565bbb86SNeel Natu static void
315d3956e46SJohn Baldwin vioapic_write(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr,
316d3956e46SJohn Baldwin     uint32_t data)
317565bbb86SNeel Natu {
318b5b28fc9SNeel Natu 	uint64_t data64, mask64;
3195b8a8cd1SNeel Natu 	uint64_t last, changed;
320d8be3d52SJohn Baldwin 	int regnum, pin, lshift;
3215b8a8cd1SNeel Natu 	cpuset_t allvcpus;
322565bbb86SNeel Natu 
323565bbb86SNeel Natu 	regnum = addr & 0xff;
324565bbb86SNeel Natu 	switch (regnum) {
325565bbb86SNeel Natu 	case IOAPIC_ID:
326565bbb86SNeel Natu 		vioapic->id = data & APIC_ID_MASK;
327565bbb86SNeel Natu 		break;
328565bbb86SNeel Natu 	case IOAPIC_VER:
329565bbb86SNeel Natu 	case IOAPIC_ARB:
330565bbb86SNeel Natu 		/* readonly */
331565bbb86SNeel Natu 		break;
332565bbb86SNeel Natu 	default:
333565bbb86SNeel Natu 		break;
334565bbb86SNeel Natu 	}
335565bbb86SNeel Natu 
336565bbb86SNeel Natu 	/* redirection table entries */
337565bbb86SNeel Natu 	if (regnum >= IOAPIC_REDTBL &&
338565bbb86SNeel Natu 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
339565bbb86SNeel Natu 		pin = (regnum - IOAPIC_REDTBL) / 2;
340565bbb86SNeel Natu 		if ((regnum - IOAPIC_REDTBL) % 2)
341565bbb86SNeel Natu 			lshift = 32;
342565bbb86SNeel Natu 		else
343565bbb86SNeel Natu 			lshift = 0;
344565bbb86SNeel Natu 
3455b8a8cd1SNeel Natu 		last = vioapic->rtbl[pin].reg;
3465b8a8cd1SNeel Natu 
347b5b28fc9SNeel Natu 		data64 = (uint64_t)data << lshift;
348b5b28fc9SNeel Natu 		mask64 = (uint64_t)0xffffffff << lshift;
349b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
350b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
351565bbb86SNeel Natu 
3525ea87868SRoger Pau Monné 		/*
3535ea87868SRoger Pau Monné 		 * Switching from level to edge triggering will clear the IRR
3545ea87868SRoger Pau Monné 		 * bit. This is what FreeBSD will do in order to EOI an
3555ea87868SRoger Pau Monné 		 * interrupt when the IO-APIC doesn't support targeted EOI (see
3565ea87868SRoger Pau Monné 		 * _ioapic_eoi_source).
3575ea87868SRoger Pau Monné 		 */
3585ea87868SRoger Pau Monné 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
3595ea87868SRoger Pau Monné 		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
3605ea87868SRoger Pau Monné 			vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
3615ea87868SRoger Pau Monné 
362b5b28fc9SNeel Natu 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
363565bbb86SNeel Natu 		    pin, vioapic->rtbl[pin].reg);
364565bbb86SNeel Natu 
365565bbb86SNeel Natu 		/*
3665b8a8cd1SNeel Natu 		 * If any fields in the redirection table entry (except mask
3675b8a8cd1SNeel Natu 		 * or polarity) have changed then rendezvous all the vcpus
3685b8a8cd1SNeel Natu 		 * to update their vlapic trigger-mode registers.
3695b8a8cd1SNeel Natu 		 */
3705b8a8cd1SNeel Natu 		changed = last ^ vioapic->rtbl[pin].reg;
3715b8a8cd1SNeel Natu 		if (changed & ~(IOART_INTMASK | IOART_INTPOL)) {
3725b8a8cd1SNeel Natu 			VIOAPIC_CTR1(vioapic, "ioapic pin%d: recalculate "
3735b8a8cd1SNeel Natu 			    "vlapic trigger-mode register", pin);
3745b8a8cd1SNeel Natu 			VIOAPIC_UNLOCK(vioapic);
3755b8a8cd1SNeel Natu 			allvcpus = vm_active_cpus(vioapic->vm);
376d8be3d52SJohn Baldwin 			(void)vm_smp_rendezvous(vcpu, allvcpus,
3775b8a8cd1SNeel Natu 			    vioapic_update_tmr, NULL);
3785b8a8cd1SNeel Natu 			VIOAPIC_LOCK(vioapic);
3795b8a8cd1SNeel Natu 		}
3805b8a8cd1SNeel Natu 
3815b8a8cd1SNeel Natu 		/*
382b5b28fc9SNeel Natu 		 * Generate an interrupt if the following conditions are met:
383d7d06769SRoger Pau Monné 		 * - pin trigger mode is level
384b5b28fc9SNeel Natu 		 * - pin level is asserted
385565bbb86SNeel Natu 		 */
3865ea87868SRoger Pau Monné 		if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
387b5b28fc9SNeel Natu 		    (vioapic->rtbl[pin].acnt > 0)) {
388b5b28fc9SNeel Natu 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
389b5b28fc9SNeel Natu 			    "write, acnt %d", pin, vioapic->rtbl[pin].acnt);
390b5b28fc9SNeel Natu 			vioapic_send_intr(vioapic, pin);
391565bbb86SNeel Natu 		}
392565bbb86SNeel Natu 	}
393565bbb86SNeel Natu }
394565bbb86SNeel Natu 
395565bbb86SNeel Natu static int
396d3956e46SJohn Baldwin vioapic_mmio_rw(struct vioapic *vioapic, struct vcpu *vcpu, uint64_t gpa,
3975b8a8cd1SNeel Natu     uint64_t *data, int size, bool doread)
398565bbb86SNeel Natu {
399565bbb86SNeel Natu 	uint64_t offset;
400565bbb86SNeel Natu 
401565bbb86SNeel Natu 	offset = gpa - VIOAPIC_BASE;
402565bbb86SNeel Natu 
403565bbb86SNeel Natu 	/*
404565bbb86SNeel Natu 	 * The IOAPIC specification allows 32-bit wide accesses to the
405565bbb86SNeel Natu 	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
406565bbb86SNeel Natu 	 */
407565bbb86SNeel Natu 	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
408565bbb86SNeel Natu 		if (doread)
409565bbb86SNeel Natu 			*data = 0;
410565bbb86SNeel Natu 		return (0);
411565bbb86SNeel Natu 	}
412565bbb86SNeel Natu 
413565bbb86SNeel Natu 	VIOAPIC_LOCK(vioapic);
414565bbb86SNeel Natu 	if (offset == IOREGSEL) {
415565bbb86SNeel Natu 		if (doread)
416565bbb86SNeel Natu 			*data = vioapic->ioregsel;
417565bbb86SNeel Natu 		else
418565bbb86SNeel Natu 			vioapic->ioregsel = *data;
419565bbb86SNeel Natu 	} else {
4205b8a8cd1SNeel Natu 		if (doread) {
421d3956e46SJohn Baldwin 			*data = vioapic_read(vioapic, vcpu,
4225b8a8cd1SNeel Natu 			    vioapic->ioregsel);
4235b8a8cd1SNeel Natu 		} else {
424d3956e46SJohn Baldwin 			vioapic_write(vioapic, vcpu, vioapic->ioregsel,
4255b8a8cd1SNeel Natu 			    *data);
4265b8a8cd1SNeel Natu 		}
427565bbb86SNeel Natu 	}
428565bbb86SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
429565bbb86SNeel Natu 
430565bbb86SNeel Natu 	return (0);
431565bbb86SNeel Natu }
432565bbb86SNeel Natu 
433565bbb86SNeel Natu int
434d3956e46SJohn Baldwin vioapic_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval,
435565bbb86SNeel Natu     int size, void *arg)
436565bbb86SNeel Natu {
437565bbb86SNeel Natu 	int error;
438565bbb86SNeel Natu 	struct vioapic *vioapic;
439565bbb86SNeel Natu 
440d3956e46SJohn Baldwin 	vioapic = vm_ioapic(vcpu_vm(vcpu));
441d3956e46SJohn Baldwin 	error = vioapic_mmio_rw(vioapic, vcpu, gpa, rval, size, true);
442565bbb86SNeel Natu 	return (error);
443565bbb86SNeel Natu }
444565bbb86SNeel Natu 
445565bbb86SNeel Natu int
446d3956e46SJohn Baldwin vioapic_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t wval,
447565bbb86SNeel Natu     int size, void *arg)
448565bbb86SNeel Natu {
449565bbb86SNeel Natu 	int error;
450565bbb86SNeel Natu 	struct vioapic *vioapic;
451565bbb86SNeel Natu 
452d3956e46SJohn Baldwin 	vioapic = vm_ioapic(vcpu_vm(vcpu));
453d3956e46SJohn Baldwin 	error = vioapic_mmio_rw(vioapic, vcpu, gpa, &wval, size, false);
454565bbb86SNeel Natu 	return (error);
455565bbb86SNeel Natu }
456565bbb86SNeel Natu 
457b5b28fc9SNeel Natu void
458*e42c24d5SJohn Baldwin vioapic_process_eoi(struct vm *vm, int vector)
459b5b28fc9SNeel Natu {
460b5b28fc9SNeel Natu 	struct vioapic *vioapic;
461b5b28fc9SNeel Natu 	int pin;
462b5b28fc9SNeel Natu 
463b5b28fc9SNeel Natu 	KASSERT(vector >= 0 && vector < 256,
464b5b28fc9SNeel Natu 	    ("vioapic_process_eoi: invalid vector %d", vector));
465b5b28fc9SNeel Natu 
466b5b28fc9SNeel Natu 	vioapic = vm_ioapic(vm);
467b5b28fc9SNeel Natu 	VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector);
468b5b28fc9SNeel Natu 
469b5b28fc9SNeel Natu 	/*
470b5b28fc9SNeel Natu 	 * XXX keep track of the pins associated with this vector instead
471b5b28fc9SNeel Natu 	 * of iterating on every single pin each time.
472b5b28fc9SNeel Natu 	 */
473b5b28fc9SNeel Natu 	VIOAPIC_LOCK(vioapic);
474b5b28fc9SNeel Natu 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
475b5b28fc9SNeel Natu 		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
476b5b28fc9SNeel Natu 			continue;
477b5b28fc9SNeel Natu 		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
478b5b28fc9SNeel Natu 			continue;
479b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
480b5b28fc9SNeel Natu 		if (vioapic->rtbl[pin].acnt > 0) {
481b5b28fc9SNeel Natu 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, "
482b5b28fc9SNeel Natu 			    "acnt %d", pin, vioapic->rtbl[pin].acnt);
483b5b28fc9SNeel Natu 			vioapic_send_intr(vioapic, pin);
484b5b28fc9SNeel Natu 		}
485b5b28fc9SNeel Natu 	}
486b5b28fc9SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
487b5b28fc9SNeel Natu }
488b5b28fc9SNeel Natu 
489565bbb86SNeel Natu struct vioapic *
490565bbb86SNeel Natu vioapic_init(struct vm *vm)
491565bbb86SNeel Natu {
492565bbb86SNeel Natu 	int i;
493565bbb86SNeel Natu 	struct vioapic *vioapic;
494565bbb86SNeel Natu 
495565bbb86SNeel Natu 	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
496565bbb86SNeel Natu 
497565bbb86SNeel Natu 	vioapic->vm = vm;
4989c43cd07SNeel Natu 	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_SPIN);
499565bbb86SNeel Natu 
500565bbb86SNeel Natu 	/* Initialize all redirection entries to mask all interrupts */
501565bbb86SNeel Natu 	for (i = 0; i < REDIR_ENTRIES; i++)
502565bbb86SNeel Natu 		vioapic->rtbl[i].reg = 0x0001000000010000UL;
503565bbb86SNeel Natu 
504565bbb86SNeel Natu 	return (vioapic);
505565bbb86SNeel Natu }
506565bbb86SNeel Natu 
507565bbb86SNeel Natu void
508565bbb86SNeel Natu vioapic_cleanup(struct vioapic *vioapic)
509565bbb86SNeel Natu {
510565bbb86SNeel Natu 
511565bbb86SNeel Natu 	free(vioapic, M_VIOAPIC);
512565bbb86SNeel Natu }
513b5b28fc9SNeel Natu 
514b5b28fc9SNeel Natu int
515b5b28fc9SNeel Natu vioapic_pincount(struct vm *vm)
516b5b28fc9SNeel Natu {
517b5b28fc9SNeel Natu 
518b5b28fc9SNeel Natu 	return (REDIR_ENTRIES);
519b5b28fc9SNeel Natu }
520483d953aSJohn Baldwin 
521483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
522483d953aSJohn Baldwin int
523483d953aSJohn Baldwin vioapic_snapshot(struct vioapic *vioapic, struct vm_snapshot_meta *meta)
524483d953aSJohn Baldwin {
525483d953aSJohn Baldwin 	int ret;
526483d953aSJohn Baldwin 	int i;
527483d953aSJohn Baldwin 
528483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vioapic->ioregsel, meta, ret, done);
529483d953aSJohn Baldwin 
530483d953aSJohn Baldwin 	for (i = 0; i < nitems(vioapic->rtbl); i++) {
531483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].reg, meta, ret, done);
532483d953aSJohn Baldwin 		SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].acnt, meta, ret, done);
533483d953aSJohn Baldwin 	}
534483d953aSJohn Baldwin 
535483d953aSJohn Baldwin done:
536483d953aSJohn Baldwin 	return (ret);
537483d953aSJohn Baldwin }
538483d953aSJohn Baldwin #endif
539