vioapic.c revision 258494
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 258494 2013-11-23 03:56:03Z neel $
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vioapic.c 258494 2013-11-23 03:56: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
163enum irqstate {
164	IRQSTATE_ASSERT,
165	IRQSTATE_DEASSERT,
166	IRQSTATE_PULSE
167};
168
169static int
170vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
171{
172	struct vioapic *vioapic;
173
174	if (irq < 0 || irq >= REDIR_ENTRIES)
175		return (EINVAL);
176
177	vioapic = vm_ioapic(vm);
178
179	VIOAPIC_LOCK(vioapic);
180	switch (irqstate) {
181	case IRQSTATE_ASSERT:
182		vioapic_set_pinstate(vioapic, irq, true);
183		break;
184	case IRQSTATE_DEASSERT:
185		vioapic_set_pinstate(vioapic, irq, false);
186		break;
187	case IRQSTATE_PULSE:
188		vioapic_set_pinstate(vioapic, irq, true);
189		vioapic_set_pinstate(vioapic, irq, false);
190		break;
191	default:
192		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
193	}
194	VIOAPIC_UNLOCK(vioapic);
195
196	return (0);
197}
198
199int
200vioapic_assert_irq(struct vm *vm, int irq)
201{
202
203	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
204}
205
206int
207vioapic_deassert_irq(struct vm *vm, int irq)
208{
209
210	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
211}
212
213int
214vioapic_pulse_irq(struct vm *vm, int irq)
215{
216
217	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
218}
219
220static uint32_t
221vioapic_read(struct vioapic *vioapic, uint32_t addr)
222{
223	int regnum, pin, rshift;
224
225	regnum = addr & 0xff;
226	switch (regnum) {
227	case IOAPIC_ID:
228		return (vioapic->id);
229		break;
230	case IOAPIC_VER:
231		return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
232		break;
233	case IOAPIC_ARB:
234		return (vioapic->id);
235		break;
236	default:
237		break;
238	}
239
240	/* redirection table entries */
241	if (regnum >= IOAPIC_REDTBL &&
242	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
243		pin = (regnum - IOAPIC_REDTBL) / 2;
244		if ((regnum - IOAPIC_REDTBL) % 2)
245			rshift = 32;
246		else
247			rshift = 0;
248
249		return (vioapic->rtbl[pin].reg >> rshift);
250	}
251
252	return (0);
253}
254
255static void
256vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data)
257{
258	int regnum, pin, lshift;
259
260	regnum = addr & 0xff;
261	switch (regnum) {
262	case IOAPIC_ID:
263		vioapic->id = data & APIC_ID_MASK;
264		break;
265	case IOAPIC_VER:
266	case IOAPIC_ARB:
267		/* readonly */
268		break;
269	default:
270		break;
271	}
272
273	/* redirection table entries */
274	if (regnum >= IOAPIC_REDTBL &&
275	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
276		pin = (regnum - IOAPIC_REDTBL) / 2;
277		if ((regnum - IOAPIC_REDTBL) % 2)
278			lshift = 32;
279		else
280			lshift = 0;
281
282		vioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
283		vioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
284
285		VIOAPIC_CTR2(vioapic, "ioapic pin%d redir table entry %#lx",
286		    pin, vioapic->rtbl[pin].reg);
287
288		if (vioapic->rtbl[pin].pending &&
289		    ((vioapic->rtbl[pin].reg & IOART_INTMASK) ==
290		    IOART_INTMCLR)) {
291			vioapic->rtbl[pin].pending = false;
292			/*
293			 * Inject the deferred level-triggered int if it is
294			 * still asserted. Simulate by toggling the pin
295			 * off and then on.
296			 */
297			if (vioapic->rtbl[pin].pinstate == true) {
298				VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
299				    "interrupt delivered", pin);
300				vioapic_set_pinstate(vioapic, pin, false);
301				vioapic_set_pinstate(vioapic, pin, true);
302			} else {
303				VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
304				    "interrupt dismissed", pin);
305			}
306		}
307	}
308}
309
310static int
311vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data,
312    int size, bool doread)
313{
314	uint64_t offset;
315
316	offset = gpa - VIOAPIC_BASE;
317
318	/*
319	 * The IOAPIC specification allows 32-bit wide accesses to the
320	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
321	 */
322	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
323		if (doread)
324			*data = 0;
325		return (0);
326	}
327
328	VIOAPIC_LOCK(vioapic);
329	if (offset == IOREGSEL) {
330		if (doread)
331			*data = vioapic->ioregsel;
332		else
333			vioapic->ioregsel = *data;
334	} else {
335		if (doread)
336			*data = vioapic_read(vioapic, vioapic->ioregsel);
337		else
338			vioapic_write(vioapic, vioapic->ioregsel, *data);
339	}
340	VIOAPIC_UNLOCK(vioapic);
341
342	return (0);
343}
344
345int
346vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
347    int size, void *arg)
348{
349	int error;
350	struct vioapic *vioapic;
351
352	vioapic = vm_ioapic(vm);
353	error = vioapic_mmio_rw(vioapic, gpa, rval, size, true);
354	return (error);
355}
356
357int
358vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
359    int size, void *arg)
360{
361	int error;
362	struct vioapic *vioapic;
363
364	vioapic = vm_ioapic(vm);
365	error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false);
366	return (error);
367}
368
369struct vioapic *
370vioapic_init(struct vm *vm)
371{
372	int i;
373	struct vioapic *vioapic;
374
375	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
376
377	vioapic->vm = vm;
378	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF);
379
380	/* Initialize all redirection entries to mask all interrupts */
381	for (i = 0; i < REDIR_ENTRIES; i++)
382		vioapic->rtbl[i].reg = 0x0001000000010000UL;
383
384	return (vioapic);
385}
386
387void
388vioapic_cleanup(struct vioapic *vioapic)
389{
390
391	free(vioapic, M_VIOAPIC);
392}
393