xref: /freebsd/sys/amd64/vmm/io/vioapic.c (revision a18eacbefdfa1085ca3db829e86ece78cd416493)
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/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 
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 		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 
71 static 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
83 static const char *
84 pinstate_str(bool asserted)
85 {
86 
87 	if (asserted)
88 		return ("asserted");
89 	else
90 		return ("deasserted");
91 }
92 #endif
93 
94 static void
95 vioapic_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 
163 static int
164 vioapic_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 
180 int
181 vioapic_assert_irq(struct vm *vm, int irq)
182 {
183 
184 	return (vioapic_set_irqstate(vm, irq, true));
185 }
186 
187 int
188 vioapic_deassert_irq(struct vm *vm, int irq)
189 {
190 
191 	return (vioapic_set_irqstate(vm, irq, false));
192 }
193 
194 static uint32_t
195 vioapic_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 
229 static void
230 vioapic_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 
284 static int
285 vioapic_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 
319 int
320 vioapic_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 
331 int
332 vioapic_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 
343 struct vioapic *
344 vioapic_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 
361 void
362 vioapic_cleanup(struct vioapic *vioapic)
363 {
364 
365 	free(vioapic, M_VIOAPIC);
366 }
367