Author: grehan
Date: Fri Oct 25 03:18:56 2013
New Revision: 257092
URL: http://svnweb.freebsd.org/changeset/base/257092

Log:
  Fix bug in the ioapic emulation for level-triggered interrupts,
  where a pin assertion while a source was masked would result in
  the interrupt being lost, with the symptom being a console hang.
  The condition is now recorded, and the interrupt generated when
  the source is unmasked.
  
  Discovered by:        OpenBSD 5.4 MP
  Reviewed by:  neel
  MFC after:    3 days

Modified:
  head/usr.sbin/bhyve/ioapic.c

Modified: head/usr.sbin/bhyve/ioapic.c
==============================================================================
--- head/usr.sbin/bhyve/ioapic.c        Fri Oct 25 02:55:04 2013        
(r257091)
+++ head/usr.sbin/bhyve/ioapic.c        Fri Oct 25 03:18:56 2013        
(r257092)
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <assert.h>
 #include <stdbool.h>
+#include <pthread.h>
 
 #include <vmmapi.h>
 
@@ -46,34 +47,40 @@ __FBSDID("$FreeBSD$");
 
 #include <stdio.h>
 
+static uint64_t ioapic_clearpend, ioapic_togglepend, ioapic_setpend;
+
 #define        IOAPIC_PADDR    0xFEC00000
 
 #define        IOREGSEL        0x00
 #define        IOWIN           0x10
 
 #define        REDIR_ENTRIES   16
-#define        INTR_ASSERTED(ioapic, pin)      ((ioapic)->pinstate[(pin)] == 
true)
+#define        INTR_ASSERTED(ioapic, pin)      \
+       ((ioapic)->rtbl[(pin)].pinstate == true)
 
 struct ioapic {
        int             inited;
        uint32_t        id;
-       uint64_t        redtbl[REDIR_ENTRIES];
-       bool            pinstate[REDIR_ENTRIES];
+       struct {
+               uint64_t reg;
+               bool     pinstate;
+               bool     pending;
+       } rtbl[REDIR_ENTRIES];
 
        uintptr_t       paddr;          /* gpa where the ioapic is mapped */
        uint32_t        ioregsel;
        struct memory_region *region;
+       pthread_mutex_t mtx;
 };
 
 static struct ioapic ioapics[1];       /* only a single ioapic for now */
 
-static int ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr,
-                               int size, uint64_t *data);
-static int ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr,
-                               int size, uint64_t data);
+static int ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic,
+    uintptr_t paddr, int size, uint64_t *data);
+static int ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic,
+    uintptr_t paddr, int size, uint64_t data);
 static int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir,
-                                uintptr_t paddr, int size, uint64_t *val,
-                                void *arg1, long arg2);
+    uintptr_t paddr, int size, uint64_t *val, void *arg1, long arg2);
 
 static void
 ioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate)
@@ -84,14 +91,11 @@ ioapic_set_pinstate(struct vmctx *ctx, i
        
        ioapic = &ioapics[0];           /* assume a single ioapic */
 
-       if (pin < 0 || pin >= REDIR_ENTRIES)
-               return;
-
        /* Nothing to do if interrupt pin has not changed state */
-       if (ioapic->pinstate[pin] == newstate)
+       if (ioapic->rtbl[pin].pinstate == newstate)
                return;
 
-       ioapic->pinstate[pin] = newstate;       /* record it */
+       ioapic->rtbl[pin].pinstate = newstate;  /* record it */
 
        /* Nothing to do if interrupt pin is deasserted */
        if (!INTR_ASSERTED(ioapic, pin))
@@ -105,8 +109,8 @@ ioapic_set_pinstate(struct vmctx *ctx, i
         *  Level-triggered sources will work so long as there is
         * no sharing.
         */
-       low = ioapic->redtbl[pin];
-       high = ioapic->redtbl[pin] >> 32;
+       low = ioapic->rtbl[pin].reg;
+       high = ioapic->rtbl[pin].reg >> 32;
        if ((low & IOART_INTMASK) == IOART_INTMCLR &&
            (low & IOART_DESTMOD) == IOART_DESTPHY &&
            (low & IOART_DELMOD) == IOART_DELFIXED) {
@@ -124,19 +128,47 @@ ioapic_set_pinstate(struct vmctx *ctx, i
                                vcpu++;
                        }
                }
+       } else if ((low & IOART_INTMASK) != IOART_INTMCLR &&
+                  low & IOART_TRGRLVL) {
+               /*
+                * For level-triggered interrupts that have been
+                * masked, set the pending bit so that an interrupt
+                * will be generated on unmask and if the level is
+                * still asserted
+                */
+               ioapic_setpend++;
+               ioapic->rtbl[pin].pending = true;
        }
 }
 
