Index | Thread | Search

From:
le unium <uniumfr@gmail.com>
Subject:
diff: fix mwx(4) firmware download mbuf corruption & tx cleanup race
To:
tech@openbsd.org
Cc:
claudio@openbsd.org
Date:
Tue, 13 Oct 2026 05:59:51 +0530

Download raw body.

Thread
hi claudio and tech@,

while testing mwx(4) on an mt7921 card, the driver hit two panics during the
firmware download stage at boot:

1) pool_cache_item_magic_check: mbufpl cpu free list modified
2) kernel: protection fault trap in m_tag_delete_chain

with the diff applied, mwx attaches on the mt7921, scans on both 2.4ghz and
5ghz, completes wpa2-psk 4w handshakes and doesnt break over 802.11a/ofdm.

regards,
unium
--- sys/dev/pci/if_mwx.c.orig	Tue Oct 13 05:41:37 2026
+++ sys/dev/pci/if_mwx.c	Tue Oct 13 05:49:20 2026
@@ -2457,7 +2457,7 @@ mwx_dma_alloc(struct mwx_softc *sc)
 	if ((rv = mwx_queue_alloc(sc, &sc->sc_txmcuq, 256,
 	    txq_mcu_regbase)) != 0)
 		return rv;
-	if ((rv = mwx_queue_alloc(sc, &sc->sc_txfwdlq, 16 /* XXX */,
+	if ((rv = mwx_queue_alloc(sc, &sc->sc_txfwdlq, 64 /* XXX */,
 	    MT_TX_FWDL_RING_BASE)) != 0)
 		return rv;
 
@@ -2653,15 +2653,20 @@ mwx_dma_txwi_enqueue(struct mwx_softc *sc, struct mwx_
 void
 mwx_dma_tx_cleanup(struct mwx_softc *sc, struct mwx_queue *q)
 {
-	struct mwx_queue_data *md;
 	struct mwx_desc *desc;
-	int idx, last;
+	struct mbuf *m;
+	struct mwx_queue_data *md;
+	int idx, last, s;
 
+	s = splnet();
+
 	idx = q->mq_cons;
 	last = mwx_read(sc, q->mq_regbase + MT_DMA_DMA_IDX);
 
-	if (idx == last)
+	if (idx == last) {
+		splx(s);
 		return;
+	}
 
 	bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
 	     BUS_DMASYNC_PREWRITE);
@@ -2671,11 +2676,13 @@ mwx_dma_tx_cleanup(struct mwx_softc *sc, struct mwx_qu
 		desc = &q->mq_desc[idx];
 
 		if (md->md_mbuf != NULL) {
+			m = md->md_mbuf;
+			md->md_mbuf = NULL;
+
 			bus_dmamap_sync(sc->sc_dmat, md->md_map, 0,
 			    md->md_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
 			bus_dmamap_unload(sc->sc_dmat, md->md_map);
-			m_freem(md->md_mbuf);
-			md->md_mbuf = NULL;
+			m_freem(m);
 		}
 		if (md->md_txwi != NULL) {
 			/* nothing here, cleanup via mwx_mac_tx_free() */
@@ -2704,6 +2711,8 @@ mwx_dma_tx_cleanup(struct mwx_softc *sc, struct mwx_qu
 		q->mq_wakeme = 0;
 		wakeup(q);
 	}
+
+	splx(s);
 }
 
 void
@@ -2883,10 +2892,9 @@ mwx_mcu_alloc_msg(size_t len)
 		}
 	}
 
-	m_align(m, len + headspace);
-	m->m_pkthdr.len = m->m_len = len + headspace;
-	memset(mtod(m, caddr_t), 0, m->m_len);
-	m_adj(m, headspace);
+	m->m_data += headspace;
+	m->m_len = m->m_pkthdr.len = len;
+	memset(mtod(m, caddr_t), 0, len);
 
 	return m;
 }
@@ -2929,6 +2937,9 @@ mwx_mcu_send_mbuf(struct mwx_softc *sc, uint32_t cmd, 
 	tot_len = txd_len + len;
 	KASSERT(m_leadingspace(m) >= txd_len);
 	m = m_prepend(m, txd_len, M_DONTWAIT);
+	if (m == NULL)
+		return ENOBUFS;
+	m->m_pkthdr.len = tot_len;
 	txd = mtod(m, uint32_t *);
 	memset(txd, 0, txd_len);
 
@@ -3671,7 +3682,7 @@ mwx_load_firmware(struct mwx_softc *sc)
 			goto out;
 		}
 		rv = mwx_mcu_send_firmware(sc, MCU_CMD_FW_SCATTER,
-		    dl, len, 4096);
+		    dl, len, 4096 - sizeof(struct mwx_mcu_txd));
 		if (rv != 0) {
 			DPRINTF("%s: failed to send patch\n", DEVNAME(sc));
 			goto out;
@@ -3741,7 +3752,7 @@ out:
 		}
 
 		rv = mwx_mcu_send_firmware(sc, MCU_CMD_FW_SCATTER,
-		    fwbuf + offset, len, 4096);
+		    fwbuf + offset, len, 4096 - sizeof(struct mwx_mcu_txd));
 		if (rv != 0) {
 			DPRINTF("%s: failed to send firmware\n", DEVNAME(sc));
 			goto fail;
@@ -3972,7 +3983,7 @@ mwx_mcu_send_firmware(struct mwx_softc *sc, int cmd, u
     size_t len, size_t max_len)
 {
 	size_t cur_len;
-	int rv;
+	int i, rv;
 
 	while (len > 0) {
 		cur_len = len;
@@ -3986,7 +3997,14 @@ mwx_mcu_send_firmware(struct mwx_softc *sc, int cmd, u
 		data += cur_len;
 		len -= cur_len;
 
-		mwx_dma_tx_cleanup(sc, &sc->sc_txfwdlq);
+		for (i = 0; i < 1000; i++) {
+			mwx_dma_tx_cleanup(sc, &sc->sc_txfwdlq);
+			if (sc->sc_txfwdlq.mq_cons == sc->sc_txfwdlq.mq_prod)
+				break;
+			delay(10);
+		}
+		if (i >= 1000)
+			return ETIMEDOUT;
 	}
 
 	return 0;