Page MenuHomeFreeBSD

D60413.id189007.diff
No OneTemporary

D60413.id189007.diff

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

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)

Event Timeline