vioapic.c revision 258494
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 258494 2013-11-23 03:56:03Z neel $ 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vioapic.c 258494 2013-11-23 03:56:03Z 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 "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 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 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 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#ifdef KTR 83static const char * 84pinstate_str(bool asserted) 85{ 86 87 if (asserted) 88 return ("asserted"); 89 else 90 return ("deasserted"); 91} 92#endif 93 94static void 95vioapic_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 163enum irqstate { 164 IRQSTATE_ASSERT, 165 IRQSTATE_DEASSERT, 166 IRQSTATE_PULSE 167}; 168 169static int 170vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate) 171{ 172 struct vioapic *vioapic; 173 174 if (irq < 0 || irq >= REDIR_ENTRIES) 175 return (EINVAL); 176 177 vioapic = vm_ioapic(vm); 178 179 VIOAPIC_LOCK(vioapic); 180 switch (irqstate) { 181 case IRQSTATE_ASSERT: 182 vioapic_set_pinstate(vioapic, irq, true); 183 break; 184 case IRQSTATE_DEASSERT: 185 vioapic_set_pinstate(vioapic, irq, false); 186 break; 187 case IRQSTATE_PULSE: 188 vioapic_set_pinstate(vioapic, irq, true); 189 vioapic_set_pinstate(vioapic, irq, false); 190 break; 191 default: 192 panic("vioapic_set_irqstate: invalid irqstate %d", irqstate); 193 } 194 VIOAPIC_UNLOCK(vioapic); 195 196 return (0); 197} 198 199int 200vioapic_assert_irq(struct vm *vm, int irq) 201{ 202 203 return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT)); 204} 205 206int 207vioapic_deassert_irq(struct vm *vm, int irq) 208{ 209 210 return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT)); 211} 212 213int 214vioapic_pulse_irq(struct vm *vm, int irq) 215{ 216 217 return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE)); 218} 219 220static uint32_t 221vioapic_read(struct vioapic *vioapic, uint32_t addr) 222{ 223 int regnum, pin, rshift; 224 225 regnum = addr & 0xff; 226 switch (regnum) { 227 case IOAPIC_ID: 228 return (vioapic->id); 229 break; 230 case IOAPIC_VER: 231 return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11); 232 break; 233 case IOAPIC_ARB: 234 return (vioapic->id); 235 break; 236 default: 237 break; 238 } 239 240 /* redirection table entries */ 241 if (regnum >= IOAPIC_REDTBL && 242 regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 243 pin = (regnum - IOAPIC_REDTBL) / 2; 244 if ((regnum - IOAPIC_REDTBL) % 2) 245 rshift = 32; 246 else 247 rshift = 0; 248 249 return (vioapic->rtbl[pin].reg >> rshift); 250 } 251 252 return (0); 253} 254 255static void 256vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data) 257{ 258 int regnum, pin, lshift; 259 260 regnum = addr & 0xff; 261 switch (regnum) { 262 case IOAPIC_ID: 263 vioapic->id = data & APIC_ID_MASK; 264 break; 265 case IOAPIC_VER: 266 case IOAPIC_ARB: 267 /* readonly */ 268 break; 269 default: 270 break; 271 } 272 273 /* redirection table entries */ 274 if (regnum >= IOAPIC_REDTBL && 275 regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 276 pin = (regnum - IOAPIC_REDTBL) / 2; 277 if ((regnum - IOAPIC_REDTBL) % 2) 278 lshift = 32; 279 else 280 lshift = 0; 281 282 vioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift); 283 vioapic->rtbl[pin].reg |= ((uint64_t)data << lshift); 284 285 VIOAPIC_CTR2(vioapic, "ioapic pin%d redir table entry %#lx", 286 pin, vioapic->rtbl[pin].reg); 287 288 if (vioapic->rtbl[pin].pending && 289 ((vioapic->rtbl[pin].reg & IOART_INTMASK) == 290 IOART_INTMCLR)) { 291 vioapic->rtbl[pin].pending = false; 292 /* 293 * Inject the deferred level-triggered int if it is 294 * still asserted. Simulate by toggling the pin 295 * off and then on. 296 */ 297 if (vioapic->rtbl[pin].pinstate == true) { 298 VIOAPIC_CTR1(vioapic, "ioapic pin%d pending " 299 "interrupt delivered", pin); 300 vioapic_set_pinstate(vioapic, pin, false); 301 vioapic_set_pinstate(vioapic, pin, true); 302 } else { 303 VIOAPIC_CTR1(vioapic, "ioapic pin%d pending " 304 "interrupt dismissed", pin); 305 } 306 } 307 } 308} 309 310static int 311vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data, 312 int size, bool doread) 313{ 314 uint64_t offset; 315 316 offset = gpa - VIOAPIC_BASE; 317 318 /* 319 * The IOAPIC specification allows 32-bit wide accesses to the 320 * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 321 */ 322 if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 323 if (doread) 324 *data = 0; 325 return (0); 326 } 327 328 VIOAPIC_LOCK(vioapic); 329 if (offset == IOREGSEL) { 330 if (doread) 331 *data = vioapic->ioregsel; 332 else 333 vioapic->ioregsel = *data; 334 } else { 335 if (doread) 336 *data = vioapic_read(vioapic, vioapic->ioregsel); 337 else 338 vioapic_write(vioapic, vioapic->ioregsel, *data); 339 } 340 VIOAPIC_UNLOCK(vioapic); 341 342 return (0); 343} 344 345int 346vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, 347 int size, void *arg) 348{ 349 int error; 350 struct vioapic *vioapic; 351 352 vioapic = vm_ioapic(vm); 353 error = vioapic_mmio_rw(vioapic, gpa, rval, size, true); 354 return (error); 355} 356 357int 358vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval, 359 int size, void *arg) 360{ 361 int error; 362 struct vioapic *vioapic; 363 364 vioapic = vm_ioapic(vm); 365 error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false); 366 return (error); 367} 368 369struct vioapic * 370vioapic_init(struct vm *vm) 371{ 372 int i; 373 struct vioapic *vioapic; 374 375 vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO); 376 377 vioapic->vm = vm; 378 mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF); 379 380 /* Initialize all redirection entries to mask all interrupts */ 381 for (i = 0; i < REDIR_ENTRIES; i++) 382 vioapic->rtbl[i].reg = 0x0001000000010000UL; 383 384 return (vioapic); 385} 386 387void 388vioapic_cleanup(struct vioapic *vioapic) 389{ 390 391 free(vioapic, M_VIOAPIC); 392} 393