summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorgrehan <grehan@FreeBSD.org>2013-10-30 21:12:27 +0000
committergrehan <grehan@FreeBSD.org>2013-10-30 21:12:27 +0000
commit382c31019068aa68cea45143a9a64793b18c0967 (patch)
tree9caa31fca57825d287280f659acb81c381bb1d12 /usr.sbin
parent249db5aac359e977e2e92a1e27132c9d3037f3dd (diff)
downloadFreeBSD-src-382c31019068aa68cea45143a9a64793b18c0967.zip
FreeBSD-src-382c31019068aa68cea45143a9a64793b18c0967.tar.gz
MFC r257092
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. Approved by: re (glebius)
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/ioapic.c116
1 files changed, 85 insertions, 31 deletions
diff --git a/usr.sbin/bhyve/ioapic.c b/usr.sbin/bhyve/ioapic.c
index aeb008e..b996214 100644
--- a/usr.sbin/bhyve/ioapic.c
+++ b/usr.sbin/bhyve/ioapic.c
@@ -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, int pin, bool newstate)
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, int pin, bool newstate)
* 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, int pin, bool newstate)
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, uint32_t addr)
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, uint32_t addr, uint32_t data)
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, uintptr_t paddr, int size,
}
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 *ioapic, uintptr_t paddr, int size,
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, int vcpu, int dir, uintptr_t paddr,
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);
}
OpenPOWER on IntegriCloud