1565bbb86SNeel Natu /*- 2565bbb86SNeel Natu * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3565bbb86SNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4565bbb86SNeel Natu * All rights reserved. 5565bbb86SNeel Natu * 6565bbb86SNeel Natu * Redistribution and use in source and binary forms, with or without 7565bbb86SNeel Natu * modification, are permitted provided that the following conditions 8565bbb86SNeel Natu * are met: 9565bbb86SNeel Natu * 1. Redistributions of source code must retain the above copyright 10565bbb86SNeel Natu * notice, this list of conditions and the following disclaimer. 11565bbb86SNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 12565bbb86SNeel Natu * notice, this list of conditions and the following disclaimer in the 13565bbb86SNeel Natu * documentation and/or other materials provided with the distribution. 14565bbb86SNeel Natu * 15565bbb86SNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16565bbb86SNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17565bbb86SNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18565bbb86SNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19565bbb86SNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20565bbb86SNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21565bbb86SNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22565bbb86SNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23565bbb86SNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24565bbb86SNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25565bbb86SNeel Natu * SUCH DAMAGE. 26565bbb86SNeel Natu * 27565bbb86SNeel Natu * $FreeBSD$ 28565bbb86SNeel Natu */ 29565bbb86SNeel Natu 30565bbb86SNeel Natu #include <sys/cdefs.h> 31565bbb86SNeel Natu __FBSDID("$FreeBSD$"); 32565bbb86SNeel Natu 33565bbb86SNeel Natu #include <sys/param.h> 34565bbb86SNeel Natu #include <sys/queue.h> 35565bbb86SNeel Natu #include <sys/cpuset.h> 36565bbb86SNeel Natu #include <sys/lock.h> 37565bbb86SNeel Natu #include <sys/mutex.h> 38565bbb86SNeel Natu #include <sys/systm.h> 39565bbb86SNeel Natu #include <sys/kernel.h> 40565bbb86SNeel Natu #include <sys/malloc.h> 41565bbb86SNeel Natu 42565bbb86SNeel Natu #include <x86/apicreg.h> 43565bbb86SNeel Natu #include <machine/vmm.h> 44565bbb86SNeel Natu 45565bbb86SNeel Natu #include "vmm_ktr.h" 46565bbb86SNeel Natu #include "vmm_lapic.h" 47565bbb86SNeel Natu #include "vioapic.h" 48565bbb86SNeel Natu 49565bbb86SNeel Natu #define IOREGSEL 0x00 50565bbb86SNeel Natu #define IOWIN 0x10 51565bbb86SNeel Natu 52565bbb86SNeel Natu #define REDIR_ENTRIES 16 53565bbb86SNeel Natu #define INTR_ASSERTED(vioapic, pin) ((vioapic)->rtbl[(pin)].pinstate == true) 54565bbb86SNeel Natu 55565bbb86SNeel Natu struct vioapic { 56565bbb86SNeel Natu struct vm *vm; 57565bbb86SNeel Natu struct mtx mtx; 58565bbb86SNeel Natu uint32_t id; 59565bbb86SNeel Natu uint32_t ioregsel; 60565bbb86SNeel Natu struct { 61565bbb86SNeel Natu uint64_t reg; 62565bbb86SNeel Natu bool pinstate; 63565bbb86SNeel Natu bool pending; 64565bbb86SNeel Natu } rtbl[REDIR_ENTRIES]; 65565bbb86SNeel Natu }; 66565bbb86SNeel Natu 67565bbb86SNeel Natu #define VIOAPIC_LOCK(vioapic) mtx_lock(&((vioapic)->mtx)) 68565bbb86SNeel Natu #define VIOAPIC_UNLOCK(vioapic) mtx_unlock(&((vioapic)->mtx)) 69565bbb86SNeel Natu #define VIOAPIC_LOCKED(vioapic) mtx_owned(&((vioapic)->mtx)) 70565bbb86SNeel Natu 71565bbb86SNeel Natu static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic"); 72565bbb86SNeel Natu 73565bbb86SNeel Natu #define VIOAPIC_CTR1(vioapic, fmt, a1) \ 74565bbb86SNeel Natu VM_CTR1((vioapic)->vm, fmt, a1) 75565bbb86SNeel Natu 76565bbb86SNeel Natu #define VIOAPIC_CTR2(vioapic, fmt, a1, a2) \ 77565bbb86SNeel Natu VM_CTR2((vioapic)->vm, fmt, a1, a2) 78565bbb86SNeel Natu 79565bbb86SNeel Natu #define VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3) \ 80565bbb86SNeel Natu VM_CTR3((vioapic)->vm, fmt, a1, a2, a3) 81565bbb86SNeel Natu 82565bbb86SNeel Natu #ifdef KTR 83565bbb86SNeel Natu static const char * 84565bbb86SNeel Natu pinstate_str(bool asserted) 85565bbb86SNeel Natu { 86565bbb86SNeel Natu 87565bbb86SNeel Natu if (asserted) 88565bbb86SNeel Natu return ("asserted"); 89565bbb86SNeel Natu else 90565bbb86SNeel Natu return ("deasserted"); 91565bbb86SNeel Natu } 92565bbb86SNeel Natu #endif 93565bbb86SNeel Natu 94565bbb86SNeel Natu static void 95565bbb86SNeel Natu vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate) 96565bbb86SNeel Natu { 97565bbb86SNeel Natu int vector, apicid, vcpuid; 98565bbb86SNeel Natu uint32_t low, high; 99565bbb86SNeel Natu cpuset_t dmask; 100565bbb86SNeel Natu 101565bbb86SNeel Natu KASSERT(pin >= 0 && pin < REDIR_ENTRIES, 102565bbb86SNeel Natu ("vioapic_set_pinstate: invalid pin number %d", pin)); 103565bbb86SNeel Natu 104565bbb86SNeel Natu KASSERT(VIOAPIC_LOCKED(vioapic), 105565bbb86SNeel Natu ("vioapic_set_pinstate: vioapic is not locked")); 106565bbb86SNeel Natu 107565bbb86SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d %s", pin, pinstate_str(newstate)); 108565bbb86SNeel Natu 109565bbb86SNeel Natu /* Nothing to do if interrupt pin has not changed state */ 110565bbb86SNeel Natu if (vioapic->rtbl[pin].pinstate == newstate) 111565bbb86SNeel Natu return; 112565bbb86SNeel Natu 113565bbb86SNeel Natu vioapic->rtbl[pin].pinstate = newstate; /* record it */ 114565bbb86SNeel Natu 115565bbb86SNeel Natu /* Nothing to do if interrupt pin is deasserted */ 116565bbb86SNeel Natu if (!INTR_ASSERTED(vioapic, pin)) 117565bbb86SNeel Natu return; 118565bbb86SNeel Natu 119565bbb86SNeel Natu /* 120565bbb86SNeel Natu * XXX 121565bbb86SNeel Natu * We only deal with: 122565bbb86SNeel Natu * - edge triggered interrupts 123565bbb86SNeel Natu * - fixed delivery mode 124565bbb86SNeel Natu * Level-triggered sources will work so long as there is no sharing. 125565bbb86SNeel Natu */ 126565bbb86SNeel Natu low = vioapic->rtbl[pin].reg; 127565bbb86SNeel Natu high = vioapic->rtbl[pin].reg >> 32; 128565bbb86SNeel Natu if ((low & IOART_INTMASK) == IOART_INTMCLR && 129565bbb86SNeel Natu (low & IOART_DESTMOD) == IOART_DESTPHY && 130565bbb86SNeel Natu (low & IOART_DELMOD) == IOART_DELFIXED) { 131565bbb86SNeel Natu vector = low & IOART_INTVEC; 132565bbb86SNeel Natu apicid = high >> APIC_ID_SHIFT; 133565bbb86SNeel Natu if (apicid != 0xff) { 134565bbb86SNeel Natu /* unicast */ 135565bbb86SNeel Natu vcpuid = vm_apicid2vcpuid(vioapic->vm, apicid); 136565bbb86SNeel Natu VIOAPIC_CTR3(vioapic, "ioapic pin%d triggering " 137565bbb86SNeel Natu "intr vector %d on vcpuid %d", pin, vector, vcpuid); 138565bbb86SNeel Natu lapic_set_intr(vioapic->vm, vcpuid, vector); 139565bbb86SNeel Natu } else { 140565bbb86SNeel Natu /* broadcast */ 141565bbb86SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d triggering intr " 142565bbb86SNeel Natu "vector %d on all vcpus", pin, vector); 143565bbb86SNeel Natu dmask = vm_active_cpus(vioapic->vm); 144565bbb86SNeel Natu while ((vcpuid = CPU_FFS(&dmask)) != 0) { 145565bbb86SNeel Natu vcpuid--; 146565bbb86SNeel Natu CPU_CLR(vcpuid, &dmask); 147565bbb86SNeel Natu lapic_set_intr(vioapic->vm, vcpuid, vector); 148565bbb86SNeel Natu } 149565bbb86SNeel Natu } 150565bbb86SNeel Natu } else if ((low & IOART_INTMASK) != IOART_INTMCLR && 151565bbb86SNeel Natu (low & IOART_TRGRLVL) != 0) { 152565bbb86SNeel Natu /* 153565bbb86SNeel Natu * For level-triggered interrupts that have been 154565bbb86SNeel Natu * masked, set the pending bit so that an interrupt 155565bbb86SNeel Natu * will be generated on unmask and if the level is 156565bbb86SNeel Natu * still asserted 157565bbb86SNeel Natu */ 158565bbb86SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d interrupt pending", pin); 159565bbb86SNeel Natu vioapic->rtbl[pin].pending = true; 160565bbb86SNeel Natu } 161565bbb86SNeel Natu } 162565bbb86SNeel Natu 163*ac7304a7SNeel Natu enum irqstate { 164*ac7304a7SNeel Natu IRQSTATE_ASSERT, 165*ac7304a7SNeel Natu IRQSTATE_DEASSERT, 166*ac7304a7SNeel Natu IRQSTATE_PULSE 167*ac7304a7SNeel Natu }; 168*ac7304a7SNeel Natu 169565bbb86SNeel Natu static int 170*ac7304a7SNeel Natu vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate) 171565bbb86SNeel Natu { 172565bbb86SNeel Natu struct vioapic *vioapic; 173565bbb86SNeel Natu 174565bbb86SNeel Natu if (irq < 0 || irq >= REDIR_ENTRIES) 175565bbb86SNeel Natu return (EINVAL); 176565bbb86SNeel Natu 177565bbb86SNeel Natu vioapic = vm_ioapic(vm); 178565bbb86SNeel Natu 179565bbb86SNeel Natu VIOAPIC_LOCK(vioapic); 180*ac7304a7SNeel Natu switch (irqstate) { 181*ac7304a7SNeel Natu case IRQSTATE_ASSERT: 182*ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, true); 183*ac7304a7SNeel Natu break; 184*ac7304a7SNeel Natu case IRQSTATE_DEASSERT: 185*ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, false); 186*ac7304a7SNeel Natu break; 187*ac7304a7SNeel Natu case IRQSTATE_PULSE: 188*ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, true); 189*ac7304a7SNeel Natu vioapic_set_pinstate(vioapic, irq, false); 190*ac7304a7SNeel Natu break; 191*ac7304a7SNeel Natu default: 192*ac7304a7SNeel Natu panic("vioapic_set_irqstate: invalid irqstate %d", irqstate); 193*ac7304a7SNeel Natu } 194565bbb86SNeel Natu VIOAPIC_UNLOCK(vioapic); 195565bbb86SNeel Natu 196565bbb86SNeel Natu return (0); 197565bbb86SNeel Natu } 198565bbb86SNeel Natu 199565bbb86SNeel Natu int 200565bbb86SNeel Natu vioapic_assert_irq(struct vm *vm, int irq) 201565bbb86SNeel Natu { 202565bbb86SNeel Natu 203*ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT)); 204565bbb86SNeel Natu } 205565bbb86SNeel Natu 206565bbb86SNeel Natu int 207565bbb86SNeel Natu vioapic_deassert_irq(struct vm *vm, int irq) 208565bbb86SNeel Natu { 209565bbb86SNeel Natu 210*ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT)); 211*ac7304a7SNeel Natu } 212*ac7304a7SNeel Natu 213*ac7304a7SNeel Natu int 214*ac7304a7SNeel Natu vioapic_pulse_irq(struct vm *vm, int irq) 215*ac7304a7SNeel Natu { 216*ac7304a7SNeel Natu 217*ac7304a7SNeel Natu return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE)); 218565bbb86SNeel Natu } 219565bbb86SNeel Natu 220565bbb86SNeel Natu static uint32_t 221565bbb86SNeel Natu vioapic_read(struct vioapic *vioapic, uint32_t addr) 222565bbb86SNeel Natu { 223565bbb86SNeel Natu int regnum, pin, rshift; 224565bbb86SNeel Natu 225565bbb86SNeel Natu regnum = addr & 0xff; 226565bbb86SNeel Natu switch (regnum) { 227565bbb86SNeel Natu case IOAPIC_ID: 228565bbb86SNeel Natu return (vioapic->id); 229565bbb86SNeel Natu break; 230565bbb86SNeel Natu case IOAPIC_VER: 231565bbb86SNeel Natu return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11); 232565bbb86SNeel Natu break; 233565bbb86SNeel Natu case IOAPIC_ARB: 234565bbb86SNeel Natu return (vioapic->id); 235565bbb86SNeel Natu break; 236565bbb86SNeel Natu default: 237565bbb86SNeel Natu break; 238565bbb86SNeel Natu } 239565bbb86SNeel Natu 240565bbb86SNeel Natu /* redirection table entries */ 241565bbb86SNeel Natu if (regnum >= IOAPIC_REDTBL && 242565bbb86SNeel Natu regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 243565bbb86SNeel Natu pin = (regnum - IOAPIC_REDTBL) / 2; 244565bbb86SNeel Natu if ((regnum - IOAPIC_REDTBL) % 2) 245565bbb86SNeel Natu rshift = 32; 246565bbb86SNeel Natu else 247565bbb86SNeel Natu rshift = 0; 248565bbb86SNeel Natu 249565bbb86SNeel Natu return (vioapic->rtbl[pin].reg >> rshift); 250565bbb86SNeel Natu } 251565bbb86SNeel Natu 252565bbb86SNeel Natu return (0); 253565bbb86SNeel Natu } 254565bbb86SNeel Natu 255565bbb86SNeel Natu static void 256565bbb86SNeel Natu vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data) 257565bbb86SNeel Natu { 258565bbb86SNeel Natu int regnum, pin, lshift; 259565bbb86SNeel Natu 260565bbb86SNeel Natu regnum = addr & 0xff; 261565bbb86SNeel Natu switch (regnum) { 262565bbb86SNeel Natu case IOAPIC_ID: 263565bbb86SNeel Natu vioapic->id = data & APIC_ID_MASK; 264565bbb86SNeel Natu break; 265565bbb86SNeel Natu case IOAPIC_VER: 266565bbb86SNeel Natu case IOAPIC_ARB: 267565bbb86SNeel Natu /* readonly */ 268565bbb86SNeel Natu break; 269565bbb86SNeel Natu default: 270565bbb86SNeel Natu break; 271565bbb86SNeel Natu } 272565bbb86SNeel Natu 273565bbb86SNeel Natu /* redirection table entries */ 274565bbb86SNeel Natu if (regnum >= IOAPIC_REDTBL && 275565bbb86SNeel Natu regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 276565bbb86SNeel Natu pin = (regnum - IOAPIC_REDTBL) / 2; 277565bbb86SNeel Natu if ((regnum - IOAPIC_REDTBL) % 2) 278565bbb86SNeel Natu lshift = 32; 279565bbb86SNeel Natu else 280565bbb86SNeel Natu lshift = 0; 281565bbb86SNeel Natu 282565bbb86SNeel Natu vioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift); 283565bbb86SNeel Natu vioapic->rtbl[pin].reg |= ((uint64_t)data << lshift); 284565bbb86SNeel Natu 285565bbb86SNeel Natu VIOAPIC_CTR2(vioapic, "ioapic pin%d redir table entry %#lx", 286565bbb86SNeel Natu pin, vioapic->rtbl[pin].reg); 287565bbb86SNeel Natu 288565bbb86SNeel Natu if (vioapic->rtbl[pin].pending && 289565bbb86SNeel Natu ((vioapic->rtbl[pin].reg & IOART_INTMASK) == 290565bbb86SNeel Natu IOART_INTMCLR)) { 291565bbb86SNeel Natu vioapic->rtbl[pin].pending = false; 292565bbb86SNeel Natu /* 293565bbb86SNeel Natu * Inject the deferred level-triggered int if it is 294565bbb86SNeel Natu * still asserted. Simulate by toggling the pin 295565bbb86SNeel Natu * off and then on. 296565bbb86SNeel Natu */ 297565bbb86SNeel Natu if (vioapic->rtbl[pin].pinstate == true) { 298565bbb86SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d pending " 299565bbb86SNeel Natu "interrupt delivered", pin); 300565bbb86SNeel Natu vioapic_set_pinstate(vioapic, pin, false); 301565bbb86SNeel Natu vioapic_set_pinstate(vioapic, pin, true); 302565bbb86SNeel Natu } else { 303565bbb86SNeel Natu VIOAPIC_CTR1(vioapic, "ioapic pin%d pending " 304565bbb86SNeel Natu "interrupt dismissed", pin); 305565bbb86SNeel Natu } 306565bbb86SNeel Natu } 307565bbb86SNeel Natu } 308565bbb86SNeel Natu } 309565bbb86SNeel Natu 310565bbb86SNeel Natu static int 311565bbb86SNeel Natu vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data, 312565bbb86SNeel Natu int size, bool doread) 313565bbb86SNeel Natu { 314565bbb86SNeel Natu uint64_t offset; 315565bbb86SNeel Natu 316565bbb86SNeel Natu offset = gpa - VIOAPIC_BASE; 317565bbb86SNeel Natu 318565bbb86SNeel Natu /* 319565bbb86SNeel Natu * The IOAPIC specification allows 32-bit wide accesses to the 320565bbb86SNeel Natu * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 321565bbb86SNeel Natu */ 322565bbb86SNeel Natu if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 323565bbb86SNeel Natu if (doread) 324565bbb86SNeel Natu *data = 0; 325565bbb86SNeel Natu return (0); 326565bbb86SNeel Natu } 327565bbb86SNeel Natu 328565bbb86SNeel Natu VIOAPIC_LOCK(vioapic); 329565bbb86SNeel Natu if (offset == IOREGSEL) { 330565bbb86SNeel Natu if (doread) 331565bbb86SNeel Natu *data = vioapic->ioregsel; 332565bbb86SNeel Natu else 333565bbb86SNeel Natu vioapic->ioregsel = *data; 334565bbb86SNeel Natu } else { 335565bbb86SNeel Natu if (doread) 336565bbb86SNeel Natu *data = vioapic_read(vioapic, vioapic->ioregsel); 337565bbb86SNeel Natu else 338565bbb86SNeel Natu vioapic_write(vioapic, vioapic->ioregsel, *data); 339565bbb86SNeel Natu } 340565bbb86SNeel Natu VIOAPIC_UNLOCK(vioapic); 341565bbb86SNeel Natu 342565bbb86SNeel Natu return (0); 343565bbb86SNeel Natu } 344565bbb86SNeel Natu 345565bbb86SNeel Natu int 346565bbb86SNeel Natu vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, 347565bbb86SNeel Natu int size, void *arg) 348565bbb86SNeel Natu { 349565bbb86SNeel Natu int error; 350565bbb86SNeel Natu struct vioapic *vioapic; 351565bbb86SNeel Natu 352565bbb86SNeel Natu vioapic = vm_ioapic(vm); 353565bbb86SNeel Natu error = vioapic_mmio_rw(vioapic, gpa, rval, size, true); 354565bbb86SNeel Natu return (error); 355565bbb86SNeel Natu } 356565bbb86SNeel Natu 357565bbb86SNeel Natu int 358565bbb86SNeel Natu vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval, 359565bbb86SNeel Natu int size, void *arg) 360565bbb86SNeel Natu { 361565bbb86SNeel Natu int error; 362565bbb86SNeel Natu struct vioapic *vioapic; 363565bbb86SNeel Natu 364565bbb86SNeel Natu vioapic = vm_ioapic(vm); 365565bbb86SNeel Natu error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false); 366565bbb86SNeel Natu return (error); 367565bbb86SNeel Natu } 368565bbb86SNeel Natu 369565bbb86SNeel Natu struct vioapic * 370565bbb86SNeel Natu vioapic_init(struct vm *vm) 371565bbb86SNeel Natu { 372565bbb86SNeel Natu int i; 373565bbb86SNeel Natu struct vioapic *vioapic; 374565bbb86SNeel Natu 375565bbb86SNeel Natu vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO); 376565bbb86SNeel Natu 377565bbb86SNeel Natu vioapic->vm = vm; 378565bbb86SNeel Natu mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF); 379565bbb86SNeel Natu 380565bbb86SNeel Natu /* Initialize all redirection entries to mask all interrupts */ 381565bbb86SNeel Natu for (i = 0; i < REDIR_ENTRIES; i++) 382565bbb86SNeel Natu vioapic->rtbl[i].reg = 0x0001000000010000UL; 383565bbb86SNeel Natu 384565bbb86SNeel Natu return (vioapic); 385565bbb86SNeel Natu } 386565bbb86SNeel Natu 387565bbb86SNeel Natu void 388565bbb86SNeel Natu vioapic_cleanup(struct vioapic *vioapic) 389565bbb86SNeel Natu { 390565bbb86SNeel Natu 391565bbb86SNeel Natu free(vioapic, M_VIOAPIC); 392565bbb86SNeel Natu } 393