From owner-svn-src-all@FreeBSD.ORG Fri Oct 25 03:18:57 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 5E4FECB1; Fri, 25 Oct 2013 03:18:57 +0000 (UTC) (envelope-from grehan@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 3A8A92559; Fri, 25 Oct 2013 03:18:57 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9P3IvhQ064462; Fri, 25 Oct 2013 03:18:57 GMT (envelope-from grehan@svn.freebsd.org) Received: (from grehan@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9P3Iuj7064461; Fri, 25 Oct 2013 03:18:56 GMT (envelope-from grehan@svn.freebsd.org) Message-Id: <201310250318.r9P3Iuj7064461@svn.freebsd.org> From: Peter Grehan Date: Fri, 25 Oct 2013 03:18:56 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r257092 - head/usr.sbin/bhyve X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 25 Oct 2013 03:18:57 -0000 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 #include #include +#include #include @@ -46,34 +47,40 @@ __FBSDID("$FreeBSD$"); #include +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); }