xref: /freebsd/sys/amd64/vmm/io/vioapic.c (revision 5b8a8cd1fee566ffd0e29b581779935704c98372)
1565bbb86SNeel Natu /*-
2565bbb86SNeel Natu  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3565bbb86SNeel Natu  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4565bbb86SNeel Natu  * All rights reserved.
5565bbb86SNeel Natu  *
6565bbb86SNeel Natu  * Redistribution and use in source and binary forms, with or without
7565bbb86SNeel Natu  * modification, are permitted provided that the following conditions
8565bbb86SNeel Natu  * are met:
9565bbb86SNeel Natu  * 1. Redistributions of source code must retain the above copyright
10565bbb86SNeel Natu  *    notice, this list of conditions and the following disclaimer.
11565bbb86SNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
12565bbb86SNeel Natu  *    notice, this list of conditions and the following disclaimer in the
13565bbb86SNeel Natu  *    documentation and/or other materials provided with the distribution.
14565bbb86SNeel Natu  *
15565bbb86SNeel Natu  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16565bbb86SNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17565bbb86SNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18565bbb86SNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19565bbb86SNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20565bbb86SNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21565bbb86SNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22565bbb86SNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23565bbb86SNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24565bbb86SNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25565bbb86SNeel Natu  * SUCH DAMAGE.
26565bbb86SNeel Natu  *
27565bbb86SNeel Natu  * $FreeBSD$
28565bbb86SNeel Natu  */
29565bbb86SNeel Natu 
30565bbb86SNeel Natu #include <sys/cdefs.h>
31565bbb86SNeel Natu __FBSDID("$FreeBSD$");
32565bbb86SNeel Natu 
33565bbb86SNeel Natu #include <sys/param.h>
34565bbb86SNeel Natu #include <sys/queue.h>
35565bbb86SNeel Natu #include <sys/cpuset.h>
36565bbb86SNeel Natu #include <sys/lock.h>
37565bbb86SNeel Natu #include <sys/mutex.h>
38565bbb86SNeel Natu #include <sys/systm.h>
39565bbb86SNeel Natu #include <sys/kernel.h>
40565bbb86SNeel Natu #include <sys/malloc.h>
41565bbb86SNeel Natu 
42565bbb86SNeel Natu #include <x86/apicreg.h>
43565bbb86SNeel Natu #include <machine/vmm.h>
44565bbb86SNeel Natu 
45565bbb86SNeel Natu #include "vmm_ktr.h"
46565bbb86SNeel Natu #include "vmm_lapic.h"
474f8be175SNeel Natu #include "vlapic.h"
48565bbb86SNeel Natu #include "vioapic.h"
49565bbb86SNeel Natu 
50565bbb86SNeel Natu #define	IOREGSEL	0x00
51565bbb86SNeel Natu #define	IOWIN		0x10
52565bbb86SNeel Natu 
53b5b28fc9SNeel Natu #define	REDIR_ENTRIES	24
54b5b28fc9SNeel Natu #define	RTBL_RO_BITS	((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
55565bbb86SNeel Natu 
56565bbb86SNeel Natu struct vioapic {
57565bbb86SNeel Natu 	struct vm	*vm;
58565bbb86SNeel Natu 	struct mtx	mtx;
59565bbb86SNeel Natu 	uint32_t	id;
60565bbb86SNeel Natu 	uint32_t	ioregsel;
61565bbb86SNeel Natu 	struct {
62565bbb86SNeel Natu 		uint64_t reg;
63b5b28fc9SNeel Natu 		int	 acnt;	/* sum of pin asserts (+1) and deasserts (-1) */
64565bbb86SNeel Natu 	} rtbl[REDIR_ENTRIES];
65565bbb86SNeel Natu };
66565bbb86SNeel Natu 
67565bbb86SNeel Natu #define	VIOAPIC_LOCK(vioapic)		mtx_lock(&((vioapic)->mtx))
68565bbb86SNeel Natu #define	VIOAPIC_UNLOCK(vioapic)		mtx_unlock(&((vioapic)->mtx))
69565bbb86SNeel Natu #define	VIOAPIC_LOCKED(vioapic)		mtx_owned(&((vioapic)->mtx))
70565bbb86SNeel Natu 
71565bbb86SNeel Natu static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
72565bbb86SNeel Natu 
73565bbb86SNeel Natu #define	VIOAPIC_CTR1(vioapic, fmt, a1)					\
74565bbb86SNeel Natu 	VM_CTR1((vioapic)->vm, fmt, a1)
75565bbb86SNeel Natu 
76565bbb86SNeel Natu #define	VIOAPIC_CTR2(vioapic, fmt, a1, a2)				\
77565bbb86SNeel Natu 	VM_CTR2((vioapic)->vm, fmt, a1, a2)
78565bbb86SNeel Natu 
79565bbb86SNeel Natu #define	VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3)				\
80565bbb86SNeel Natu 	VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
81565bbb86SNeel Natu 
82b5b28fc9SNeel Natu #define	VIOAPIC_CTR4(vioapic, fmt, a1, a2, a3, a4)			\
83b5b28fc9SNeel Natu 	VM_CTR4((vioapic)->vm, fmt, a1, a2, a3, a4)
84b5b28fc9SNeel Natu 
85565bbb86SNeel Natu #ifdef KTR
86565bbb86SNeel Natu static const char *
87565bbb86SNeel Natu pinstate_str(bool asserted)
88565bbb86SNeel Natu {
89565bbb86SNeel Natu 
90565bbb86SNeel Natu 	if (asserted)
91565bbb86SNeel Natu 		return ("asserted");
92565bbb86SNeel Natu 	else
93565bbb86SNeel Natu 		return ("deasserted");
94565bbb86SNeel Natu }
95565bbb86SNeel Natu #endif
96565bbb86SNeel Natu 
97565bbb86SNeel Natu static void
98b5b28fc9SNeel Natu vioapic_send_intr(struct vioapic *vioapic, int pin)
99565bbb86SNeel Natu {
1004f8be175SNeel Natu 	int vector, delmode;
1014f8be175SNeel Natu 	uint32_t low, high, dest;
1024f8be175SNeel Natu 	bool level, phys;
103565bbb86SNeel Natu 
104565bbb86SNeel Natu 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
105565bbb86SNeel Natu 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
106565bbb86SNeel Natu 
107565bbb86SNeel Natu 	KASSERT(VIOAPIC_LOCKED(vioapic),
108565bbb86SNeel Natu 	    ("vioapic_set_pinstate: vioapic is not locked"));
109565bbb86SNeel Natu 
110565bbb86SNeel Natu 	low = vioapic->rtbl[pin].reg;
111565bbb86SNeel Natu 	high = vioapic->rtbl[pin].reg >> 32;
112b5b28fc9SNeel Natu 
113b5b28fc9SNeel Natu 	if ((low & IOART_INTMASK) == IOART_INTMSET) {
114b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
115b5b28fc9SNeel Natu 		return;
116b5b28fc9SNeel Natu 	}
117b5b28fc9SNeel Natu 
1184f8be175SNeel Natu 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
1194f8be175SNeel Natu 	delmode = low & IOART_DELMOD;
120b5b28fc9SNeel Natu 	level = low & IOART_TRGRLVL ? true : false;
121b5b28fc9SNeel Natu 	if (level)
122b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
123b5b28fc9SNeel Natu 
124565bbb86SNeel Natu 	vector = low & IOART_INTVEC;
1254f8be175SNeel Natu 	dest = high >> APIC_ID_SHIFT;
1264f8be175SNeel Natu 	vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
127565bbb86SNeel Natu }
128b5b28fc9SNeel Natu 
129b5b28fc9SNeel Natu static void
130b5b28fc9SNeel Natu vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
131b5b28fc9SNeel Natu {
132b5b28fc9SNeel Natu 	int oldcnt, newcnt;
133b5b28fc9SNeel Natu 	bool needintr;
134b5b28fc9SNeel Natu 
135b5b28fc9SNeel Natu 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
136b5b28fc9SNeel Natu 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
137b5b28fc9SNeel Natu 
138b5b28fc9SNeel Natu 	KASSERT(VIOAPIC_LOCKED(vioapic),
139b5b28fc9SNeel Natu 	    ("vioapic_set_pinstate: vioapic is not locked"));
140b5b28fc9SNeel Natu 
141b5b28fc9SNeel Natu 	oldcnt = vioapic->rtbl[pin].acnt;
142b5b28fc9SNeel Natu 	if (newstate)
143b5b28fc9SNeel Natu 		vioapic->rtbl[pin].acnt++;
144b5b28fc9SNeel Natu 	else
145b5b28fc9SNeel Natu 		vioapic->rtbl[pin].acnt--;
146b5b28fc9SNeel Natu 	newcnt = vioapic->rtbl[pin].acnt;
147b5b28fc9SNeel Natu 
148b5b28fc9SNeel Natu 	if (newcnt < 0) {
149b5b28fc9SNeel Natu 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d",
150b5b28fc9SNeel Natu 		    pin, newcnt);
151b5b28fc9SNeel Natu 	}
152b5b28fc9SNeel Natu 
153b5b28fc9SNeel Natu 	needintr = false;
154b5b28fc9SNeel Natu 	if (oldcnt == 0 && newcnt == 1) {
155b5b28fc9SNeel Natu 		needintr = true;
156b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin);
157b5b28fc9SNeel Natu 	} else if (oldcnt == 1 && newcnt == 0) {
158b5b28fc9SNeel Natu 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin);
159b5b28fc9SNeel Natu 	} else {
160b5b28fc9SNeel Natu 		VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d",
161b5b28fc9SNeel Natu 		    pin, pinstate_str(newstate), newcnt);
162b5b28fc9SNeel Natu 	}
163b5b28fc9SNeel Natu 
164b5b28fc9SNeel Natu 	if (needintr)
165b5b28fc9SNeel Natu 		vioapic_send_intr(vioapic, pin);
166565bbb86SNeel Natu }
167565bbb86SNeel Natu 
168ac7304a7SNeel Natu enum irqstate {
169ac7304a7SNeel Natu 	IRQSTATE_ASSERT,
170ac7304a7SNeel Natu 	IRQSTATE_DEASSERT,
171ac7304a7SNeel Natu 	IRQSTATE_PULSE
172ac7304a7SNeel Natu };
173ac7304a7SNeel Natu 
174565bbb86SNeel Natu static int
175ac7304a7SNeel Natu vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
176565bbb86SNeel Natu {
177565bbb86SNeel Natu 	struct vioapic *vioapic;
178565bbb86SNeel Natu 
179565bbb86SNeel Natu 	if (irq < 0 || irq >= REDIR_ENTRIES)
180565bbb86SNeel Natu 		return (EINVAL);
181565bbb86SNeel Natu 
182565bbb86SNeel Natu 	vioapic = vm_ioapic(vm);
183565bbb86SNeel Natu 
184565bbb86SNeel Natu 	VIOAPIC_LOCK(vioapic);
185ac7304a7SNeel Natu 	switch (irqstate) {
186ac7304a7SNeel Natu 	case IRQSTATE_ASSERT:
187ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, true);
188ac7304a7SNeel Natu 		break;
189ac7304a7SNeel Natu 	case IRQSTATE_DEASSERT:
190ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, false);
191ac7304a7SNeel Natu 		break;
192ac7304a7SNeel Natu 	case IRQSTATE_PULSE:
193ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, true);
194ac7304a7SNeel Natu 		vioapic_set_pinstate(vioapic, irq, false);
195ac7304a7SNeel Natu 		break;
196ac7304a7SNeel Natu 	default:
197ac7304a7SNeel Natu 		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
198ac7304a7SNeel Natu 	}
199565bbb86SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
200565bbb86SNeel Natu 
201565bbb86SNeel Natu 	return (0);
202565bbb86SNeel Natu }
203565bbb86SNeel Natu 
204565bbb86SNeel Natu int
205565bbb86SNeel Natu vioapic_assert_irq(struct vm *vm, int irq)
206565bbb86SNeel Natu {
207565bbb86SNeel Natu 
208ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
209565bbb86SNeel Natu }
210565bbb86SNeel Natu 
211565bbb86SNeel Natu int
212565bbb86SNeel Natu vioapic_deassert_irq(struct vm *vm, int irq)
213565bbb86SNeel Natu {
214565bbb86SNeel Natu 
215ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
216ac7304a7SNeel Natu }
217ac7304a7SNeel Natu 
218ac7304a7SNeel Natu int
219ac7304a7SNeel Natu vioapic_pulse_irq(struct vm *vm, int irq)
220ac7304a7SNeel Natu {
221ac7304a7SNeel Natu 
222ac7304a7SNeel Natu 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
223565bbb86SNeel Natu }
224565bbb86SNeel Natu 
225*5b8a8cd1SNeel Natu /*
226*5b8a8cd1SNeel Natu  * Reset the vlapic's trigger-mode register to reflect the ioapic pin
227*5b8a8cd1SNeel Natu  * configuration.
228*5b8a8cd1SNeel Natu  */
229*5b8a8cd1SNeel Natu static void
230*5b8a8cd1SNeel Natu vioapic_update_tmr(struct vm *vm, int vcpuid, void *arg)
231*5b8a8cd1SNeel Natu {
232*5b8a8cd1SNeel Natu 	struct vioapic *vioapic;
233*5b8a8cd1SNeel Natu 	struct vlapic *vlapic;
234*5b8a8cd1SNeel Natu 	uint32_t low, high, dest;
235*5b8a8cd1SNeel Natu 	int delmode, pin, vector;
236*5b8a8cd1SNeel Natu 	bool level, phys;
237*5b8a8cd1SNeel Natu 
238*5b8a8cd1SNeel Natu 	vlapic = vm_lapic(vm, vcpuid);
239*5b8a8cd1SNeel Natu 	vioapic = vm_ioapic(vm);
240*5b8a8cd1SNeel Natu 
241*5b8a8cd1SNeel Natu 	VIOAPIC_LOCK(vioapic);
242*5b8a8cd1SNeel Natu 	/*
243*5b8a8cd1SNeel Natu 	 * Reset all vectors to be edge-triggered.
244*5b8a8cd1SNeel Natu 	 */
245*5b8a8cd1SNeel Natu 	vlapic_reset_tmr(vlapic);
246*5b8a8cd1SNeel Natu 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
247*5b8a8cd1SNeel Natu 		low = vioapic->rtbl[pin].reg;
248*5b8a8cd1SNeel Natu 		high = vioapic->rtbl[pin].reg >> 32;
249*5b8a8cd1SNeel Natu 
250*5b8a8cd1SNeel Natu 		level = low & IOART_TRGRLVL ? true : false;
251*5b8a8cd1SNeel Natu 		if (!level)
252*5b8a8cd1SNeel Natu 			continue;
253*5b8a8cd1SNeel Natu 
254*5b8a8cd1SNeel Natu 		/*
255*5b8a8cd1SNeel Natu 		 * For a level-triggered 'pin' let the vlapic figure out if
256*5b8a8cd1SNeel Natu 		 * an assertion on this 'pin' would result in an interrupt
257*5b8a8cd1SNeel Natu 		 * being delivered to it. If yes, then it will modify the
258*5b8a8cd1SNeel Natu 		 * TMR bit associated with this vector to level-triggered.
259*5b8a8cd1SNeel Natu 		 */
260*5b8a8cd1SNeel Natu 		phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
261*5b8a8cd1SNeel Natu 		delmode = low & IOART_DELMOD;
262*5b8a8cd1SNeel Natu 		vector = low & IOART_INTVEC;
263*5b8a8cd1SNeel Natu 		dest = high >> APIC_ID_SHIFT;
264*5b8a8cd1SNeel Natu 		vlapic_set_tmr_level(vlapic, dest, phys, delmode, vector);
265*5b8a8cd1SNeel Natu 	}
266*5b8a8cd1SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
267*5b8a8cd1SNeel Natu }
268*5b8a8cd1SNeel Natu 
269565bbb86SNeel Natu static uint32_t
270*5b8a8cd1SNeel Natu vioapic_read(struct vioapic *vioapic, int vcpuid, uint32_t addr)
271565bbb86SNeel Natu {
272565bbb86SNeel Natu 	int regnum, pin, rshift;
273565bbb86SNeel Natu 
274565bbb86SNeel Natu 	regnum = addr & 0xff;
275565bbb86SNeel Natu 	switch (regnum) {
276565bbb86SNeel Natu 	case IOAPIC_ID:
277565bbb86SNeel Natu 		return (vioapic->id);
278565bbb86SNeel Natu 		break;
279565bbb86SNeel Natu 	case IOAPIC_VER:
280b5b28fc9SNeel Natu 		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
281565bbb86SNeel Natu 		break;
282565bbb86SNeel Natu 	case IOAPIC_ARB:
283565bbb86SNeel Natu 		return (vioapic->id);
284565bbb86SNeel Natu 		break;
285565bbb86SNeel Natu 	default:
286565bbb86SNeel Natu 		break;
287565bbb86SNeel Natu 	}
288565bbb86SNeel Natu 
289565bbb86SNeel Natu 	/* redirection table entries */
290565bbb86SNeel Natu 	if (regnum >= IOAPIC_REDTBL &&
291565bbb86SNeel Natu 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
292565bbb86SNeel Natu 		pin = (regnum - IOAPIC_REDTBL) / 2;
293565bbb86SNeel Natu 		if ((regnum - IOAPIC_REDTBL) % 2)
294565bbb86SNeel Natu 			rshift = 32;
295565bbb86SNeel Natu 		else
296565bbb86SNeel Natu 			rshift = 0;
297565bbb86SNeel Natu 
298565bbb86SNeel Natu 		return (vioapic->rtbl[pin].reg >> rshift);
299565bbb86SNeel Natu 	}
300565bbb86SNeel Natu 
301565bbb86SNeel Natu 	return (0);
302565bbb86SNeel Natu }
303565bbb86SNeel Natu 
304565bbb86SNeel Natu static void
305*5b8a8cd1SNeel Natu vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
306565bbb86SNeel Natu {
307b5b28fc9SNeel Natu 	uint64_t data64, mask64;
308*5b8a8cd1SNeel Natu 	uint64_t last, changed;
309565bbb86SNeel Natu 	int regnum, pin, lshift;
310*5b8a8cd1SNeel Natu 	cpuset_t allvcpus;
311565bbb86SNeel Natu 
312565bbb86SNeel Natu 	regnum = addr & 0xff;
313565bbb86SNeel Natu 	switch (regnum) {
314565bbb86SNeel Natu 	case IOAPIC_ID:
315565bbb86SNeel Natu 		vioapic->id = data & APIC_ID_MASK;
316565bbb86SNeel Natu 		break;
317565bbb86SNeel Natu 	case IOAPIC_VER:
318565bbb86SNeel Natu 	case IOAPIC_ARB:
319565bbb86SNeel Natu 		/* readonly */
320565bbb86SNeel Natu 		break;
321565bbb86SNeel Natu 	default:
322565bbb86SNeel Natu 		break;
323565bbb86SNeel Natu 	}
324565bbb86SNeel Natu 
325565bbb86SNeel Natu 	/* redirection table entries */
326565bbb86SNeel Natu 	if (regnum >= IOAPIC_REDTBL &&
327565bbb86SNeel Natu 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
328565bbb86SNeel Natu 		pin = (regnum - IOAPIC_REDTBL) / 2;
329565bbb86SNeel Natu 		if ((regnum - IOAPIC_REDTBL) % 2)
330565bbb86SNeel Natu 			lshift = 32;
331565bbb86SNeel Natu 		else
332565bbb86SNeel Natu 			lshift = 0;
333565bbb86SNeel Natu 
334*5b8a8cd1SNeel Natu 		last = vioapic->rtbl[pin].reg;
335*5b8a8cd1SNeel Natu 
336b5b28fc9SNeel Natu 		data64 = (uint64_t)data << lshift;
337b5b28fc9SNeel Natu 		mask64 = (uint64_t)0xffffffff << lshift;
338b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
339b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
340565bbb86SNeel Natu 
341b5b28fc9SNeel Natu 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
342565bbb86SNeel Natu 		    pin, vioapic->rtbl[pin].reg);
343565bbb86SNeel Natu 
344565bbb86SNeel Natu 		/*
345*5b8a8cd1SNeel Natu 		 * If any fields in the redirection table entry (except mask
346*5b8a8cd1SNeel Natu 		 * or polarity) have changed then rendezvous all the vcpus
347*5b8a8cd1SNeel Natu 		 * to update their vlapic trigger-mode registers.
348*5b8a8cd1SNeel Natu 		 */
349*5b8a8cd1SNeel Natu 		changed = last ^ vioapic->rtbl[pin].reg;
350*5b8a8cd1SNeel Natu 		if (changed & ~(IOART_INTMASK | IOART_INTPOL)) {
351*5b8a8cd1SNeel Natu 			VIOAPIC_CTR1(vioapic, "ioapic pin%d: recalculate "
352*5b8a8cd1SNeel Natu 			    "vlapic trigger-mode register", pin);
353*5b8a8cd1SNeel Natu 			VIOAPIC_UNLOCK(vioapic);
354*5b8a8cd1SNeel Natu 			allvcpus = vm_active_cpus(vioapic->vm);
355*5b8a8cd1SNeel Natu 			vm_smp_rendezvous(vioapic->vm, vcpuid, allvcpus,
356*5b8a8cd1SNeel Natu 			    vioapic_update_tmr, NULL);
357*5b8a8cd1SNeel Natu 			VIOAPIC_LOCK(vioapic);
358*5b8a8cd1SNeel Natu 		}
359*5b8a8cd1SNeel Natu 
360*5b8a8cd1SNeel Natu 		/*
361b5b28fc9SNeel Natu 		 * Generate an interrupt if the following conditions are met:
362b5b28fc9SNeel Natu 		 * - pin is not masked
363b5b28fc9SNeel Natu 		 * - previous interrupt has been EOIed
364b5b28fc9SNeel Natu 		 * - pin level is asserted
365565bbb86SNeel Natu 		 */
366b5b28fc9SNeel Natu 		if ((vioapic->rtbl[pin].reg & IOART_INTMASK) == IOART_INTMCLR &&
367b5b28fc9SNeel Natu 		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0 &&
368b5b28fc9SNeel Natu 		    (vioapic->rtbl[pin].acnt > 0)) {
369b5b28fc9SNeel Natu 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
370b5b28fc9SNeel Natu 			    "write, acnt %d", pin, vioapic->rtbl[pin].acnt);
371b5b28fc9SNeel Natu 			vioapic_send_intr(vioapic, pin);
372565bbb86SNeel Natu 		}
373565bbb86SNeel Natu 	}
374565bbb86SNeel Natu }
375565bbb86SNeel Natu 
376565bbb86SNeel Natu static int
377*5b8a8cd1SNeel Natu vioapic_mmio_rw(struct vioapic *vioapic, int vcpuid, uint64_t gpa,
378*5b8a8cd1SNeel Natu     uint64_t *data, int size, bool doread)
379565bbb86SNeel Natu {
380565bbb86SNeel Natu 	uint64_t offset;
381565bbb86SNeel Natu 
382565bbb86SNeel Natu 	offset = gpa - VIOAPIC_BASE;
383565bbb86SNeel Natu 
384565bbb86SNeel Natu 	/*
385565bbb86SNeel Natu 	 * The IOAPIC specification allows 32-bit wide accesses to the
386565bbb86SNeel Natu 	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
387565bbb86SNeel Natu 	 */
388565bbb86SNeel Natu 	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
389565bbb86SNeel Natu 		if (doread)
390565bbb86SNeel Natu 			*data = 0;
391565bbb86SNeel Natu 		return (0);
392565bbb86SNeel Natu 	}
393565bbb86SNeel Natu 
394565bbb86SNeel Natu 	VIOAPIC_LOCK(vioapic);
395565bbb86SNeel Natu 	if (offset == IOREGSEL) {
396565bbb86SNeel Natu 		if (doread)
397565bbb86SNeel Natu 			*data = vioapic->ioregsel;
398565bbb86SNeel Natu 		else
399565bbb86SNeel Natu 			vioapic->ioregsel = *data;
400565bbb86SNeel Natu 	} else {
401*5b8a8cd1SNeel Natu 		if (doread) {
402*5b8a8cd1SNeel Natu 			*data = vioapic_read(vioapic, vcpuid,
403*5b8a8cd1SNeel Natu 			    vioapic->ioregsel);
404*5b8a8cd1SNeel Natu 		} else {
405*5b8a8cd1SNeel Natu 			vioapic_write(vioapic, vcpuid, vioapic->ioregsel,
406*5b8a8cd1SNeel Natu 			    *data);
407*5b8a8cd1SNeel Natu 		}
408565bbb86SNeel Natu 	}
409565bbb86SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
410565bbb86SNeel Natu 
411565bbb86SNeel Natu 	return (0);
412565bbb86SNeel Natu }
413565bbb86SNeel Natu 
414565bbb86SNeel Natu int
415565bbb86SNeel Natu vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
416565bbb86SNeel Natu     int size, void *arg)
417565bbb86SNeel Natu {
418565bbb86SNeel Natu 	int error;
419565bbb86SNeel Natu 	struct vioapic *vioapic;
420565bbb86SNeel Natu 
421565bbb86SNeel Natu 	vioapic = vm_ioapic(vm);
422*5b8a8cd1SNeel Natu 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, rval, size, true);
423565bbb86SNeel Natu 	return (error);
424565bbb86SNeel Natu }
425565bbb86SNeel Natu 
426565bbb86SNeel Natu int
427565bbb86SNeel Natu vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
428565bbb86SNeel Natu     int size, void *arg)
429565bbb86SNeel Natu {
430565bbb86SNeel Natu 	int error;
431565bbb86SNeel Natu 	struct vioapic *vioapic;
432565bbb86SNeel Natu 
433565bbb86SNeel Natu 	vioapic = vm_ioapic(vm);
434*5b8a8cd1SNeel Natu 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, &wval, size, false);
435565bbb86SNeel Natu 	return (error);
436565bbb86SNeel Natu }
437565bbb86SNeel Natu 
438b5b28fc9SNeel Natu void
439b5b28fc9SNeel Natu vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
440b5b28fc9SNeel Natu {
441b5b28fc9SNeel Natu 	struct vioapic *vioapic;
442b5b28fc9SNeel Natu 	int pin;
443b5b28fc9SNeel Natu 
444b5b28fc9SNeel Natu 	KASSERT(vector >= 0 && vector < 256,
445b5b28fc9SNeel Natu 	    ("vioapic_process_eoi: invalid vector %d", vector));
446b5b28fc9SNeel Natu 
447b5b28fc9SNeel Natu 	vioapic = vm_ioapic(vm);
448b5b28fc9SNeel Natu 	VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector);
449b5b28fc9SNeel Natu 
450b5b28fc9SNeel Natu 	/*
451b5b28fc9SNeel Natu 	 * XXX keep track of the pins associated with this vector instead
452b5b28fc9SNeel Natu 	 * of iterating on every single pin each time.
453b5b28fc9SNeel Natu 	 */
454b5b28fc9SNeel Natu 	VIOAPIC_LOCK(vioapic);
455b5b28fc9SNeel Natu 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
456b5b28fc9SNeel Natu 		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
457b5b28fc9SNeel Natu 			continue;
458b5b28fc9SNeel Natu 		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
459b5b28fc9SNeel Natu 			continue;
460b5b28fc9SNeel Natu 		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
461b5b28fc9SNeel Natu 		if (vioapic->rtbl[pin].acnt > 0) {
462b5b28fc9SNeel Natu 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, "
463b5b28fc9SNeel Natu 			    "acnt %d", pin, vioapic->rtbl[pin].acnt);
464b5b28fc9SNeel Natu 			vioapic_send_intr(vioapic, pin);
465b5b28fc9SNeel Natu 		}
466b5b28fc9SNeel Natu 	}
467b5b28fc9SNeel Natu 	VIOAPIC_UNLOCK(vioapic);
468b5b28fc9SNeel Natu }
469b5b28fc9SNeel Natu 
470565bbb86SNeel Natu struct vioapic *
471565bbb86SNeel Natu vioapic_init(struct vm *vm)
472565bbb86SNeel Natu {
473565bbb86SNeel Natu 	int i;
474565bbb86SNeel Natu 	struct vioapic *vioapic;
475565bbb86SNeel Natu 
476565bbb86SNeel Natu 	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
477565bbb86SNeel Natu 
478565bbb86SNeel Natu 	vioapic->vm = vm;
479565bbb86SNeel Natu 	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF);
480565bbb86SNeel Natu 
481565bbb86SNeel Natu 	/* Initialize all redirection entries to mask all interrupts */
482565bbb86SNeel Natu 	for (i = 0; i < REDIR_ENTRIES; i++)
483565bbb86SNeel Natu 		vioapic->rtbl[i].reg = 0x0001000000010000UL;
484565bbb86SNeel Natu 
485565bbb86SNeel Natu 	return (vioapic);
486565bbb86SNeel Natu }
487565bbb86SNeel Natu 
488565bbb86SNeel Natu void
489565bbb86SNeel Natu vioapic_cleanup(struct vioapic *vioapic)
490565bbb86SNeel Natu {
491565bbb86SNeel Natu 
492565bbb86SNeel Natu 	free(vioapic, M_VIOAPIC);
493565bbb86SNeel Natu }
494b5b28fc9SNeel Natu 
495b5b28fc9SNeel Natu int
496b5b28fc9SNeel Natu vioapic_pincount(struct vm *vm)
497b5b28fc9SNeel Natu {
498b5b28fc9SNeel Natu 
499b5b28fc9SNeel Natu 	return (REDIR_ENTRIES);
500b5b28fc9SNeel Natu }
501