ioapic.c revision 241744
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: projects/bhyve/usr.sbin/bhyve/ioapic.c 241744 2012-10-19 18:11:17Z grehan $ 27239045Sneel */ 28239045Sneel 29239045Sneel#include <sys/cdefs.h> 30239045Sneel__FBSDID("$FreeBSD: projects/bhyve/usr.sbin/bhyve/ioapic.c 241744 2012-10-19 18:11:17Z grehan $"); 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> 40239045Sneel 41239045Sneel#include <vmmapi.h> 42239045Sneel 43239045Sneel#include "inout.h" 44241744Sgrehan#include "mem.h" 45239045Sneel#include "instruction_emul.h" 46239045Sneel#include "fbsdrun.h" 47239045Sneel 48239045Sneel#include <stdio.h> 49239045Sneel 50239045Sneel#define IOAPIC_PADDR 0xFEC00000 51239045Sneel 52239045Sneel#define IOREGSEL 0x00 53239045Sneel#define IOWIN 0x10 54239045Sneel 55239045Sneel#define REDIR_ENTRIES 16 56239045Sneel#define INTR_ASSERTED(ioapic, pin) ((ioapic)->pinstate[(pin)] == true) 57239045Sneel 58239045Sneelstruct ioapic { 59239045Sneel int inited; 60239045Sneel uint32_t id; 61239045Sneel uint64_t redtbl[REDIR_ENTRIES]; 62239045Sneel bool pinstate[REDIR_ENTRIES]; 63239045Sneel 64239045Sneel uintptr_t paddr; /* gpa where the ioapic is mapped */ 65239045Sneel uint32_t ioregsel; 66239045Sneel struct memory_region *region; 67239045Sneel}; 68239045Sneel 69239045Sneelstatic struct ioapic ioapics[1]; /* only a single ioapic for now */ 70239045Sneel 71241744Sgrehanstatic int ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr, 72241744Sgrehan int size, uint64_t *data); 73241744Sgrehanstatic int ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr, 74241744Sgrehan int size, uint64_t data); 75241744Sgrehanstatic int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir, 76241744Sgrehan uintptr_t paddr, int size, uint64_t *val, 77241744Sgrehan void *arg1, long arg2); 78239045Sneel 79239045Sneelstatic void 80239045Sneelioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate) 81239045Sneel{ 82239045Sneel int vector, apicid, vcpu; 83239045Sneel uint32_t low, high; 84239045Sneel struct ioapic *ioapic; 85239045Sneel 86239045Sneel ioapic = &ioapics[0]; /* assume a single ioapic */ 87239045Sneel 88239045Sneel if (pin < 0 || pin >= REDIR_ENTRIES) 89239045Sneel return; 90239045Sneel 91239045Sneel /* Nothing to do if interrupt pin has not changed state */ 92239045Sneel if (ioapic->pinstate[pin] == newstate) 93239045Sneel return; 94239045Sneel 95239045Sneel ioapic->pinstate[pin] = newstate; /* record it */ 96239045Sneel 97239045Sneel /* Nothing to do if interrupt pin is deasserted */ 98239045Sneel if (!INTR_ASSERTED(ioapic, pin)) 99239045Sneel return; 100239045Sneel 101239045Sneel /* 102239045Sneel * XXX 103239045Sneel * We only deal with: 104239045Sneel * - edge triggered interrupts 105239045Sneel * - physical destination mode 106239045Sneel * - fixed delivery mode 107239045Sneel */ 108239045Sneel low = ioapic->redtbl[pin]; 109239045Sneel high = ioapic->redtbl[pin] >> 32; 110239045Sneel if ((low & IOART_INTMASK) == IOART_INTMCLR && 111239045Sneel (low & IOART_TRGRMOD) == IOART_TRGREDG && 112239045Sneel (low & IOART_DESTMOD) == IOART_DESTPHY && 113239045Sneel (low & IOART_DELMOD) == IOART_DELFIXED) { 114239045Sneel vector = low & IOART_INTVEC; 115239045Sneel apicid = high >> APIC_ID_SHIFT; 116239045Sneel if (apicid != 0xff) { 117239045Sneel /* unicast */ 118239045Sneel vcpu = vm_apicid2vcpu(ctx, apicid); 119239045Sneel vm_lapic_irq(ctx, vcpu, vector); 120239045Sneel } else { 121239045Sneel /* broadcast */ 122239045Sneel vcpu = 0; 123239045Sneel while (vcpu < guest_ncpus) { 124239045Sneel vm_lapic_irq(ctx, vcpu, vector); 125239045Sneel vcpu++; 126239045Sneel } 127239045Sneel } 128239045Sneel } 129239045Sneel} 130239045Sneel 131239045Sneelvoid 132239045Sneelioapic_deassert_pin(struct vmctx *ctx, int pin) 133239045Sneel{ 134239045Sneel ioapic_set_pinstate(ctx, pin, false); 135239045Sneel} 136239045Sneel 137239045Sneelvoid 138239045Sneelioapic_assert_pin(struct vmctx *ctx, int pin) 139239045Sneel{ 140239045Sneel ioapic_set_pinstate(ctx, pin, true); 141239045Sneel} 142239045Sneel 143239045Sneelvoid 144239045Sneelioapic_init(int which) 145239045Sneel{ 146241744Sgrehan struct mem_range memp; 147241744Sgrehan struct ioapic *ioapic; 148241744Sgrehan int error; 149239045Sneel int i; 150239045Sneel 151239045Sneel assert(which == 0); 152239045Sneel 153239045Sneel ioapic = &ioapics[which]; 154239045Sneel assert(ioapic->inited == 0); 155239045Sneel 156239045Sneel bzero(ioapic, sizeof(struct ioapic)); 157239045Sneel 158239045Sneel /* Initialize all redirection entries to mask all interrupts */ 159239045Sneel for (i = 0; i < REDIR_ENTRIES; i++) 160239045Sneel ioapic->redtbl[i] = 0x0001000000010000UL; 161239045Sneel 162239045Sneel ioapic->paddr = IOAPIC_PADDR; 163239045Sneel 164241744Sgrehan /* Register emulated memory region */ 165241744Sgrehan memp.name = "ioapic"; 166241744Sgrehan memp.flags = MEM_F_RW; 167241744Sgrehan memp.handler = ioapic_region_handler; 168241744Sgrehan memp.arg1 = ioapic; 169241744Sgrehan memp.arg2 = which; 170241744Sgrehan memp.base = ioapic->paddr; 171241744Sgrehan memp.size = sizeof(struct IOAPIC); 172241744Sgrehan error = register_mem(&memp); 173241744Sgrehan 174241744Sgrehan assert (error == 0); 175241744Sgrehan 176239045Sneel ioapic->inited = 1; 177239045Sneel} 178239045Sneel 179239045Sneelstatic uint32_t 180239045Sneelioapic_read(struct ioapic *ioapic, uint32_t addr) 181239045Sneel{ 182239045Sneel int regnum, pin, rshift; 183239045Sneel 184239045Sneel assert(ioapic->inited); 185239045Sneel 186239045Sneel regnum = addr & 0xff; 187239045Sneel switch (regnum) { 188239045Sneel case IOAPIC_ID: 189239045Sneel return (ioapic->id); 190239045Sneel break; 191239045Sneel case IOAPIC_VER: 192239045Sneel return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11); 193239045Sneel break; 194239045Sneel case IOAPIC_ARB: 195239045Sneel return (ioapic->id); 196239045Sneel break; 197239045Sneel default: 198239045Sneel break; 199239045Sneel } 200239045Sneel 201239045Sneel /* redirection table entries */ 202239045Sneel if (regnum >= IOAPIC_REDTBL && 203239045Sneel regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 204239045Sneel pin = (regnum - IOAPIC_REDTBL) / 2; 205239045Sneel if ((regnum - IOAPIC_REDTBL) % 2) 206239045Sneel rshift = 32; 207239045Sneel else 208239045Sneel rshift = 0; 209239045Sneel 210239045Sneel return (ioapic->redtbl[pin] >> rshift); 211239045Sneel } 212239045Sneel 213239045Sneel return (0); 214239045Sneel} 215239045Sneel 216239045Sneelstatic void 217239045Sneelioapic_write(struct ioapic *ioapic, uint32_t addr, uint32_t data) 218239045Sneel{ 219239045Sneel int regnum, pin, lshift; 220239045Sneel 221239045Sneel assert(ioapic->inited); 222239045Sneel 223239045Sneel regnum = addr & 0xff; 224239045Sneel switch (regnum) { 225239045Sneel case IOAPIC_ID: 226239045Sneel ioapic->id = data & APIC_ID_MASK; 227239045Sneel break; 228239045Sneel case IOAPIC_VER: 229239045Sneel case IOAPIC_ARB: 230239045Sneel /* readonly */ 231239045Sneel break; 232239045Sneel default: 233239045Sneel break; 234239045Sneel } 235239045Sneel 236239045Sneel /* redirection table entries */ 237239045Sneel if (regnum >= IOAPIC_REDTBL && 238239045Sneel regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) { 239239045Sneel pin = (regnum - IOAPIC_REDTBL) / 2; 240239045Sneel if ((regnum - IOAPIC_REDTBL) % 2) 241239045Sneel lshift = 32; 242239045Sneel else 243239045Sneel lshift = 0; 244239045Sneel 245239045Sneel ioapic->redtbl[pin] &= ~((uint64_t)0xffffffff << lshift); 246239045Sneel ioapic->redtbl[pin] |= ((uint64_t)data << lshift); 247239045Sneel } 248239045Sneel} 249239045Sneel 250239045Sneelstatic int 251241744Sgrehanioapic_region_read(struct ioapic *ioapic, uintptr_t paddr, int size, 252241744Sgrehan uint64_t *data) 253239045Sneel{ 254241744Sgrehan int offset; 255239045Sneel 256239045Sneel offset = paddr - ioapic->paddr; 257239045Sneel 258239045Sneel /* 259239045Sneel * The IOAPIC specification allows 32-bit wide accesses to the 260239045Sneel * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 261239045Sneel */ 262239045Sneel if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 263239045Sneel#if 1 264239045Sneel printf("invalid access to ioapic%d: size %d, offset %d\n", 265241744Sgrehan (int)(ioapic - ioapics), size, offset); 266239045Sneel#endif 267239045Sneel *data = 0; 268239045Sneel return (0); 269239045Sneel } 270239045Sneel 271239045Sneel if (offset == IOREGSEL) 272239045Sneel *data = ioapic->ioregsel; 273239045Sneel else 274239045Sneel *data = ioapic_read(ioapic, ioapic->ioregsel); 275239045Sneel 276239045Sneel return (0); 277239045Sneel} 278239045Sneel 279239045Sneelstatic int 280241744Sgrehanioapic_region_write(struct ioapic *ioapic, uintptr_t paddr, int size, 281241744Sgrehan uint64_t data) 282239045Sneel{ 283241744Sgrehan int offset; 284239045Sneel 285239045Sneel offset = paddr - ioapic->paddr; 286239045Sneel 287239045Sneel /* 288239045Sneel * The ioapic specification allows 32-bit wide accesses to the 289239045Sneel * IOREGSEL (offset 0) and IOWIN (offset 16) registers. 290239045Sneel */ 291239045Sneel if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) { 292239045Sneel#if 1 293239045Sneel printf("invalid access to ioapic%d: size %d, offset %d\n", 294241744Sgrehan (int)(ioapic - ioapics), size, offset); 295239045Sneel#endif 296239045Sneel return (0); 297239045Sneel } 298239045Sneel 299239045Sneel if (offset == IOREGSEL) 300239045Sneel ioapic->ioregsel = data; 301239045Sneel else 302239045Sneel ioapic_write(ioapic, ioapic->ioregsel, data); 303239045Sneel 304239045Sneel return (0); 305239045Sneel} 306241744Sgrehan 307241744Sgrehanstatic int 308241744Sgrehanioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr, 309241744Sgrehan int size, uint64_t *val, void *arg1, long arg2) 310241744Sgrehan{ 311241744Sgrehan struct ioapic *ioapic; 312241744Sgrehan int which; 313241744Sgrehan 314241744Sgrehan ioapic = arg1; 315241744Sgrehan which = arg2; 316241744Sgrehan 317241744Sgrehan assert(ioapic == &ioapics[which]); 318241744Sgrehan 319241744Sgrehan if (dir == MEM_F_READ) 320241744Sgrehan ioapic_region_read(ioapic, paddr, size, val); 321241744Sgrehan else 322241744Sgrehan ioapic_region_write(ioapic, paddr, size, *val); 323241744Sgrehan 324241744Sgrehan return (0); 325241744Sgrehan} 326