blob: 4f86ea0e89ed46f171b5a6dd12042d0add55efed [file] [log] [blame]
/*
* IOAPIC emulation
*
* Copyright 2015 Google Inc.
*
* See LICENSE for details.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <parlib/arch/arch.h>
#include <parlib/ros_debug.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <stdint.h>
#include <err.h>
#include <sys/mman.h>
#include <vmm/vmm.h>
#include <vmm/virtio.h>
#include <vmm/virtio_mmio.h>
#include <vmm/virtio_ids.h>
#include <vmm/virtio_config.h>
#include <vmm/sched.h>
#define IOAPIC_CONFIG 0x100
#define IOAPIC_NUM_PINS 24
int debug_ioapic;
int apic_id_mask = 0xf0;
#define DPRINTF(fmt, ...) \
if (debug_ioapic) { fprintf(stderr, "ioapic: " fmt , ## __VA_ARGS__); }
struct ioapic {
int id;
int reg;
uint32_t arbid;
uint32_t value[256];
};
static struct ioapic ioapic[1];
static uint32_t ioapic_read(struct guest_thread *vm_thread, int ix,
uint64_t offset)
{
uint32_t ret = (uint32_t)-1;
uint32_t reg = ioapic[ix].reg;
if (offset == 0) {
DPRINTF("ioapic_read ix %x return 0x%x\n", ix, reg);
return reg;
}
DPRINTF("ioapic_read %x 0x%x\n", ix, (int)reg);
switch (reg) {
case 0:
return ioapic[ix].id;
case 1:
return 0x170011;
case 2:
return ioapic[ix].arbid;
default:
if (reg >= 0 && reg < (IOAPIC_NUM_PINS*2 + 0x10)) {
//bx_io_redirect_entry_t *entry = ioredtbl + index;
//data = (ioregsel&1) ? entry->get_hi_part()
// : entry->get_lo_part();
ret = ioapic[ix].value[reg];
DPRINTF("IOAPIC_READ %x: %x return %08x\n", ix, reg,
ret);
return ret;
} else {
DPRINTF("IOAPIC READ: %x BAD INDEX 0x%x\n", ix, reg);
}
return ret;
}
return 0;
}
static void ioapic_write(struct guest_thread *vm_thread, int ix,
uint64_t offset, uint32_t value)
{
uint32_t ret;
uint32_t reg = ioapic[ix].reg;
struct virtual_machine *vm = gth_to_vm(vm_thread);
uint32_t irqreg;
if (offset == 0) {
DPRINTF("ioapic_write ix %x set reg 0x%x\n", ix, value);
ioapic[ix].reg = value;
return;
}
for (int i = 0; i < VIRTIO_MMIO_MAX_NUM_DEV; i++) {
if (vm->virtio_mmio_devices[i] == NULL)
continue;
/* The first IRQ register starts at 0x10, and there are two
* 32-bit registers for each IRQ. The first 8 bits of the value
* assigned to 'reg' is the interrupt vector. */
irqreg = (vm->virtio_mmio_devices[i]->irq) * 2 + 0x10;
if (reg == irqreg && (value & 0xff) != 0) {
vm->virtio_mmio_devices[i]->vec = value & 0xff;
DPRINTF("irq vector for irq number %d is: %lx\n",
vm->virtio_mmio_devices[i]->irq, value & 0xff);
} else if (reg == irqreg + 1) {
vm->virtio_mmio_devices[i]->dest = value >> 24;
if (value >> 24 == 0xff)
vm->virtio_mmio_devices[i]->dest = 0xffffffff;
DPRINTF("high value for irq number %d is: %lx\n",
vm->virtio_mmio_devices[i]->irq, value);
DPRINTF("irq destination for irq number %d is: %lx\n",
vm->virtio_mmio_devices[i]->irq, value >> 24);
}
}
switch (reg) {
case 0:
DPRINTF("IOAPIC_WRITE: Set %d ID to %d\n", ix, value);
ioapic[ix].id = value;
break;
case 1:
case 2:
DPRINTF("IOAPIC_WRITE: Can't write %d\n", reg);
default:
if (reg >= 0 && reg < (IOAPIC_NUM_PINS*2 + 0x10)) {
ioapic[ix].value[reg] = value;
DPRINTF("IOAPIC %x: set %08x to %016x\n", ix, reg,
value);
} else {
DPRINTF("IOAPIC WRITE: %x BAD INDEX 0x%x\n", ix, reg);
}
break;
}
}
int do_ioapic(struct guest_thread *vm_thread, uint64_t gpa, int destreg,
uint64_t *regp, int store)
{
/* TODO(ganshun): compute an index for the ioapic array. */
int ix = 0;
uint32_t offset = gpa & 0xfffff;
/* basic sanity tests. */
DPRINTF("%s: %p 0x%x %p %s\n", __func__, (void *)gpa, destreg, regp,
store ? "write" : "read");
if ((offset != 0) && (offset != 0x10)) {
DPRINTF("Bad register offset: 0x%x and has to be 0x0 or 0x10\n",
offset);
return -1;
}
if (store) {
ioapic_write(vm_thread, ix, offset, *regp);
} else {
*regp = ioapic_read(vm_thread, ix, offset);
}
return 0;
}