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