]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/amd64/vmm/io/vioapic.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / amd64 / vmm / io / vioapic.c
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 }