VMM: Dynamically retrieve the interrupt vector for a virtio device.
[akaros.git] / user / vmm / ioapic.c
1 /*
2  * IOAPIC emulation
3  *
4  * Copyright 2015 Google Inc.
5  *
6  * See LICENSE for details.
7  */
8
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <pthread.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <parlib/arch/arch.h>
15 #include <parlib/ros_debug.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/uio.h>
21 #include <stdint.h>
22 #include <err.h>
23 #include <sys/mman.h>
24 #include <vmm/vmm.h>
25 #include <vmm/virtio.h>
26 #include <vmm/virtio_mmio.h>
27 #include <vmm/virtio_ids.h>
28 #include <vmm/virtio_config.h>
29 #include <vmm/sched.h>
30
31 #define IOAPIC_CONFIG 0x100
32 #define IOAPIC_NUM_PINS 24
33
34 /* The problem with the IOAPIC and ISOR Structure means all irq numbers
35  * must be at least 24, so this is used to correct that offset. */
36 #define IOAPIC_LAST_IRQ 23
37
38 int debug_ioapic = 1;
39 int apic_id_mask = 0xf0;
40
41 #define DPRINTF(fmt, ...) \
42         if (debug_ioapic) { fprintf(stderr, "ioapic: " fmt , ## __VA_ARGS__); }
43
44
45 struct ioapic {
46         int id;
47         int reg;
48         uint32_t arbid;
49         uint32_t value[256];
50 };
51
52 static struct ioapic ioapic[1];
53
54 static uint32_t ioapic_read(struct guest_thread *vm_thread, int ix,
55                             uint64_t offset)
56 {
57         uint32_t ret = (uint32_t)-1;
58         uint32_t reg = ioapic[ix].reg;
59
60         if (offset == 0) {
61                 DPRINTF("ioapic_read ix %x return 0x%x\n", ix, reg);
62                 return reg;
63         }
64
65         DPRINTF("ioapic_read %x 0x%x\n", ix, (int)reg);
66         switch (reg) {
67         case 0:
68                 return ioapic[ix].id;
69                 break;
70         case 1:
71                 return 0x170011;
72                 break;
73         case 2:
74                 return ioapic[ix].arbid;
75                 break;
76         default:
77                 if (reg >= 0 && reg < (IOAPIC_NUM_PINS*2 + 0x10)) {
78                         //bx_io_redirect_entry_t *entry = ioredtbl + index;
79                         //data = (ioregsel&1) ? entry->get_hi_part() : entry->get_lo_part();
80                         ret = ioapic[ix].value[reg];
81                         DPRINTF("IOAPIC_READ %x: %x return %08x\n", ix, reg, ret);
82                         return ret;
83                 } else {
84                         DPRINTF("IOAPIC READ: %x BAD INDEX 0x%x\n", ix, reg);
85                 }
86                 return ret;
87                 break;
88         }
89         return 0;
90 }
91
92 static void ioapic_write(struct guest_thread *vm_thread, int ix,
93                          uint64_t offset, uint32_t value)
94 {
95         uint32_t ret;
96         uint32_t reg = ioapic[ix].reg;
97         struct virtual_machine *vm = gth_to_vm(vm_thread);
98         uint32_t irqreg;
99
100         if (offset == 0) {
101                 DPRINTF("ioapic_write ix %x set reg 0x%x\n", ix, value);
102                 ioapic[ix].reg = value;
103                 return;
104         }
105
106         for (int i = 0; i < VIRTIO_MMIO_MAX_NUM_DEV; i++) {
107                 if (vm->virtio_mmio_devices[i] == NULL)
108                         continue;
109
110                 /* The first IRQ register starts at 0x10, and there are two 32-bit
111                  * registers for each IRQ. The first 8 bits of the value assigned to
112                  * 'reg' is the interrupt vector. */
113                 irqreg = (vm->virtio_mmio_devices[i]->irq - IOAPIC_LAST_IRQ) * 2 + 0x10;
114                 if (reg == irqreg && (value & 0xff) != 0) {
115                         vm->virtio_mmio_devices[i]->vec = value & 0xff;
116                         DPRINTF("irq vector for irq number %d is: %lx\n",
117                                  vm->virtio_mmio_devices[i]->irq, value & 0xff);
118                 }
119         }
120
121         switch (reg) {
122         case 0:
123                 DPRINTF("IOAPIC_WRITE: Set %d ID to %d\n", ix, value);
124                 ioapic[ix].id = value;
125                 break;
126         case 1:
127         case 2:
128                 DPRINTF("IOAPIC_WRITE: Can't write %d\n", reg);
129         default:
130                 if (reg >= 0 && reg < (IOAPIC_NUM_PINS*2 + 0x10)) {
131                         ioapic[ix].value[reg] = value;
132                         DPRINTF("IOAPIC %x: set %08x to %016x\n", ix, reg, value);
133                 } else {
134                         DPRINTF("IOAPIC WRITE: %x BAD INDEX 0x%x\n", ix, reg);
135                 }
136                 break;
137         }
138
139 }
140
141 int do_ioapic(struct guest_thread *vm_thread, uint64_t gpa, int destreg,
142               uint64_t *regp, int store)
143 {
144         /* TODO(ganshun): compute an index for the ioapic array. */
145         int ix = 0;
146         uint32_t offset = gpa & 0xfffff;
147         /* basic sanity tests. */
148         DPRINTF("%s: %p 0x%x %p %s\n", __func__, (void *)gpa, destreg, regp, store ? "write" : "read");
149
150         if ((offset != 0) && (offset != 0x10)) {
151                 DPRINTF("Bad register offset: 0x%x and has to be 0x0 or 0x10\n", offset);
152                 return -1;
153         }
154
155         if (store) {
156                 ioapic_write(vm_thread, ix, offset, *regp);
157         } else {
158                 *regp = ioapic_read(vm_thread, ix, offset);
159         }
160
161 }