vioapic.c revision 258075
1/*-
2 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/amd64/vmm/io/vioapic.c 258075 2013-11-12 22:51:03Z neel $
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vioapic.c 258075 2013-11-12 22:51:03Z neel $");
32
33#include <sys/param.h>
34#include <sys/queue.h>
35#include <sys/cpuset.h>
36#include <sys/lock.h>
37#include <sys/mutex.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/malloc.h>
41
42#include <x86/apicreg.h>
43#include <machine/vmm.h>
44
45#include "vmm_ktr.h"
46#include "vmm_lapic.h"
47#include "vioapic.h"
48
49#define	IOREGSEL	0x00
50#define	IOWIN		0x10
51
52#define	REDIR_ENTRIES	16
53#define	INTR_ASSERTED(vioapic, pin) ((vioapic)->rtbl[(pin)].pinstate == true)
54
55struct vioapic {
56	struct vm	*vm;
57	struct mtx	mtx;
58	uint32_t	id;
59	uint32_t	ioregsel;
60	struct {
61		uint64_t reg;
62		bool     pinstate;
63		bool     pending;
64	} rtbl[REDIR_ENTRIES];
65};
66
67#define	VIOAPIC_LOCK(vioapic)		mtx_lock(&((vioapic)->mtx))
68#define	VIOAPIC_UNLOCK(vioapic)		mtx_unlock(&((vioapic)->mtx))
69#define	VIOAPIC_LOCKED(vioapic)		mtx_owned(&((vioapic)->mtx))
70
71static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
72
73#define	VIOAPIC_CTR1(vioapic, fmt, a1)					\
74	VM_CTR1((vioapic)->vm, fmt, a1)
75
76#define	VIOAPIC_CTR2(vioapic, fmt, a1, a2)				\
77	VM_CTR2((vioapic)->vm, fmt, a1, a2)
78
79#define	VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3)				\
80	VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
81
82#ifdef KTR
83static const char *
84pinstate_str(bool asserted)
85{
86
87	if (asserted)
88		return ("asserted");
89	else
90		return ("deasserted");
91}
92#endif
93
94static void
95vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
96{
97	int vector, apicid, vcpuid;
98	uint32_t low, high;
99	cpuset_t dmask;
100
101	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
102	    ("vioapic_set_pinstate: invalid pin number %d", pin));
103
104	KASSERT(VIOAPIC_LOCKED(vioapic),
105	    ("vioapic_set_pinstate: vioapic is not locked"));
106
107	VIOAPIC_CTR2(vioapic, "ioapic pin%d %s", pin, pinstate_str(newstate));
108
109	/* Nothing to do if interrupt pin has not changed state */
110	if (vioapic->rtbl[pin].pinstate == newstate)
111		return;
112
113	vioapic->rtbl[pin].pinstate = newstate;	/* record it */
114
115	/* Nothing to do if interrupt pin is deasserted */
116	if (!INTR_ASSERTED(vioapic, pin))
117		return;
118
119	/*
120	 * XXX
121	 * We only deal with:
122	 * - edge triggered interrupts
123	 * - fixed delivery mode
124	 *  Level-triggered sources will work so long as there is no sharing.
125	 */
126	low = vioapic->rtbl[pin].reg;
127	high = vioapic->rtbl[pin].reg >> 32;
128	if ((low & IOART_INTMASK) == IOART_INTMCLR &&
129	    (low & IOART_DESTMOD) == IOART_DESTPHY &&
130	    (low & IOART_DELMOD) == IOART_DELFIXED) {
131		vector = low & IOART_INTVEC;
132		apicid = high >> APIC_ID_SHIFT;
133		if (apicid != 0xff) {
134			/* unicast */
135			vcpuid = vm_apicid2vcpuid(vioapic->vm, apicid);
136			VIOAPIC_CTR3(vioapic, "ioapic pin%d triggering "
137			    "intr vector %d on vcpuid %d", pin, vector, vcpuid);
138			lapic_set_intr(vioapic->vm, vcpuid, vector);
139		} else {
140			/* broadcast */
141			VIOAPIC_CTR2(vioapic, "ioapic pin%d triggering intr "
142			    "vector %d on all vcpus", pin, vector);
143			dmask = vm_active_cpus(vioapic->vm);
144			while ((vcpuid = CPU_FFS(&dmask)) != 0) {
145				vcpuid--;
146				CPU_CLR(vcpuid, &dmask);
147				lapic_set_intr(vioapic->vm, vcpuid, vector);
148			}
149		}
150	} else if ((low & IOART_INTMASK) != IOART_INTMCLR &&
151		   (low & IOART_TRGRLVL) != 0) {
152		/*
153		 * For level-triggered interrupts that have been
154		 * masked, set the pending bit so that an interrupt
155		 * will be generated on unmask and if the level is
156		 * still asserted
157		 */
158		VIOAPIC_CTR1(vioapic, "ioapic pin%d interrupt pending", pin);
159		vioapic->rtbl[pin].pending = true;
160	}
161}
162
163static int
164vioapic_set_irqstate(struct vm *vm, int irq, bool state)
165{
166	struct vioapic *vioapic;
167
168	if (irq < 0 || irq >= REDIR_ENTRIES)
169		return (EINVAL);
170
171	vioapic = vm_ioapic(vm);
172
173	VIOAPIC_LOCK(vioapic);
174	vioapic_set_pinstate(vioapic, irq, state);
175	VIOAPIC_UNLOCK(vioapic);
176
177	return (0);
178}
179
180int
181vioapic_assert_irq(struct vm *vm, int irq)
182{
183
184	return (vioapic_set_irqstate(vm, irq, true));
185}
186
187int
188vioapic_deassert_irq(struct vm *vm, int irq)
189{
190
191	return (vioapic_set_irqstate(vm, irq, false));
192}
193
194static uint32_t
195vioapic_read(struct vioapic *vioapic, uint32_t addr)
196{
197	int regnum, pin, rshift;
198
199	regnum = addr & 0xff;
200	switch (regnum) {
201	case IOAPIC_ID:
202		return (vioapic->id);
203		break;
204	case IOAPIC_VER:
205		return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
206		break;
207	case IOAPIC_ARB:
208		return (vioapic->id);
209		break;
210	default:
211		break;
212	}
213
214	/* redirection table entries */
215	if (regnum >= IOAPIC_REDTBL &&
216	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
217		pin = (regnum - IOAPIC_REDTBL) / 2;
218		if ((regnum - IOAPIC_REDTBL) % 2)
219			rshift = 32;
220		else
221			rshift = 0;
222
223		return (vioapic->rtbl[pin].reg >> rshift);
224	}
225
226	return (0);
227}
228
229static void
230vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data)
231{
232	int regnum, pin, lshift;
233
234	regnum = addr & 0xff;
235	switch (regnum) {
236	case IOAPIC_ID:
237		vioapic->id = data & APIC_ID_MASK;
238		break;
239	case IOAPIC_VER:
240	case IOAPIC_ARB:
241		/* readonly */
242		break;
243	default:
244		break;
245	}
246
247	/* redirection table entries */
248	if (regnum >= IOAPIC_REDTBL &&
249	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
250		pin = (regnum - IOAPIC_REDTBL) / 2;
251		if ((regnum - IOAPIC_REDTBL) % 2)
252			lshift = 32;
253		else
254			lshift = 0;
255
256		vioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
257		vioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
258
259		VIOAPIC_CTR2(vioapic, "ioapic pin%d redir table entry %#lx",
260		    pin, vioapic->rtbl[pin].reg);
261
262		if (vioapic->rtbl[pin].pending &&
263		    ((vioapic->rtbl[pin].reg & IOART_INTMASK) ==
264		    IOART_INTMCLR)) {
265			vioapic->rtbl[pin].pending = false;
266			/*
267			 * Inject the deferred level-triggered int if it is
268			 * still asserted. Simulate by toggling the pin
269			 * off and then on.
270			 */
271			if (vioapic->rtbl[pin].pinstate == true) {
272				VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
273				    "interrupt delivered", pin);
274				vioapic_set_pinstate(vioapic, pin, false);
275				vioapic_set_pinstate(vioapic, pin, true);
276			} else {
277				VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
278				    "interrupt dismissed", pin);
279			}
280		}
281	}
282}
283
284static int
285vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data,
286    int size, bool doread)
287{
288	uint64_t offset;
289
290	offset = gpa - VIOAPIC_BASE;
291
292	/*
293	 * The IOAPIC specification allows 32-bit wide accesses to the
294	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
295	 */
296	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
297		if (doread)
298			*data = 0;
299		return (0);
300	}
301
302	VIOAPIC_LOCK(vioapic);
303	if (offset == IOREGSEL) {
304		if (doread)
305			*data = vioapic->ioregsel;
306		else
307			vioapic->ioregsel = *data;
308	} else {
309		if (doread)
310			*data = vioapic_read(vioapic, vioapic->ioregsel);
311		else
312			vioapic_write(vioapic, vioapic->ioregsel, *data);
313	}
314	VIOAPIC_UNLOCK(vioapic);
315
316	return (0);
317}
318
319int
320vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
321    int size, void *arg)
322{
323	int error;
324	struct vioapic *vioapic;
325
326	vioapic = vm_ioapic(vm);
327	error = vioapic_mmio_rw(vioapic, gpa, rval, size, true);
328	return (error);
329}
330
331int
332vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
333    int size, void *arg)
334{
335	int error;
336	struct vioapic *vioapic;
337
338	vioapic = vm_ioapic(vm);
339	error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false);
340	return (error);
341}
342
343struct vioapic *
344vioapic_init(struct vm *vm)
345{
346	int i;
347	struct vioapic *vioapic;
348
349	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
350
351	vioapic->vm = vm;
352	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF);
353
354	/* Initialize all redirection entries to mask all interrupts */
355	for (i = 0; i < REDIR_ENTRIES; i++)
356		vioapic->rtbl[i].reg = 0x0001000000010000UL;
357
358	return (vioapic);
359}
360
361void
362vioapic_cleanup(struct vioapic *vioapic)
363{
364
365	free(vioapic, M_VIOAPIC);
366}
367