VMM: Dynamically retrieve the interrupt vector for a virtio device.
authorKyle Milka <kmilka1995@gmail.com>
Fri, 17 Jun 2016 15:57:35 +0000 (08:57 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 20 Jun 2016 14:29:36 +0000 (10:29 -0400)
We dynamically get the interrupt vector from ioapic_write to the
relevant irq in virtio_console. The poke_guest function now posts
the interrupt vector for a specific device.

We now no longer need Ron's vroom patch for linux.

Fixes: b/29243001
Change-Id: I222f33582c013dc730412d28881a184d0ae31fb1
Signed-off-by: Kyle Milka <kmilka@google.com>
[ masked 'value' in DPRINTF ]
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tests/vmm/vmrunkernel.c
user/vmm/include/vmm/virtio_mmio.h
user/vmm/ioapic.c
user/vmm/virtio_lguest_console.c
user/vmm/virtio_mmio.c

index 9074f83..0a928b8 100644 (file)
@@ -97,7 +97,7 @@ struct acpi_table_madt madt = {
 struct acpi_madt_local_apic Apic0 = {.header = {.type = ACPI_MADT_TYPE_LOCAL_APIC, .length = sizeof(struct acpi_madt_local_apic)},
                                     .processor_id = 0, .id = 0};
 struct acpi_madt_io_apic Apic1 = {.header = {.type = ACPI_MADT_TYPE_IO_APIC, .length = sizeof(struct acpi_madt_io_apic)},
-                                 .id = 1, .address = 0xfec00000, .global_irq_base = 0};
+                                 .id = 0, .address = 0xfec00000, .global_irq_base = 0};
 struct acpi_madt_local_x2apic X2Apic0 = {
        .header = {
                .type = ACPI_MADT_TYPE_LOCAL_X2APIC,
@@ -108,42 +108,24 @@ struct acpi_madt_local_x2apic X2Apic0 = {
 };
 
 struct acpi_madt_interrupt_override isor[] = {
-       /* I have no idea if it should be source irq 2, global 0, or global 2, source 0. Shit. */
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 2, .global_irq = 0, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
+       /* From the ACPI Specification Version 6.1:
+        * For example, if your machine has the ISA Programmable Interrupt Timer
+        * (PIT) connected to ISA IRQ 0, but in APIC mode, it is connected to I/O
+        * APIC interrupt input 2, then you would need an Interrupt Source Override
+        * where the source entry is ‘0’ and the Global System Interrupt is ‘2.’
+        */
+       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE,
+                   .length = sizeof(struct acpi_madt_interrupt_override)},
+        .bus = 0, .source_irq = 0, .global_irq = 2, .inti_flags = 0},
+       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE,
+                   .length = sizeof(struct acpi_madt_interrupt_override)},
         .bus = 0, .source_irq = 1, .global_irq = 1, .inti_flags = 0},
-       //{.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        //.bus = 0, .source_irq = 2, .global_irq = 2, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
+       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE,
+                   .length = sizeof(struct acpi_madt_interrupt_override)},
         .bus = 0, .source_irq = 3, .global_irq = 3, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
+       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE,
+                   .length = sizeof(struct acpi_madt_interrupt_override)},
         .bus = 0, .source_irq = 4, .global_irq = 4, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 5, .global_irq = 5, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 6, .global_irq = 6, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 7, .global_irq = 7, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 8, .global_irq = 8, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 9, .global_irq = 9, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 10, .global_irq = 10, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 11, .global_irq = 11, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 12, .global_irq = 12, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 13, .global_irq = 13, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 14, .global_irq = 14, .inti_flags = 0},
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 15, .global_irq = 15, .inti_flags = 0},
-       // VMMCP routes irq 32 to gsi 17
-       {.header = {.type = ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, .length = sizeof(struct acpi_madt_interrupt_override)},
-        .bus = 0, .source_irq = 32, .global_irq = 17, .inti_flags = 5},
 };
 
 
@@ -206,14 +188,17 @@ void timer_thread(void *arg)
 // FIXME.
 volatile int consdata = 0;
 
-static void virtio_poke_guest(void)
+static void virtio_poke_guest(uint8_t vec)
 {
-       set_posted_interrupt(0xE5);
+       set_posted_interrupt(vec);
        ros_syscall(SYS_vmm_poke_guest, 0, 0, 0, 0, 0, 0);
 }
 
 static struct virtio_mmio_dev cons_mmio_dev = {
-       .poke_guest = virtio_poke_guest
+       .poke_guest = virtio_poke_guest,
+       /* At the moment irq numbers cannot be below 24; this is a problem with
+        * the IOAPIC and Interrupt Source Override Structure. */
+       .irq = 26,
 };
 
 static struct virtio_console_config cons_cfg;
@@ -246,7 +231,6 @@ static struct virtio_vq_dev cons_vqdev = {
                }
 };
 
-
 void lowmem() {
        __asm__ __volatile__ (".section .lowmem, \"aw\"\n\tlow: \n\t.=0x1000\n\t.align 0x100000\n\t.previous\n");
 }
@@ -613,8 +597,10 @@ int main(int argc, char **argv)
                if (vm->virtio_mmio_devices[i] == NULL)
                        continue;
                /* Append all the virtio mmio base addresses. */
