vioapic.c revision 261088
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: stable/10/sys/amd64/vmm/io/vioapic.c 261088 2014-01-23 20:21:39Z jhb $
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vioapic.c 261088 2014-01-23 20:21:39Z jhb $");
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	24
53#define	RTBL_RO_BITS	((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
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		int	 acnt;	/* sum of pin asserts (+1) and deasserts (-1) */
63	} rtbl[REDIR_ENTRIES];
64};
65
66#define	VIOAPIC_LOCK(vioapic)		mtx_lock(&((vioapic)->mtx))
67#define	VIOAPIC_UNLOCK(vioapic)		mtx_unlock(&((vioapic)->mtx))
68#define	VIOAPIC_LOCKED(vioapic)		mtx_owned(&((vioapic)->mtx))
69
70static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
71
72#define	VIOAPIC_CTR1(vioapic, fmt, a1)					\
73	VM_CTR1((vioapic)->vm, fmt, a1)
74
75#define	VIOAPIC_CTR2(vioapic, fmt, a1, a2)				\
76	VM_CTR2((vioapic)->vm, fmt, a1, a2)
77
78#define	VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3)				\
79	VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
80
81#define	VIOAPIC_CTR4(vioapic, fmt, a1, a2, a3, a4)			\
82	VM_CTR4((vioapic)->vm, fmt, a1, a2, a3, a4)
83
84#ifdef KTR
85static const char *
86pinstate_str(bool asserted)
87{
88
89	if (asserted)
90		return ("asserted");
91	else
92		return ("deasserted");
93}
94
95static const char *
96trigger_str(bool level)
97{
98
99	if (level)
100		return ("level");
101	else
102		return ("edge");
103}
104#endif
105
106static void
107vioapic_send_intr(struct vioapic *vioapic, int pin)
108{
109	int vector, apicid, vcpuid;
110	uint32_t low, high;
111	cpuset_t dmask;
112	bool level;
113
114	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
115	    ("vioapic_set_pinstate: invalid pin number %d", pin));
116
117	KASSERT(VIOAPIC_LOCKED(vioapic),
118	    ("vioapic_set_pinstate: vioapic is not locked"));
119
120	low = vioapic->rtbl[pin].reg;
121	high = vioapic->rtbl[pin].reg >> 32;
122
123	/*
124	 * XXX We only deal with:
125	 * - physical destination
126	 * - fixed delivery mode
127	 */
128	if ((low & IOART_DESTMOD) != IOART_DESTPHY) {
129		VIOAPIC_CTR2(vioapic, "ioapic pin%d: unsupported dest mode "
130		    "0x%08x", pin, low);
131		return;
132	}
133
134	if ((low & IOART_DELMOD) != IOART_DELFIXED) {
135		VIOAPIC_CTR2(vioapic, "ioapic pin%d: unsupported delivery mode "
136		    "0x%08x", pin, low);
137		return;
138	}
139
140	if ((low & IOART_INTMASK) == IOART_INTMSET) {
141		VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
142		return;
143	}
144
145	level = low & IOART_TRGRLVL ? true : false;
146	if (level)
147		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
148
149	vector = low & IOART_INTVEC;
150	apicid = high >> APIC_ID_SHIFT;
151	if (apicid != 0xff) {
152		/* unicast */
153		vcpuid = vm_apicid2vcpuid(vioapic->vm, apicid);
154		VIOAPIC_CTR4(vioapic, "ioapic pin%d: %s triggered intr "
155		    "vector %d on vcpuid %d", pin, trigger_str(level),
156		    vector, vcpuid);
157		lapic_set_intr(vioapic->vm, vcpuid, vector, level);
158	} else {
159		/* broadcast */
160		VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s triggered intr "
161		    "vector %d on all vcpus", pin, trigger_str(level), vector);
162		dmask = vm_active_cpus(vioapic->vm);
163		while ((vcpuid = CPU_FFS(&dmask)) != 0) {
164			vcpuid--;
165			CPU_CLR(vcpuid, &dmask);
166			lapic_set_intr(vioapic->vm, vcpuid, vector, level);
167		}
168	}
169}
170
171static void
172vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
173{
174	int oldcnt, newcnt;
175	bool needintr;
176
177	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
178	    ("vioapic_set_pinstate: invalid pin number %d", pin));
179
180	KASSERT(VIOAPIC_LOCKED(vioapic),
181	    ("vioapic_set_pinstate: vioapic is not locked"));
182
183	oldcnt = vioapic->rtbl[pin].acnt;
184	if (newstate)
185		vioapic->rtbl[pin].acnt++;
186	else
187		vioapic->rtbl[pin].acnt--;
188	newcnt = vioapic->rtbl[pin].acnt;
189
190	if (newcnt < 0) {
191		VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d",
192		    pin, newcnt);
193	}
194
195	needintr = false;
196	if (oldcnt == 0 && newcnt == 1) {
197		needintr = true;
198		VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin);
199	} else if (oldcnt == 1 && newcnt == 0) {
200		VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin);
201	} else {
202		VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d",
203		    pin, pinstate_str(newstate), newcnt);
204	}
205
206	if (needintr)
207		vioapic_send_intr(vioapic, pin);
208}
209
210enum irqstate {
211	IRQSTATE_ASSERT,
212	IRQSTATE_DEASSERT,
213	IRQSTATE_PULSE
214};
215
216static int
217vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
218{
219	struct vioapic *vioapic;
220
221	if (irq < 0 || irq >= REDIR_ENTRIES)
222		return (EINVAL);
223
224	vioapic = vm_ioapic(vm);
225
226	VIOAPIC_LOCK(vioapic);
227	switch (irqstate) {
228	case IRQSTATE_ASSERT:
229		vioapic_set_pinstate(vioapic, irq, true);
230		break;
231	case IRQSTATE_DEASSERT:
232		vioapic_set_pinstate(vioapic, irq, false);
233		break;
234	case IRQSTATE_PULSE:
235		vioapic_set_pinstate(vioapic, irq, true);
236		vioapic_set_pinstate(vioapic, irq, false);
237		break;
238	default:
239		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
240	}
241	VIOAPIC_UNLOCK(vioapic);
242
243	return (0);
244}
245
246int
247vioapic_assert_irq(struct vm *vm, int irq)
248{
249
250	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
251}
252
253int
254vioapic_deassert_irq(struct vm *vm, int irq)
255{
256
257	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
258}
259
260int
261vioapic_pulse_irq(struct vm *vm, int irq)
262{
263
264	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
265}
266
267static uint32_t
268vioapic_read(struct vioapic *vioapic, uint32_t addr)
269{
270	int regnum, pin, rshift;
271
272	regnum = addr & 0xff;
273	switch (regnum) {
274	case IOAPIC_ID:
275		return (vioapic->id);
276		break;
277	case IOAPIC_VER:
278		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
279		break;
280	case IOAPIC_ARB:
281		return (vioapic->id);
282		break;
283	default:
284		break;
285	}
286
287	/* redirection table entries */
288	if (regnum >= IOAPIC_REDTBL &&
289	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
290		pin = (regnum - IOAPIC_REDTBL) / 2;
291		if ((regnum - IOAPIC_REDTBL) % 2)
292			rshift = 32;
293		else
294			rshift = 0;
295
296		return (vioapic->rtbl[pin].reg >> rshift);
297	}
298
299	return (0);
300}
301
302static void
303vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data)
304{
305	uint64_t data64, mask64;
306	int regnum, pin, lshift;
307
308	regnum = addr & 0xff;
309	switch (regnum) {
310	case IOAPIC_ID:
311		vioapic->id = data & APIC_ID_MASK;
312		break;
313	case IOAPIC_VER:
314	case IOAPIC_ARB:
315		/* readonly */
316		break;
317	default:
318		break;
319	}
320
321	/* redirection table entries */
322	if (regnum >= IOAPIC_REDTBL &&
323	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
324		pin = (regnum - IOAPIC_REDTBL) / 2;
325		if ((regnum - IOAPIC_REDTBL) % 2)
326			lshift = 32;
327		else
328			lshift = 0;
329
330		data64 = (uint64_t)data << lshift;
331		mask64 = (uint64_t)0xffffffff << lshift;
332		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
333		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
334
335		VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
336		    pin, vioapic->rtbl[pin].reg);
337
338		/*
339		 * Generate an interrupt if the following conditions are met:
340		 * - pin is not masked
341		 * - previous interrupt has been EOIed
342		 * - pin level is asserted
343		 */
344		if ((vioapic->rtbl[pin].reg & IOART_INTMASK) == IOART_INTMCLR &&
345		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0 &&
346		    (vioapic->rtbl[pin].acnt > 0)) {
347			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
348			    "write, acnt %d", pin, vioapic->rtbl[pin].acnt);
349			vioapic_send_intr(vioapic, pin);
350		}
351	}
352}
353
354static int
355vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data,
356    int size, bool doread)
357{
358	uint64_t offset;
359
360	offset = gpa - VIOAPIC_BASE;
361
362	/*
363	 * The IOAPIC specification allows 32-bit wide accesses to the
364	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
365	 */
366	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
367		if (doread)
368			*data = 0;
369		return (0);
370	}
371
372	VIOAPIC_LOCK(vioapic);
373	if (offset == IOREGSEL) {
374		if (doread)
375			*data = vioapic->ioregsel;
376		else
377			vioapic->ioregsel = *data;
378	} else {
379		if (doread)
380			*data = vioapic_read(vioapic, vioapic->ioregsel);
381		else
382			vioapic_write(vioapic, vioapic->ioregsel, *data);
383	}
384	VIOAPIC_UNLOCK(vioapic);
385
386	return (0);
387}
388
389int
390vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
391    int size, void *arg)
392{
393	int error;
394	struct vioapic *vioapic;
395
396	vioapic = vm_ioapic(vm);
397	error = vioapic_mmio_rw(vioapic, gpa, rval, size, true);
398	return (error);
399}
400
401int
402vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
403    int size, void *arg)
404{
405	int error;
406	struct vioapic *vioapic;
407
408	vioapic = vm_ioapic(vm);
409	error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false);
410	return (error);
411}
412
413void
414vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
415{
416	struct vioapic *vioapic;
417	int pin;
418
419	KASSERT(vector >= 0 && vector < 256,
420	    ("vioapic_process_eoi: invalid vector %d", vector));
421
422	vioapic = vm_ioapic(vm);
423	VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector);
424
425	/*
426	 * XXX keep track of the pins associated with this vector instead
427	 * of iterating on every single pin each time.
428	 */
429	VIOAPIC_LOCK(vioapic);
430	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
431		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
432			continue;
433		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
434			continue;
435		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
436		if (vioapic->rtbl[pin].acnt > 0) {
437			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, "
438			    "acnt %d", pin, vioapic->rtbl[pin].acnt);
439			vioapic_send_intr(vioapic, pin);
440		}
441	}
442	VIOAPIC_UNLOCK(vioapic);
443}
444
445struct vioapic *
446vioapic_init(struct vm *vm)
447{
448	int i;
449	struct vioapic *vioapic;
450
451	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
452
453	vioapic->vm = vm;
454	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF);
455
456	/* Initialize all redirection entries to mask all interrupts */
457	for (i = 0; i < REDIR_ENTRIES; i++)
458		vioapic->rtbl[i].reg = 0x0001000000010000UL;
459
460	return (vioapic);
461}
462
463void
464vioapic_cleanup(struct vioapic *vioapic)
465{
466
467	free(vioapic, M_VIOAPIC);
468}
469
470int
471vioapic_pincount(struct vm *vm)
472{
473
474	return (REDIR_ENTRIES);
475}
476