| /* |
| * 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; |
| } |