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