-               len = snprintf(cmdlinep, cmdlinesz, " virtio_mmio.device=1K@0x%llx:32",
-                              vm->virtio_mmio_devices[i]->addr);
+               len = snprintf(cmdlinep, cmdlinesz,
+                              " virtio_mmio.device=1K@0x%llx:%lld",
+                              vm->virtio_mmio_devices[i]->addr,
+                              vm->virtio_mmio_devices[i]->irq);
                if (len >= cmdlinesz) {
                        fprintf(stderr, "Too many arguments to the linux command line.");
                        exit(1);
index f0b8979..54471da 100644 (file)
@@ -201,7 +201,7 @@ struct virtio_mmio_dev {
 
        // This utility function will be called when the device needs to interrupt
        // the guest. You can have it do whatever you want, but it is required.
-       void (*poke_guest)(void);
+       void (*poke_guest)(uint8_t vec);
 
        // Status flags for the device
        uint8_t status;
@@ -212,6 +212,12 @@ struct virtio_mmio_dev {
 
        // The generic vq device contained by this mmio transport
        struct virtio_vq_dev *vqdev;
+
+       // The specific irq number for this device.
+       uint64_t irq;
+
+       // Actual vector the device will inject.
+       uint8_t vec;
 };
 
 // Sets the VIRTIO_MMIO_INT_VRING bit in the interrupt status
index 00c9819..3f6d58c 100644 (file)
 #define IOAPIC_CONFIG 0x100
 #define IOAPIC_NUM_PINS 24
 
+/* The problem with the IOAPIC and ISOR Structure means all irq numbers
+ * must be at least 24, so this is used to correct that offset. */
+#define IOAPIC_LAST_IRQ 23
+
 int debug_ioapic = 1;
 int apic_id_mask = 0xf0;
 
@@ -47,12 +51,12 @@ struct ioapic {
 
 static struct ioapic ioapic[1];
 
-static uint32_t ioapic_read(int ix, uint64_t offset)
+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;
@@ -85,10 +89,13 @@ static uint32_t ioapic_read(int ix, uint64_t offset)
        return 0;
 }
 
-static void ioapic_write(int ix, uint64_t offset, uint32_t value)
+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);
@@ -96,6 +103,21 @@ static void ioapic_write(int ix, uint64_t offset, uint32_t 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 - IOAPIC_LAST_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);
+               }
+       }
+
        switch (reg) {
        case 0:
                DPRINTF("IOAPIC_WRITE: Set %d ID to %d\n", ix, value);
@@ -119,7 +141,7 @@ static void ioapic_write(int ix, uint64_t offset, uint32_t value)
 int do_ioapic(struct guest_thread *vm_thread, uint64_t gpa, int destreg,
               uint64_t *regp, int store)
 {
-       // TODO: compute an index for the ioapic array. 
+       /* TODO(ganshun): compute an index for the ioapic array. */
        int ix = 0;
        uint32_t offset = gpa & 0xfffff;
        /* basic sanity tests. */
@@ -131,9 +153,9 @@ int do_ioapic(struct guest_thread *vm_thread, uint64_t gpa, int destreg,
        }
 
        if (store) {
-               ioapic_write(ix, offset, *regp);
+               ioapic_write(vm_thread, ix, offset, *regp);
        } else {
-               *regp = ioapic_read(ix, offset);
+               *regp = ioapic_read(vm_thread, ix, offset);
        }
 
 }
index 0303a0f..ffc0787 100644 (file)
@@ -40,6 +40,7 @@ void cons_receiveq_fn(void *_vq) // host -> guest
        uint32_t i, j;
        int num_read;
        struct iovec *iov;
+       struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
 
        if (!vq)
                errx(1,
@@ -85,10 +86,10 @@ void cons_receiveq_fn(void *_vq) // host -> guest
                virtio_add_used_desc(vq, head, num_read);
 
                // Poke the guest however the mmio transport prefers
-               // NOTE: assuming that the mmio transport was used for now
-               virtio_mmio_set_vring_irq(vq->vqdev->transport_dev);
-               if (((struct virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest)
-                       ((struct virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest();
+               // NOTE: assuming that the mmio transport was used for now.
+               virtio_mmio_set_vring_irq(dev);
+               if (dev->poke_guest)
+                       dev->poke_guest(dev->vec);
                else
                        VIRTIO_DEV_ERRX(vq->vqdev,
                                "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
@@ -103,6 +104,7 @@ void cons_transmitq_fn(void *_vq) // guest -> host
        uint32_t olen, ilen;
        uint32_t i, j;
        struct iovec *iov;
+       struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
 
        if (!vq)
                errx(1,
@@ -146,9 +148,9 @@ void cons_transmitq_fn(void *_vq) // guest -> host
 
                // Poke the guest however the mmio transport prefers
                // NOTE: assuming that the mmio transport was used for now
-               virtio_mmio_set_vring_irq(vq->vqdev->transport_dev);
-               if (((struct virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest)
-                       ((struct virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest();
+               virtio_mmio_set_vring_irq(dev);
+               if (dev->poke_guest)
+                       dev->poke_guest(dev->vec);
                else
                        VIRTIO_DEV_ERRX(vq->vqdev,
                                "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
index 5cef438..9be52fe 100644 (file)
@@ -451,7 +451,7 @@ void virtio_mmio_wr(struct virtual_machine *vm,
                // Notify the driver that the device-specific config changed
                virtio_mmio_set_cfg_irq(mmio_dev);
                if (mmio_dev->poke_guest)
-                       mmio_dev->poke_guest();
+                       mmio_dev->poke_guest(mmio_dev->vec);
 
                return;
        }