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