xref: /freebsd/sys/amd64/vmm/io/vioapic.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
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$
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 
41 #include <x86/apicreg.h>
42 #include <machine/vmm.h>
43 
44 #include "vmm_ktr.h"
45 #include "vmm_lapic.h"
46 #include "vlapic.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 
55 struct 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_spin(&((vioapic)->mtx))
67 #define	VIOAPIC_UNLOCK(vioapic)		mtx_unlock_spin(&((vioapic)->mtx))
68 #define	VIOAPIC_LOCKED(vioapic)		mtx_owned(&((vioapic)->mtx))
69 
70 static 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
85 static const char *
86 pinstate_str(bool asserted)
87 {
88 
89 	if (asserted)
90 		return ("asserted");
91 	else
92 		return ("deasserted");
93 }
94 #endif
95 
96 static void
97 vioapic_send_intr(struct vioapic *vioapic, int pin)
98 {
99 	int vector, delmode;
100 	uint32_t low, high, dest;
101 	bool level, phys;
102 
103 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
104 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
105 
106 	KASSERT(VIOAPIC_LOCKED(vioapic),
107 	    ("vioapic_set_pinstate: vioapic is not locked"));
108 
109 	low = vioapic->rtbl[pin].reg;
110 	high = vioapic->rtbl[pin].reg >> 32;
111 
112 	if ((low & IOART_INTMASK) == IOART_INTMSET) {
113 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
114 		return;
115 	}
116 
117 	phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
118 	delmode = low & IOART_DELMOD;
119 	level = low & IOART_TRGRLVL ? true : false;
120 	if (level)
121 		vioapic->rtbl[pin].reg |= IOART_REM_IRR;
122 
123 	vector = low & IOART_INTVEC;
124 	dest = high >> APIC_ID_SHIFT;
125 	vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
126 }
127 
128 static void
129 vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
130 {
131 	int oldcnt, newcnt;
132 	bool needintr;
133 
134 	KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
135 	    ("vioapic_set_pinstate: invalid pin number %d", pin));
136 
137 	KASSERT(VIOAPIC_LOCKED(vioapic),
138 	    ("vioapic_set_pinstate: vioapic is not locked"));
139 
140 	oldcnt = vioapic->rtbl[pin].acnt;
141 	if (newstate)
142 		vioapic->rtbl[pin].acnt++;
143 	else
144 		vioapic->rtbl[pin].acnt--;
145 	newcnt = vioapic->rtbl[pin].acnt;
146 
147 	if (newcnt < 0) {
148 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d",
149 		    pin, newcnt);
150 	}
151 
152 	needintr = false;
153 	if (oldcnt == 0 && newcnt == 1) {
154 		needintr = true;
155 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin);
156 	} else if (oldcnt == 1 && newcnt == 0) {
157 		VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin);
158 	} else {
159 		VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d",
160 		    pin, pinstate_str(newstate), newcnt);
161 	}
162 
163 	if (needintr)
164 		vioapic_send_intr(vioapic, pin);
165 }
166 
167 enum irqstate {
168 	IRQSTATE_ASSERT,
169 	IRQSTATE_DEASSERT,
170 	IRQSTATE_PULSE
171 };
172 
173 static int
174 vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
175 {
176 	struct vioapic *vioapic;
177 
178 	if (irq < 0 || irq >= REDIR_ENTRIES)
179 		return (EINVAL);
180 
181 	vioapic = vm_ioapic(vm);
182 
183 	VIOAPIC_LOCK(vioapic);
184 	switch (irqstate) {
185 	case IRQSTATE_ASSERT:
186 		vioapic_set_pinstate(vioapic, irq, true);
187 		break;
188 	case IRQSTATE_DEASSERT:
189 		vioapic_set_pinstate(vioapic, irq, false);
190 		break;
191 	case IRQSTATE_PULSE:
192 		vioapic_set_pinstate(vioapic, irq, true);
193 		vioapic_set_pinstate(vioapic, irq, false);
194 		break;
195 	default:
196 		panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
197 	}
198 	VIOAPIC_UNLOCK(vioapic);
199 
200 	return (0);
201 }
202 
203 int
204 vioapic_assert_irq(struct vm *vm, int irq)
205 {
206 
207 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
208 }
209 
210 int
211 vioapic_deassert_irq(struct vm *vm, int irq)
212 {
213 
214 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
215 }
216 
217 int
218 vioapic_pulse_irq(struct vm *vm, int irq)
219 {
220 
221 	return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
222 }
223 
224 /*
225  * Reset the vlapic's trigger-mode register to reflect the ioapic pin
226  * configuration.
227  */
228 static void
229 vioapic_update_tmr(struct vm *vm, int vcpuid, void *arg)
230 {
231 	struct vioapic *vioapic;
232 	struct vlapic *vlapic;
233 	uint32_t low, high, dest;
234 	int delmode, pin, vector;
235 	bool level, phys;
236 
237 	vlapic = vm_lapic(vm, vcpuid);
238 	vioapic = vm_ioapic(vm);
239 
240 	VIOAPIC_LOCK(vioapic);
241 	/*
242 	 * Reset all vectors to be edge-triggered.
243 	 */
244 	vlapic_reset_tmr(vlapic);
245 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
246 		low = vioapic->rtbl[pin].reg;
247 		high = vioapic->rtbl[pin].reg >> 32;
248 
249 		level = low & IOART_TRGRLVL ? true : false;
250 		if (!level)
251 			continue;
252 
253 		/*
254 		 * For a level-triggered 'pin' let the vlapic figure out if
255 		 * an assertion on this 'pin' would result in an interrupt
256 		 * being delivered to it. If yes, then it will modify the
257 		 * TMR bit associated with this vector to level-triggered.
258 		 */
259 		phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
260 		delmode = low & IOART_DELMOD;
261 		vector = low & IOART_INTVEC;
262 		dest = high >> APIC_ID_SHIFT;
263 		vlapic_set_tmr_level(vlapic, dest, phys, delmode, vector);
264 	}
265 	VIOAPIC_UNLOCK(vioapic);
266 }
267 
268 static uint32_t
269 vioapic_read(struct vioapic *vioapic, int vcpuid, uint32_t addr)
270 {
271 	int regnum, pin, rshift;
272 
273 	regnum = addr & 0xff;
274 	switch (regnum) {
275 	case IOAPIC_ID:
276 		return (vioapic->id);
277 		break;
278 	case IOAPIC_VER:
279 		return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
280 		break;
281 	case IOAPIC_ARB:
282 		return (vioapic->id);
283 		break;
284 	default:
285 		break;
286 	}
287 
288 	/* redirection table entries */
289 	if (regnum >= IOAPIC_REDTBL &&
290 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
291 		pin = (regnum - IOAPIC_REDTBL) / 2;
292 		if ((regnum - IOAPIC_REDTBL) % 2)
293 			rshift = 32;
294 		else
295 			rshift = 0;
296 
297 		return (vioapic->rtbl[pin].reg >> rshift);
298 	}
299 
300 	return (0);
301 }
302 
303 static void
304 vioapic_write(struct vioapic *vioapic, int vcpuid, uint32_t addr, uint32_t data)
305 {
306 	uint64_t data64, mask64;
307 	uint64_t last, changed;
308 	int regnum, pin, lshift;
309 	cpuset_t allvcpus;
310 
311 	regnum = addr & 0xff;
312 	switch (regnum) {
313 	case IOAPIC_ID:
314 		vioapic->id = data & APIC_ID_MASK;
315 		break;
316 	case IOAPIC_VER:
317 	case IOAPIC_ARB:
318 		/* readonly */
319 		break;
320 	default:
321 		break;
322 	}
323 
324 	/* redirection table entries */
325 	if (regnum >= IOAPIC_REDTBL &&
326 	    regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
327 		pin = (regnum - IOAPIC_REDTBL) / 2;
328 		if ((regnum - IOAPIC_REDTBL) % 2)
329 			lshift = 32;
330 		else
331 			lshift = 0;
332 
333 		last = vioapic->rtbl[pin].reg;
334 
335 		data64 = (uint64_t)data << lshift;
336 		mask64 = (uint64_t)0xffffffff << lshift;
337 		vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
338 		vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
339 
340 		VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
341 		    pin, vioapic->rtbl[pin].reg);
342 
343 		/*
344 		 * If any fields in the redirection table entry (except mask
345 		 * or polarity) have changed then rendezvous all the vcpus
346 		 * to update their vlapic trigger-mode registers.
347 		 */
348 		changed = last ^ vioapic->rtbl[pin].reg;
349 		if (changed & ~(IOART_INTMASK | IOART_INTPOL)) {
350 			VIOAPIC_CTR1(vioapic, "ioapic pin%d: recalculate "
351 			    "vlapic trigger-mode register", pin);
352 			VIOAPIC_UNLOCK(vioapic);
353 			allvcpus = vm_active_cpus(vioapic->vm);
354 			vm_smp_rendezvous(vioapic->vm, vcpuid, allvcpus,
355 			    vioapic_update_tmr, NULL);
356 			VIOAPIC_LOCK(vioapic);
357 		}
358 
359 		/*
360 		 * Generate an interrupt if the following conditions are met:
361 		 * - pin is not masked
362 		 * - previous interrupt has been EOIed
363 		 * - pin level is asserted
364 		 */
365 		if ((vioapic->rtbl[pin].reg & IOART_INTMASK) == IOART_INTMCLR &&
366 		    (vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0 &&
367 		    (vioapic->rtbl[pin].acnt > 0)) {
368 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
369 			    "write, acnt %d", pin, vioapic->rtbl[pin].acnt);
370 			vioapic_send_intr(vioapic, pin);
371 		}
372 	}
373 }
374 
375 static int
376 vioapic_mmio_rw(struct vioapic *vioapic, int vcpuid, uint64_t gpa,
377     uint64_t *data, int size, bool doread)
378 {
379 	uint64_t offset;
380 
381 	offset = gpa - VIOAPIC_BASE;
382 
383 	/*
384 	 * The IOAPIC specification allows 32-bit wide accesses to the
385 	 * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
386 	 */
387 	if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
388 		if (doread)
389 			*data = 0;
390 		return (0);
391 	}
392 
393 	VIOAPIC_LOCK(vioapic);
394 	if (offset == IOREGSEL) {
395 		if (doread)
396 			*data = vioapic->ioregsel;
397 		else
398 			vioapic->ioregsel = *data;
399 	} else {
400 		if (doread) {
401 			*data = vioapic_read(vioapic, vcpuid,
402 			    vioapic->ioregsel);
403 		} else {
404 			vioapic_write(vioapic, vcpuid, vioapic->ioregsel,
405 			    *data);
406 		}
407 	}
408 	VIOAPIC_UNLOCK(vioapic);
409 
410 	return (0);
411 }
412 
413 int
414 vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
415     int size, void *arg)
416 {
417 	int error;
418 	struct vioapic *vioapic;
419 
420 	vioapic = vm_ioapic(vm);
421 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, rval, size, true);
422 	return (error);
423 }
424 
425 int
426 vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
427     int size, void *arg)
428 {
429 	int error;
430 	struct vioapic *vioapic;
431 
432 	vioapic = vm_ioapic(vm);
433 	error = vioapic_mmio_rw(vioapic, vcpuid, gpa, &wval, size, false);
434 	return (error);
435 }
436 
437 void
438 vioapic_process_eoi(struct vm *vm, int vcpuid, int vector)
439 {
440 	struct vioapic *vioapic;
441 	int pin;
442 
443 	KASSERT(vector >= 0 && vector < 256,
444 	    ("vioapic_process_eoi: invalid vector %d", vector));
445 
446 	vioapic = vm_ioapic(vm);
447 	VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector);
448 
449 	/*
450 	 * XXX keep track of the pins associated with this vector instead
451 	 * of iterating on every single pin each time.
452 	 */
453 	VIOAPIC_LOCK(vioapic);
454 	for (pin = 0; pin < REDIR_ENTRIES; pin++) {
455 		if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
456 			continue;
457 		if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
458 			continue;
459 		vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
460 		if (vioapic->rtbl[pin].acnt > 0) {
461 			VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, "
462 			    "acnt %d", pin, vioapic->rtbl[pin].acnt);
463 			vioapic_send_intr(vioapic, pin);
464 		}
465 	}
466 	VIOAPIC_UNLOCK(vioapic);
467 }
468 
469 struct vioapic *
470 vioapic_init(struct vm *vm)
471 {
472 	int i;
473 	struct vioapic *vioapic;
474 
475 	vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
476 
477 	vioapic->vm = vm;
478 	mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_SPIN);
479 
480 	/* Initialize all redirection entries to mask all interrupts */
481 	for (i = 0; i < REDIR_ENTRIES; i++)
482 		vioapic->rtbl[i].reg = 0x0001000000010000UL;
483 
484 	return (vioapic);
485 }
486 
487 void
488 vioapic_cleanup(struct vioapic *vioapic)
489 {
490 
491 	free(vioapic, M_VIOAPIC);
492 }
493 
494 int
495 vioapic_pincount(struct vm *vm)
496 {
497 
498 	return (REDIR_ENTRIES);
499 }
500