+static void
+ioapic_set_pinstate_locked(struct vmctx *ctx, int pin, bool newstate)
+{
+       struct ioapic *ioapic;
+
+       if (pin < 0 || pin >= REDIR_ENTRIES)
+               return;
+
+       ioapic = &ioapics[0];
+
+       pthread_mutex_lock(&ioapic->mtx);
+       ioapic_set_pinstate(ctx, pin, newstate);
+       pthread_mutex_unlock(&ioapic->mtx);
+}
+
+/*
+ * External entry points require locking
+ */
 void
 ioapic_deassert_pin(struct vmctx *ctx, int pin)
 {
-       ioapic_set_pinstate(ctx, pin, false);
+       ioapic_set_pinstate_locked(ctx, pin, false);
 }
 
 void
 ioapic_assert_pin(struct vmctx *ctx, int pin)
 {
-       ioapic_set_pinstate(ctx, pin, true);
+       ioapic_set_pinstate_locked(ctx, pin, true);
 }
 
 void
@@ -154,9 +186,11 @@ ioapic_init(int which)
 
        bzero(ioapic, sizeof(struct ioapic));
 
+       pthread_mutex_init(&ioapic->mtx, NULL);
+
        /* Initialize all redirection entries to mask all interrupts */
        for (i = 0; i < REDIR_ENTRIES; i++)
-               ioapic->redtbl[i] = 0x0001000000010000UL;
+               ioapic->rtbl[i].reg = 0x0001000000010000UL;
 
        ioapic->paddr = IOAPIC_PADDR;
 
@@ -206,14 +240,15 @@ ioapic_read(struct ioapic *ioapic, uint3
                else
                        rshift = 0;
 
-               return (ioapic->redtbl[pin] >> rshift);
+               return (ioapic->rtbl[pin].reg >> rshift);
        }
 
        return (0);
 }
 
 static void
-ioapic_write(struct ioapic *ioapic, uint32_t addr, uint32_t data)
+ioapic_write(struct vmctx *vm, struct ioapic *ioapic, uint32_t addr,
+    uint32_t data)
 {
        int regnum, pin, lshift;
 
@@ -241,14 +276,31 @@ ioapic_write(struct ioapic *ioapic, uint
                else
                        lshift = 0;
 
-               ioapic->redtbl[pin] &= ~((uint64_t)0xffffffff << lshift);
-               ioapic->redtbl[pin] |= ((uint64_t)data << lshift);
+               ioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
+               ioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
+       
+               if (ioapic->rtbl[pin].pending &&
+                   ((ioapic->rtbl[pin].reg & IOART_INTMASK) ==
+                        IOART_INTMCLR)) {
+                       ioapic->rtbl[pin].pending = false;
+                       ioapic_clearpend++;
+                       /*
+                        * Inject the deferred level-triggered int if it is
+                        * still asserted. Simulate by toggling the pin
+                        * off and then on.
+                        */
+                       if (ioapic->rtbl[pin].pinstate == true) {
+                               ioapic_togglepend++;
+                               ioapic_set_pinstate(vm, pin, false);
+                               ioapic_set_pinstate(vm, pin, true);
+                       }
+               }
        }
 }
 
 static int
-ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr, int size,
-                  uint64_t *data)
+ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
+    int size, uint64_t *data)
 {
        int offset;
 
@@ -276,8 +328,8 @@ ioapic_region_read(struct ioapic *ioapic
 }
 
 static int
-ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr, int size,
-                   uint64_t data)
+ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
+    int size, uint64_t data)
 {
        int offset;
 
@@ -298,14 +350,14 @@ ioapic_region_write(struct ioapic *ioapi
        if (offset == IOREGSEL)
                ioapic->ioregsel = data;
        else
-               ioapic_write(ioapic, ioapic->ioregsel, data);
+               ioapic_write(vm, ioapic, ioapic->ioregsel, data);
 
        return (0);
 }
 
 static int
 ioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr,
-                     int size, uint64_t *val, void *arg1, long arg2)
+    int size, uint64_t *val, void *arg1, long arg2)
 {
        struct ioapic *ioapic;
        int which;
@@ -315,10 +367,12 @@ ioapic_region_handler(struct vmctx *vm, 
 
        assert(ioapic == &ioapics[which]);
 
+       pthread_mutex_lock(&ioapic->mtx);
        if (dir == MEM_F_READ)
-               ioapic_region_read(ioapic, paddr, size, val);
+               ioapic_region_read(vm, ioapic, paddr, size, val);
        else
-               ioapic_region_write(ioapic, paddr, size, *val);
+               ioapic_region_write(vm, ioapic, paddr, size, *val);
+       pthread_mutex_unlock(&ioapic->mtx);
 
        return (0);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to