Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175358653
D60413.id189007.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D60413.id189007.diff
View Options
diff --git a/usr.sbin/bhyve/pci_xhci.c b/usr.sbin/bhyve/pci_xhci.c
--- a/usr.sbin/bhyve/pci_xhci.c
+++ b/usr.sbin/bhyve/pci_xhci.c
@@ -426,6 +426,11 @@
{
int i;
+ /* The interrupter's registers return to their defaults, EHB included. */
+ pthread_mutex_lock(&sc->intr_mtx);
+ memset(&sc->rtsregs.intrreg, 0, sizeof(sc->rtsregs.intrreg));
+ pthread_mutex_unlock(&sc->intr_mtx);
+
pthread_mutex_lock(&sc->event_mtx);
sc->rtsregs.er_enq_idx = 0;
sc->rtsregs.er_events_cnt = 0;
@@ -456,12 +461,25 @@
&old, new, memory_order_seq_cst, memory_order_relaxed));
}
+static void pci_xhci_intr_update(struct pci_xhci_softc *sc);
+
static uint32_t
pci_xhci_usbcmd_write(struct pci_xhci_softc *sc, uint32_t cmd)
{
int do_intr = 0;
int i;
+ /*
+ * Store INTE before the run posts its port change events so they can
+ * interrupt, and signal a pending interrupt now that INTE is on; a
+ * guest may enable the controller and interrupts in one USBCMD write.
+ */
+ if ((cmd & XHCI_CMD_INTE) && !(sc->opregs.usbcmd & XHCI_CMD_INTE)) {
+ sc->opregs.usbcmd |= XHCI_CMD_INTE;
+ pci_xhci_intr_update(sc);
+ } else if (!(cmd & XHCI_CMD_INTE))
+ sc->opregs.usbcmd &= ~XHCI_CMD_INTE;
+
if (cmd & XHCI_CMD_RS) {
do_intr = (sc->opregs.usbcmd & XHCI_CMD_RS) == 0;
@@ -728,14 +746,29 @@
DPRINTF(("%s", __func__));
pthread_mutex_lock(&sc->intr_mtx);
- sc->rtsregs.intrreg.erdp |= XHCI_ERDP_LO_BUSY;
+ /*
+ * While the Event Handler Busy flag is set the guest is still
+ * processing the Event Ring and finds the new event there; the
+ * interrupter fires again when the guest clears EHB and the ring is
+ * not empty (xHCI 4.17.2).
+ */
+ if (sc->rtsregs.intrreg.erdp & XHCI_ERDP_LO_BUSY) {
+ pthread_mutex_unlock(&sc->intr_mtx);
+ return;
+ }
+
sc->rtsregs.intrreg.iman |= XHCI_IMAN_INTR_PEND;
atomic_fetch_or_explicit(&sc->opregs.usbsts, XHCI_STS_EINT,
memory_order_seq_cst);
- /* only trigger interrupt if permitted */
+ /*
+ * Set Event Handler Busy only when an interrupt is actually signalled
+ * (xHCI 4.17.2). Latching it for an event posted while interrupts are
+ * off would hold back every later event until the ring is cleared.
+ */
if ((sc->opregs.usbcmd & XHCI_CMD_INTE) &&
(sc->rtsregs.intrreg.iman & XHCI_IMAN_INTR_ENA)) {
+ sc->rtsregs.intrreg.erdp |= XHCI_ERDP_LO_BUSY;
if (pci_msi_enabled(sc->xsc_pi))
pci_generate_msi(sc->xsc_pi, 0);
else
@@ -754,6 +787,32 @@
pthread_mutex_unlock(&sc->intr_mtx);
}
+/*
+ * Signal an interrupt that is pending (IMAN.IP) but could not be delivered
+ * when its event was posted because interrupts were off, once the guest turns
+ * IMAN.IE and USBCMD.INTE on (xHCI 4.17.2: the interrupt is asserted while IP,
+ * IE and INTE are all set). The INTx line already follows that level in
+ * pci_xhci_deassert_interrupt(); this sends the MSI.
+ */
+static void
+pci_xhci_intr_update(struct pci_xhci_softc *sc)
+{
+ struct pci_xhci_rtsregs *rts = &sc->rtsregs;
+
+ pthread_mutex_lock(&sc->intr_mtx);
+ if ((rts->intrreg.iman & XHCI_IMAN_INTR_PEND) &&
+ (rts->intrreg.iman & XHCI_IMAN_INTR_ENA) &&
+ (sc->opregs.usbcmd & XHCI_CMD_INTE) &&
+ !(rts->intrreg.erdp & XHCI_ERDP_LO_BUSY)) {
+ rts->intrreg.erdp |= XHCI_ERDP_LO_BUSY;
+ if (pci_msi_enabled(sc->xsc_pi))
+ pci_generate_msi(sc->xsc_pi, 0);
+ else
+ pci_lintr_assert(sc->xsc_pi);
+ }
+ pthread_mutex_unlock(&sc->intr_mtx);
+}
+
static void
pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
{
@@ -2229,6 +2288,39 @@
XHCI_DB_TARGET_GET(value), XHCI_DB_SID_GET(value));
}
+/*
+ * Recount the events on the Event Ring from the dequeue pointer the guest
+ * wrote. A pointer outside the segment leaves the count alone.
+ */
+static void
+pci_xhci_update_er_events_cnt(struct pci_xhci_softc *sc)
+{
+ struct pci_xhci_rtsregs *rts;
+ uint64_t erdp;
+ uint32_t erdp_i;
+
+ rts = &sc->rtsregs;
+ if (rts->er_events_cnt == 0 || rts->erstba_p == NULL)
+ return;
+
+ erdp = rts->intrreg.erdp & ~0xFUL;
+ if (erdp < rts->erstba_p->qwEvrsTablePtr)
+ return;
+ erdp_i = (erdp - rts->erstba_p->qwEvrsTablePtr) /
+ sizeof(struct xhci_trb);
+ if (erdp_i >= rts->erstba_p->dwEvrsTableSize)
+ return;
+
+ if (erdp_i <= (uint32_t)rts->er_enq_idx)
+ rts->er_events_cnt = rts->er_enq_idx - erdp_i;
+ else
+ rts->er_events_cnt = rts->erstba_p->dwEvrsTableSize -
+ (erdp_i - rts->er_enq_idx);
+
+ DPRINTF(("pci_xhci: erdp 0x%lx, events cnt %u",
+ erdp, rts->er_events_cnt));
+}
+
static void
pci_xhci_rtsregs_write(struct pci_xhci_softc *sc, uint64_t offset,
uint64_t value)
@@ -2250,7 +2342,9 @@
rts = &sc->rtsregs;
switch (offset) {
- case 0x00:
+ case 0x00: {
+ uint32_t ie0 = rts->intrreg.iman & XHCI_IMAN_INTR_ENA;
+
pthread_mutex_lock(&sc->intr_mtx);
if (value & XHCI_IMAN_INTR_PEND)
rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
@@ -2260,8 +2354,11 @@
if (!(value & XHCI_IMAN_INTR_ENA))
pci_xhci_deassert_interrupt(sc);
+ if (ie0 == 0 && (rts->intrreg.iman & XHCI_IMAN_INTR_ENA))
+ pci_xhci_intr_update(sc);
break;
+ }
case 0x04:
rts->intrreg.imod = value;
@@ -2299,8 +2396,10 @@
pthread_mutex_unlock(&sc->event_mtx);
break;
- case 0x18:
- /* ERDP low bits -- does not touch er_events_cnt/er_enq_idx */
+ case 0x18: {
+ /* ERDP low bits */
+ int busy_cleared = 0;
+
pthread_mutex_lock(&sc->intr_mtx);
rts->intrreg.erdp =
MASK_64_HI(sc->rtsregs.intrreg.erdp) |
@@ -2309,37 +2408,36 @@
if (value & XHCI_ERDP_LO_BUSY) {
rts->intrreg.erdp &= ~XHCI_ERDP_LO_BUSY;
rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
+ busy_cleared = 1;
}
pthread_mutex_unlock(&sc->intr_mtx);
rts->er_deq_seg = XHCI_ERDP_LO_SINDEX(value);
+ pthread_mutex_lock(&sc->event_mtx);
+ pci_xhci_update_er_events_cnt(sc);
+ pthread_mutex_unlock(&sc->event_mtx);
+
+ /*
+ * Events that arrived while the guest was busy did not
+ * interrupt; now that it cleared EHB the interrupter fires
+ * again if the ring is not empty (xHCI 4.17.2).
+ */
+ if (busy_cleared && rts->er_events_cnt > 0)
+ pci_xhci_assert_interrupt(sc);
+
break;
+ }
case 0x1C:
/* ERDP high bits */
- pthread_mutex_lock(&sc->event_mtx);
+ pthread_mutex_lock(&sc->intr_mtx);
rts->intrreg.erdp = (value << 32) |
MASK_64_LO(sc->rtsregs.intrreg.erdp);
+ pthread_mutex_unlock(&sc->intr_mtx);
- if (rts->er_events_cnt > 0) {
- uint64_t erdp;
- int erdp_i;
-
- erdp = rts->intrreg.erdp & ~0xF;
- erdp_i = (erdp - rts->erstba_p->qwEvrsTablePtr) /
- sizeof(struct xhci_trb);
-
- if (erdp_i <= rts->er_enq_idx)
- rts->er_events_cnt = rts->er_enq_idx - erdp_i;
- else
- rts->er_events_cnt =
- rts->erstba_p->dwEvrsTableSize -
- (erdp_i - rts->er_enq_idx);
-
- DPRINTF(("pci_xhci: erdp 0x%lx, events cnt %u",
- erdp, rts->er_events_cnt));
- }
+ pthread_mutex_lock(&sc->event_mtx);
+ pci_xhci_update_er_events_cnt(sc);
pthread_mutex_unlock(&sc->event_mtx);
break;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 11, 6:44 AM (1 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40454164
Default Alt Text
D60413.id189007.diff (6 KB)
Attached To
Mode
D60413: bhyve: xhci: do not interrupt while the event handler is busy
Attached
Detach File
Event Timeline
Log In to Comment