1239045Sneel/*- 2239045Sneel * Copyright (c) 2012 NetApp, Inc. 3239045Sneel * All rights reserved. 4239045Sneel * 5239045Sneel * Redistribution and use in source and binary forms, with or without 6239045Sneel * modification, are permitted provided that the following conditions 7239045Sneel * are met: 8239045Sneel * 1. Redistributions of source code must retain the above copyright 9239045Sneel * notice, this list of conditions and the following disclaimer. 10239045Sneel * 2. Redistributions in binary form must reproduce the above copyright 11239045Sneel * notice, this list of conditions and the following disclaimer in the 12239045Sneel * documentation and/or other materials provided with the distribution. 13239045Sneel * 14239045Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15239045Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239045Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239045Sneel * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18239045Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239045Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239045Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239045Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239045Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239045Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239045Sneel * SUCH DAMAGE. 25239045Sneel * 26239045Sneel * $FreeBSD$ 27239045Sneel */ 28239045Sneel 29239045Sneel#include <sys/cdefs.h> 30239045Sneel__FBSDID("$FreeBSD$"); 31239045Sneel 32239045Sneel#include <sys/types.h> 33239045Sneel 34239045Sneel#include <x86/apicreg.h> 35239045Sneel#include <machine/vmm.h> 36239045Sneel 37239045Sneel#include <string.h> 38239045Sneel#include <assert.h> 39239045Sneel#include <stdbool.h> 40257397Sgrehan#include <pthread.h> 41239045Sneel 42239045Sneel#include <vmmapi.h> 43239045Sneel 44239045Sneel#include "inout.h" 45241744Sgrehan#include "mem.h" 46244167Sgrehan#include "bhyverun.h" 47239045Sneel 48239045Sneel#include <stdio.h> 49239045Sneel 50257397Sgrehanstatic uint64_t ioapic_clearpend, ioapic_togglepend, ioapic_setpend; 51257397Sgrehan 52239045Sneel#define IOAPIC_PADDR 0xFEC00000 53239045Sneel 54239045Sneel#define IOREGSEL 0x00 55239045Sneel#define IOWIN 0x10 56239045Sneel 57239045Sneel#define REDIR_ENTRIES 16 58257397Sgrehan#define INTR_ASSERTED(ioapic, pin) \ 59257397Sgrehan ((ioapic)->rtbl[(pin)].pinstate == true) 60239045Sneel 61239045Sneelstruct ioapic { 62239045Sneel int inited; 63239045Sneel uint32_t id; 64257397Sgrehan struct { 65257397Sgrehan uint64_t reg; 66257397Sgrehan bool pinstate; 67257397Sgrehan bool pending; 68257397Sgrehan } rtbl[REDIR_ENTRIES]; 69239045Sneel 70239045Sneel uintptr_t paddr; /* gpa where the ioapic is mapped */ 71239045Sneel uint32_t ioregsel; 72239045Sneel struct memory_region *region; 73257397Sgrehan pthread_mutex_t mtx; 74239045Sneel}; 75239045Sneel 76239045Sneelstatic struct ioapic ioapics[1]; /* only a single ioapic for now */ 77239045Sneel 78257397Sgrehanstatic int ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic, 79257397Sgrehan uintptr_t paddr, int size, uint64_t *data); 80257397Sgrehanstatic int ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic, 81257397Sgrehan uintptr_t paddr, int size, uint64_t data); 82241744Sgrehanstatic int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir, 83257397Sgrehan uintptr_t paddr, int size, uint64_t *val, void *arg1, long arg2); 84239045Sneel 85239045Sneelstatic void 86239045Sneelioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate) 87239045Sneel{ 88239045Sneel int vector, apicid, vcpu; 89239045Sneel uint32_t low, high; 90239045Sneel struct ioapic *ioapic; 91239045Sneel 92239045Sneel ioapic = &ioapics[0]; /* assume a single ioapic */ 93239045Sneel 94239045Sneel /* Nothing to do if interrupt pin has not changed state */ 95257397Sgrehan if (ioapic->rtbl[pin].pinstate == newstate) 96239045Sneel return; 97239045Sneel 98257397Sgrehan ioapic->rtbl[pin].pinstate = newstate; /* record it */ 99239045Sneel 100239045Sneel /* Nothing to do if interrupt pin is deasserted */ 101239045Sneel if (!INTR_ASSERTED(ioapic, pin)) 102239045Sneel return; 103239045Sneel 104239045Sneel /* 105239045Sneel * XXX 106239045Sneel * We only deal with: 107239045Sneel * - edge triggered interrupts 108239045Sneel * - fixed delivery mode 109255293Sgrehan * Level-triggered sources will work so long as there is 110255292Sgrehan * no sharing. 111239045Sneel */ 112257397Sgrehan low = ioapic->rtbl[pin].reg; 113257397Sgrehan high = ioapic->rtbl[pin].reg >> 32; 114239045Sneel if ((low & IOART_INTMASK) == IOART_INTMCLR && 115239045Sneel (low & IOART_DESTMOD) == IOART_DESTPHY && 116239045Sneel (low & IOART_DELMOD) == IOART_DELFIXED) { 117239045Sneel vector = low & IOART_INTVEC; 118239045Sneel apicid = high >> APIC_ID_SHIFT; 119239045Sneel if (apicid != 0xff) { 120239045Sneel /* unicast */ 121239045Sneel vcpu = vm_apicid2vcpu(ctx, apicid); 122239045Sneel vm_lapic_irq(ctx, vcpu, vector); 123239045Sneel } else { 124239045Sneel /* broadcast */ 125239045Sneel vcpu = 0; 126239045Sneel while (vcpu < guest_ncpus) { 127239045Sneel vm_lapic_irq(ctx, vcpu, vector); 128239045Sneel vcpu++; 129239045Sneel } 130239045Sneel } 131257397Sgrehan } else if ((low & IOART_INTMASK) != IOART_INTMCLR && 132257397Sgrehan low & IOART_TRGRLVL) { 133257397Sgrehan /* 134257397Sgrehan * For level-triggered interrupts that have been 135257397Sgrehan * masked, set the pending bit so that an interrupt 136257397Sgrehan * will be generated on unmask and if the level is 137257397Sgrehan * still asserted 138257397Sgrehan */ 139257397Sgrehan ioapic_setpend++; 140257397Sgrehan ioapic->rtbl[pin].pending = true; 141239045Sneel } 142239045Sneel} 143239045Sneel 144257397Sgrehanstatic void 145257397Sgrehanioapic_set_pinstate_locked(struct vmctx *ctx, int pin, bool newstate) 146257397Sgrehan{ 147257397Sgrehan struct ioapic *ioapic; 148257397Sgrehan 149257397Sgrehan if (pin < 0 || pin >= REDIR_ENTRIES) 150257397Sgrehan return; 151257397Sgrehan 152257397Sgrehan ioapic = &ioapics[0]; 153257397Sgrehan 154257397Sgrehan pthread_mutex_lock(&ioapic->mtx); 155257397Sgrehan ioapic_set_pinstate(ctx, pin, newstate); 156257397Sgrehan pthread_mutex_unlock(&ioapic->mtx); 157257397Sgrehan} 158257397Sgrehan 159257397Sgrehan/* 160257397Sgrehan * External entry points require locking 161257397Sgrehan */ 162239045Sneelvoid 163239045Sneelioapic_deassert_pin(struct vmctx *ctx, int pin) 164239045Sneel{ 165257397Sgrehan ioapic_set_pinstate_locked(ctx, pin, false); 166239045Sneel} 167239045Sneel 168239045Sneelvoid 169239045Sneelioapic_assert_pin(struct vmctx *ctx, int pin) 170239045Sneel{ 171257397Sgrehan ioapic_set_pinstate_locked(ctx, pin, true); 172239045Sneel} 173239045Sneel 174239045Sneelvoid 175239045Sneelioapic_init(int which) 176239045Sneel{ 177241744Sgrehan struct mem_range memp; 178241744Sgrehan struct ioapic *ioapic; 179241744Sgrehan int error; 180239045Sneel int i; 181239045Sneel 182239045Sneel assert(which == 0); 183239045Sneel 184239045Sneel ioapic = &ioapics[which]; 185239045Sneel assert(ioapic->inited == 0); 186239045Sneel 187239045Sneel bzero(ioapic, sizeof(struct ioapic)); 188239045Sneel 189257397Sgrehan pthread_mutex_init(&ioapic->mtx, NULL); 190257397Sgrehan 191239045Sneel /* Initialize all redirection entries to mask all interrupts */ 192239045Sneel for (i = 0; i < REDIR_ENTRIES; i++) 193257397Sgrehan ioapic->rtbl[i].reg = 0x0001000000010000UL; 194239045Sneel 195239045Sneel ioapic->paddr = IOAPIC_PADDR; 196239045Sneel 197241744Sgrehan /* Register emulated memory region */ 198241744Sgrehan memp.name = "ioapic"; 199241744Sgrehan memp.flags = MEM_F_RW; 200241744Sgrehan memp.handler = ioapic_region_handler; 201241744Sgrehan memp.arg1 = ioapic; 202241744Sgrehan memp.arg2 = which; 203241744Sgrehan memp.base = ioapic->paddr; 204241744Sgrehan memp.size = sizeof(struct IOAPIC); 205241744Sgrehan error = register_mem(&memp); 206241744Sgrehan 207241744Sgrehan assert (error == 0); 208241744Sgrehan 209239045Sneel ioapic->inited = 1; 210239045Sneel} 211239045Sneel 212239045Sneelstatic uint32_t 213239045Sneelioapic_read(struct ioapic *ioapic, uint32_t addr) 214239045Sneel{ 215239045Sneel int regnum, pin, rshift; 216239045Sneel 217239045Sneel assert(ioapic->inited); 218239045Sneel 219239045Sneel regnum = addr & 0xff; 220239045Sneel switch (regnum) { 221239045Sneel case IOAPIC_ID: 222239045Sneel return (ioapic->id); 223239045Sneel break; 224239045Sneel case IOAPIC_VER: 225239045Sneel return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11); 226239045Sneel break; 227239045Sneel case IOAPIC_ARB: 228239045Sneel return (ioapic->id); 229239045Sneel break; 230239045Sneel default: 231239045Sneel break; 232239045Sneel } 233239045Sneel 234239045Sneel /* redirection table entries */ 235239045Sneel if (regnum >= IOAPIC_REDTBL && 236239045Sneel regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 237239045Sneel pin = (regnum - IOAPIC_REDTBL) / 2; 238239045Sneel if ((regnum - IOAPIC_REDTBL) % 2) 239239045Sneel rshift = 32; 240239045Sneel else 241239045Sneel rshift = 0; 242239045Sneel 243257397Sgrehan return (ioapic->rtbl[pin].reg >> rshift); 244239045Sneel } 245239045Sneel 246239045Sneel return (0); 247239045Sneel} 248239045Sneel 249239045Sneelstatic void 250257397Sgrehanioapic_write(struct vmctx *vm, struct ioapic *ioapic, uint32_t addr, 251257397Sgrehan uint32_t data) 252239045Sneel{ 253239045Sneel int regnum, pin, lshift; 254239045Sneel 255239045Sneel assert(ioapic->inited); 256239045Sneel 257239045Sneel regnum = addr & 0xff; 258239045Sneel switch (regnum) { 259239045Sneel case IOAPIC_ID: 260239045Sneel ioapic->id = data & APIC_ID_MASK; 261239045Sneel break; 262239045Sneel case IOAPIC_VER: 263239045Sneel case IOAPIC_ARB: 264239045Sneel /* readonly */ 265239045Sneel break; 266239045Sneel default: 267239045Sneel break; 268239045Sneel } 269239045Sneel 270239045Sneel /* redirection table entries */ 271239045Sneel if (regnum >= IOAPIC_REDTBL && 272239045Sneel regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 273239045Sneel pin = (regnum - IOAPIC_REDTBL) / 2; 274239045Sneel if ((regnum - IOAPIC_REDTBL) % 2) 275239045Sneel lshift = 32; 276239045Sneel else 277239045Sneel lshift = 0; 278239045Sneel 279257397Sgrehan ioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift); 280257397Sgrehan ioapic->rtbl[pin].reg |= ((uint64_t)data << lshift); 281257397Sgrehan 282257397Sgrehan if (ioapic->rtbl[pin].pending && 283257397Sgrehan ((ioapic->rtbl[pin].reg & IOART_INTMASK) == 284257397Sgrehan IOART_INTMCLR)) { 285257397Sgrehan ioapic->rtbl[pin].pending = false; 286257397Sgrehan ioapic_clearpend++; 287257397Sgrehan /* 288257397Sgrehan * Inject the deferred level-triggered int if it is 289257397Sgrehan * still asserted. Simulate by toggling the pin 290257397Sgrehan * off and then on. 291257397Sgrehan */ 292257397Sgrehan if (ioapic->rtbl[pin].pinstate == true) { 293257397Sgrehan ioapic_togglepend++; 294257397Sgrehan ioapic_set_pinstate(vm, pin, false); 295257397Sgrehan ioapic_set_pinstate(vm, pin, true); 296257397Sgrehan } 297257397Sgrehan } 298239045Sneel } 299239045Sneel} 300239045Sneel 301239045Sneelstatic int 302257397Sgrehanioapic_region_read(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr, 303257397Sgrehan int size, uint64_t *data) 304239045Sneel{ 305241744Sgrehan int offset; 306239045Sneel 307239045Sneel offset = paddr - ioapic->paddr; 308239045Sneel 309239045Sneel /* 310239045Sneel * The IOAPIC specification allows 32-bit wide accesses to the 311239045Sneel * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 312239045Sneel */ 313239045Sneel if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 314239045Sneel#if 1 315239045Sneel printf("invalid access to ioapic%d: size %d, offset %d\n", 316241744Sgrehan (int)(ioapic - ioapics), size, offset); 317239045Sneel#endif 318239045Sneel *data = 0; 319239045Sneel return (0); 320239045Sneel } 321239045Sneel 322239045Sneel if (offset == IOREGSEL) 323239045Sneel *data = ioapic->ioregsel; 324239045Sneel else 325239045Sneel *data = ioapic_read(ioapic, ioapic->ioregsel); 326239045Sneel 327239045Sneel return (0); 328239045Sneel} 329239045Sneel 330239045Sneelstatic int 331257397Sgrehanioapic_region_write(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr, 332257397Sgrehan int size, uint64_t data) 333239045Sneel{ 334241744Sgrehan int offset; 335239045Sneel 336239045Sneel offset = paddr - ioapic->paddr; 337239045Sneel 338239045Sneel /* 339239045Sneel * The ioapic specification allows 32-bit wide accesses to the 340239045Sneel * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 341239045Sneel */ 342239045Sneel if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 343239045Sneel#if 1 344239045Sneel printf("invalid access to ioapic%d: size %d, offset %d\n", 345241744Sgrehan (int)(ioapic - ioapics), size, offset); 346239045Sneel#endif 347239045Sneel return (0); 348239045Sneel } 349239045Sneel 350239045Sneel if (offset == IOREGSEL) 351239045Sneel ioapic->ioregsel = data; 352239045Sneel else 353257397Sgrehan ioapic_write(vm, ioapic, ioapic->ioregsel, data); 354239045Sneel 355239045Sneel return (0); 356239045Sneel} 357241744Sgrehan 358241744Sgrehanstatic int 359241744Sgrehanioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr, 360257397Sgrehan int size, uint64_t *val, void *arg1, long arg2) 361241744Sgrehan{ 362241744Sgrehan struct ioapic *ioapic; 363241744Sgrehan int which; 364241744Sgrehan 365241744Sgrehan ioapic = arg1; 366241744Sgrehan which = arg2; 367241744Sgrehan 368241744Sgrehan assert(ioapic == &ioapics[which]); 369241744Sgrehan 370257397Sgrehan pthread_mutex_lock(&ioapic->mtx); 371241744Sgrehan if (dir == MEM_F_READ) 372257397Sgrehan ioapic_region_read(vm, ioapic, paddr, size, val); 373241744Sgrehan else 374257397Sgrehan ioapic_region_write(vm, ioapic, paddr, size, *val); 375257397Sgrehan pthread_mutex_unlock(&ioapic->mtx); 376241744Sgrehan 377241744Sgrehan return (0); 378241744Sgrehan} 379