bwn BCM43224 (MacBook Air A1370) driver fixes for FreeBSD 15.1
Patches + build scripts + firmware sources to make the Broadcom BCM43224
(PCI 0x14e4:0x4353, subvendor 0x106b:0x00d1) work on FreeBSD 15.1.
Fixes:
- Fix 1: compile GPL N-PHY code (BWN_GPL_PHY build wiring)
- Fix 2: NULL-guard bwn_dma_ringfree on partial attach
- Fix 3: wire bwn_update_mcast to ic->ic_update_mcast
- Fix 5: load bwn_v4_n firmware at loader stage (loader.conf)
- Fix 6: reclaim pending TX frames at bwn_dma_stop (detach double-free)
- Fix 7: driver-owned node ref + remove dr_usedslot early-return +
bhndb_pci BHNDB_PCI_QUIRK_NO_MSI for BCM43224 (legacy INTx)
- Fix 8: restrict reclaim to TX rings only (RX reclaim caused heap
corruption / TCP panic)
README.md = rebuild guide (apply.sh + build.sh, manual steps)
RESEARCH.md = root-cause investigation log (Fixes 5-8)
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# Build artifacts (regenerated by build.sh / make)
|
||||||
|
*.o
|
||||||
|
*.fwo
|
||||||
|
*.ko
|
||||||
|
.depend*
|
||||||
|
opt_global.h
|
||||||
|
machine
|
||||||
|
x86
|
||||||
|
i386
|
||||||
|
src/firmware/bwn_v4_n.ko
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# bwn driver fixes for Broadcom BCM43224 (Apple MacBook Air A1370)
|
||||||
|
|
||||||
|
A complete, self-contained set of fixes that make the FreeBSD `bwn(4)` driver
|
||||||
|
work on the Apple MacBook Air A1370's BCM43224
|
||||||
|
(PCI `0x14e4:0x4353`, subvendor `0x106b:0x00d1`) 802.11a/b/g/n chip.
|
||||||
|
|
||||||
|
The driver has been tested through multiple `kldunload`/`kldload` cycles and
|
||||||
|
reboots on the target hardware with no panic.
|
||||||
|
|
||||||
|
## Build & install (clean FreeBSD 15.1)
|
||||||
|
|
||||||
|
You need the FreeBSD 15.1 **source tree** at `/usr/src` (or pass an alternate
|
||||||
|
`SRC_ROOT` to the scripts). Then, as root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd bwn-a1370
|
||||||
|
sudo ./apply.sh # patch /usr/src (or: sudo ./apply.sh /path/to/src)
|
||||||
|
sudo ./build.sh # build + install if_bwn.ko, bhndb_pci.ko, bwn_v4_n.ko
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
After reboot, bring up the interface:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ifconfig wlan create wlandev bwn0 [wap]
|
||||||
|
# or, to join a WPA network, create the wlan and run wpa_supplicant as usual
|
||||||
|
```
|
||||||
|
|
||||||
|
`build.sh` installs everything for the **next boot**; it does not touch the
|
||||||
|
running kernel, so an active session (e.g. SSH over a fallback `rtwn0/wlan0`
|
||||||
|
interface) is never disrupted.
|
||||||
|
|
||||||
|
### What the scripts do
|
||||||
|
|
||||||
|
- `apply.sh` — idempotently applies the 4 source patches to `/usr/src`
|
||||||
|
(`patches/patch-*.c` and `patches/patch-Makefile`). Safe to re-run.
|
||||||
|
- `build.sh` — builds `if_bwn.ko` (`sys/modules/bwn`), `bhndb_pci.ko`
|
||||||
|
(`sys/modules/bhnd/bhndb_pci`), and `bwn_v4_n.ko` (from `src/firmware/`);
|
||||||
|
installs them to `/boot/modules` and `/boot/kernel`; and appends
|
||||||
|
`bwn_v4_n_load="YES"` to `/boot/loader.conf` if missing (Fix 5).
|
||||||
|
|
||||||
|
### Manual build (equivalent to build.sh)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SRC=/usr/src
|
||||||
|
|
||||||
|
# 1. driver
|
||||||
|
make -C $SRC/sys/modules/bwn SRCTOP=$SRC SYSDIR=$SRC/sys
|
||||||
|
install -m 444 $SRC/sys/modules/bwn/if_bwn.ko /boot/modules/
|
||||||
|
|
||||||
|
# 2. PCIe bridge (NO_MSI quirk)
|
||||||
|
make -C $SRC/sys/modules/bhnd/bhndb_pci SRCTOP=$SRC SYSDIR=$SRC/sys
|
||||||
|
install -m 444 $SRC/sys/modules/bhnd/bhndb_pci/bhndb_pci.ko /boot/kernel/
|
||||||
|
|
||||||
|
# 3. firmware
|
||||||
|
make -C src/firmware SRCTOP=$SRC SYSDIR=$SRC/sys
|
||||||
|
install -m 444 src/firmware/bwn_v4_n.ko /boot/modules/
|
||||||
|
|
||||||
|
# 4. loader.conf (Fix 5)
|
||||||
|
echo 'bwn_v4_n_load="YES"' >> /boot/loader.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory layout
|
||||||
|
|
||||||
|
```
|
||||||
|
bwn-a1370/
|
||||||
|
README.md This file (rebuild guide)
|
||||||
|
RESEARCH.md Root-cause investigation log (Fixes 5-8)
|
||||||
|
apply.sh Apply the 4 source patches to /usr/src (idempotent)
|
||||||
|
build.sh Build + install all 3 modules and add the loader.conf entry
|
||||||
|
loader.conf.bwn Snippet for /boot/loader.conf (Fix 5: load firmware at boot)
|
||||||
|
patches/
|
||||||
|
patch-if_bwn.c Driver fixes (Fixes 1-4, 6-8)
|
||||||
|
patch-Makefile GPL PHY build wiring (Fix 1)
|
||||||
|
patch-bhndb_pci.c NO_MSI quirk for BCM43224 (Fix 7)
|
||||||
|
patch-bhndb_pcivar.h NO_MSI quirk definition (Fix 7)
|
||||||
|
combined.patch All of the above in one file (alternative to apply.sh)
|
||||||
|
src/firmware/ Broadcom v4 firmware module (bwn_v4_n.ko) sources + Makefile
|
||||||
|
rc.conf.backups/ Backups of loader.conf/rc.conf made by the scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
## What was broken (summary)
|
||||||
|
|
||||||
|
On a stock FreeBSD 15.1 system this chip (an N-PHY rev-23 core behind a
|
||||||
|
BCMA/bhnd bus, attached via the `bhndb_pci` PCIe-G1 bridge) failed in several
|
||||||
|
ways:
|
||||||
|
|
||||||
|
1. The GPL N-PHY code is not compiled in (build wiring).
|
||||||
|
2. A NULL ring pointer can be dereferenced on a partial attach.
|
||||||
|
3. Multicast updates are not wired to `ic->ic_update_mcast`.
|
||||||
|
4. Firmware is not found at attach (loader vs kld_list ordering).
|
||||||
|
5. **MSI interrupts are never delivered** on this host (NVIDIA MCP89 bridge),
|
||||||
|
so TX completions never run -> "device timeout" and the NIC never passes
|
||||||
|
traffic.
|
||||||
|
6. Detaching while a TX frame is pending double-frees an `ieee80211_node`
|
||||||
|
(kernel panic).
|
||||||
|
7. Reclaiming the **RX** ring at device-stop DMAs received frames into freed
|
||||||
|
kernel memory -> heap corruption / TCP panic.
|
||||||
|
|
||||||
|
| # | Area | Problem | Fix |
|
||||||
|
|---|------|---------|-----|
|
||||||
|
| 1 | build | GPL N-PHY code not compiled in | `BWN_GPL_PHY` via `opt_bwn.h`; add 7 GPL PHY files to `SRCS` in `modules/bwn/Makefile` |
|
||||||
|
| 2 | `bwn_dma_ringfree` | NULL ring deref on partial attach | NULL-guard `if (dr == NULL \|\| *dr == NULL) return;` |
|
||||||
|
| 3 | `bwn_update_mcast` | mcast not updated | wire to `ic->ic_update_mcast` |
|
||||||
|
| 4 | `build.sh` | reload clobbered live iface | unload/rebuild/reinstall/reload `if_bwn` only |
|
||||||
|
| 5 | `/boot/loader.conf` | firmware not found at attach | `bwn_v4_n_load="YES"` loads firmware at loader stage |
|
||||||
|
| 6 | `bwn_dma_stop` | detach double-frees node (vmcore.4) | reclaim pending TX frames (drop node refs) at device-stop |
|
||||||
|
| 7 | `if_bwn` + `bhndb_pci` | node ref ownership + MSI never delivered (vmcore.5 / "device timeout") | driver takes its own `ieee80211_ref_node()`; remove `dr_usedslot` early-return; `BHNDB_PCI_QUIRK_NO_MSI` for BCM43224 forces legacy INTx |
|
||||||
|
| 8 | `bwn_dma_stop` | RX-ring reclaim -> heap corruption (vmcore.6) | restrict reclaim to TX rings only (new `bwn_dma_reclaim_tx()`) |
|
||||||
|
|
||||||
|
## Files modified in /usr/src
|
||||||
|
|
||||||
|
- `sys/dev/bwn/if_bwn.c`
|
||||||
|
- `sys/modules/bwn/Makefile`
|
||||||
|
- `sys/dev/bhnd/bhndb/bhndb_pci.c`
|
||||||
|
- `sys/dev/bhnd/bhndb/bhndb_pcivar.h`
|
||||||
|
|
||||||
|
(Plus the prebuilt `bwn_v4_n.ko` firmware, reproducible from `src/firmware/`.)
|
||||||
|
|
||||||
|
See `RESEARCH.md` for the full root-cause analysis behind each fix.
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
# Research & Root-Cause Log
|
||||||
|
|
||||||
|
This document records the investigation that produced the fixes in this
|
||||||
|
repository. It is kept separate from `README.md` (the rebuild guide) so the
|
||||||
|
how-to stays clean. Each section maps to a fix number used throughout the
|
||||||
|
project.
|
||||||
|
|
||||||
|
Hardware under test: Apple MacBook Air A1370, Broadcom BCM43224
|
||||||
|
(PCI `0x14e4:0x4353`, subvendor `0x106b:0x00d1`), N-PHY rev-23 core behind a
|
||||||
|
BCMA/bhnd bus attached via the `bhndb_pci` PCIe-G1 bridge, on an NVIDIA MCP89
|
||||||
|
host bridge. FreeBSD 15.1-RELEASE-p2 (amd64).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fix 5 — Load Broadcom v4 firmware at loader time (firmware "not found" panic)
|
||||||
|
|
||||||
|
**Problem.** `if_bwn.ko` has a `pci/bwn_pci` devmatch modalias, so the kernel
|
||||||
|
AUTO-LOADS it when `bwn_pci0` attaches. The firmware-only `bwn_v4_n.ko` has NO
|
||||||
|
modalias and loads only via `kld_list`. If the firmware module is missing at
|
||||||
|
attach time (e.g. `rc.conf` `kld_list` got corrupted/shortened),
|
||||||
|
`bwn_fw_get()` fails and the driver panics while cleaning up
|
||||||
|
("firmware bwn_v4_ucode16_mimo not found").
|
||||||
|
|
||||||
|
**Fix.** Add `bwn_v4_n_load="YES"` to `/boot/loader.conf` so the firmware
|
||||||
|
module is loaded at the loader stage, before any device attach. This makes
|
||||||
|
firmware availability independent of `rc.conf`/`kld_list` ordering.
|
||||||
|
|
||||||
|
**Verification.** With `bwn_v4_n` loaded, creating `wlan1` on `bwn0` loads
|
||||||
|
`ucode16_mimo` with no "not found" error.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fix 6 — Kernel panic on kldunload after a "device timeout" (vmcore.4)
|
||||||
|
|
||||||
|
**Symptom.** `kldunload if_bwn` (or any detach while a wlan existed on bwn0)
|
||||||
|
panicked with a page fault in `node_cleanup` / `ieee80211_ageq_remove`
|
||||||
|
(fault addr `0x488`), always after a `bwn0: device timeout`.
|
||||||
|
|
||||||
|
**Root cause.** TX never completes because MSI interrupts are not delivered,
|
||||||
|
so the TX slot keeps its `mbuf` + `ieee80211_node` (`mt_ni`) reference —
|
||||||
|
`bwn_dma_handle_txeof` (the only place that clears `mt_ni`) never runs.
|
||||||
|
`bwn_dma_stop()` only halts the DMA engine and never frees queued frames, so
|
||||||
|
the node reference lingers. When net80211 later frees that node (scan cancel /
|
||||||
|
VAP teardown) the slot points at freed memory; then `bwn_detach` ->
|
||||||
|
`bwn_dma_free` -> `bwn_dma_ringfree` -> `bwn_dma_free_descbufs` ->
|
||||||
|
`ieee80211_free_node(stale mt_ni)` walks freed memory and panics.
|
||||||
|
|
||||||
|
**Fix.** Reclaim pending frames (and their node references) in
|
||||||
|
`bwn_dma_stop()`, which runs from both the detach path
|
||||||
|
(`bwn_stop` -> `bwn_core_exit`) and every HW reset. This drops node references
|
||||||
|
while the node is still alive (before `ieee80211_ifdetach` frees nodes).
|
||||||
|
`bwn_dma_free_descbufs()` is idempotent (NULLs `mt_m`/`mt_ni`), so the later
|
||||||
|
detach free finds empty slots.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fix 7 — TX node reference ownership + unconditional reclaim + MSI/INTx (vmcore.5)
|
||||||
|
|
||||||
|
**Symptom.** After Fix 6 a fresh reboot still panicked on `kldunload`
|
||||||
|
(vmcore.5): page fault in `ieee80211_ratectl_node_deinit` (fault addr `0x30`,
|
||||||
|
`vap->iv_rate` deref), `ni->ni_refcnt = 1`, node's `ni_vap` already torn down.
|
||||||
|
Also, with MSI never delivered, the chip showed "device timeout" and passed no
|
||||||
|
traffic.
|
||||||
|
|
||||||
|
**Root cause.** The driver relied on the mbuf's `rcvif` node reference (owned
|
||||||
|
by the net80211 stack) for the lifetime of a queued TX frame. The stack can
|
||||||
|
release that reference at any time (scan cancel, VAP teardown, node reclaim)
|
||||||
|
while the frame is still in the ring, because TX completion never runs when
|
||||||
|
interrupts are not delivered. Also `bwn_dma_free_descbufs()` early-returned on
|
||||||
|
`if (!dr->dr_usedslot)`; `dr_usedslot` is only a free-slot accounting hint and
|
||||||
|
is not a reliable indicator of pending frames, so the reclaim could be
|
||||||
|
skipped.
|
||||||
|
|
||||||
|
Separately, `bhndb_pci` enables MSI for the BCM43224, but on this host (NVIDIA
|
||||||
|
MCP89 Apple MacBook Air) MSI is never delivered.
|
||||||
|
|
||||||
|
**Fix.**
|
||||||
|
1. `bwn_dma_tx_start()` / `bwn_pio_tx_start()` now take the driver's OWN
|
||||||
|
reference via `ieee80211_ref_node(ni)` before stashing it in `mt->mt_ni` /
|
||||||
|
`tp->tp_ni`. `ieee80211_tx_complete()` in the txeof path releases exactly
|
||||||
|
one reference, so accounting stays balanced and detach cannot double-free.
|
||||||
|
2. Removed the `if (!dr->dr_usedslot) return;` early-return from
|
||||||
|
`bwn_dma_free_descbufs()` so reclaim unconditionally walks every slot.
|
||||||
|
3. Added `BHNDB_PCI_QUIRK_NO_MSI` for BCM43224 in `bhndb_pci.c` /
|
||||||
|
`bhndb_pcivar.h` so the bridge falls back to legacy INTx — TX interrupts
|
||||||
|
are then delivered and the "device timeout" root cause is gone.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fix 8 — Remove RX-ring reclaim from bwn_dma_stop (vmcore.6 heap corruption)
|
||||||
|
|
||||||
|
**Symptom.** After Fix 6 the bwn *detach* panic was gone (clean `bwn0:
|
||||||
|
detached` cycles), but ~47 min into a session the box panicked with a
|
||||||
|
DIFFERENT crash: a TCP stack NULL-deref inside `tcp_m_copym` while processing
|
||||||
|
an inbound packet on wlan0/rtwn0 (the SSH link). The SSH socket's send buffer
|
||||||
|
was corrupted (`so_snd.sb_mb == NULL` while `sb_ccc == 126404`; `tp` seq
|
||||||
|
numbers garbage).
|
||||||
|
|
||||||
|
**Root cause.** Fix 6's reclaim in `bwn_dma_stop()` walked **all six rings
|
||||||
|
including the RX ring**. `bwn_dma_stop()` runs on **every** `bwn_hwreset`
|
||||||
|
(every "device timeout" — and on this chip MSI TX interrupts are not
|
||||||
|
delivered, so timeouts fire constantly). `bwn_dma_free_descbufs()` frees the
|
||||||
|
RX mbuf WITHOUT clearing the DMA descriptor's physical address. The RX ring is
|
||||||
|
persistent (refilled in place by `bwn_rxeof()` -> `bwn_dma_newbuf()`, never
|
||||||
|
re-allocated) and `bwn_dma_cleanup()` only zeroes the ring *control* register.
|
||||||
|
So after a reset the descriptor still pointed at the freed page; when RX was
|
||||||
|
re-enabled the NIC DMAd received frames into freed kernel memory -> heap
|
||||||
|
corruption that eventually clobbered the SSH socket. The console log showed
|
||||||
|
~60 lines of `bwn0: bwn_dma_free_descbufs: not TX?` (one per RX slot whose
|
||||||
|
mbuf had just been freed) immediately before the panic — the smoking gun.
|
||||||
|
|
||||||
|
**Fix.** Restrict the reclaim in `bwn_dma_stop()` (factored into a new helper
|
||||||
|
`bwn_dma_reclaim_tx()`) to the **TX rings only** (wme BK/BE/VI/VO + mcast).
|
||||||
|
The RX ring is never reclaimed there. `bwn_dma_reclaim_tx()` is also called
|
||||||
|
from `bwn_detach()` BEFORE `ieee80211_ifdetach()` (defence-in-depth on top of
|
||||||
|
the ref_node fix in Fix 7).
|
||||||
|
|
||||||
|
**Verification.** Disassembly: `bwn_dma_stop` has 6 ringstops (RX + 5 TX) +
|
||||||
|
exactly 5 `bwn_dma_free_descbufs` calls (TX only; RX ring no longer
|
||||||
|
reclaimed).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why the MSI quirk matters (Fix 7, point 3)
|
||||||
|
|
||||||
|
On this host the `bhndb_pci` bridge enables MSI for the BCM43224, but the
|
||||||
|
interrupts are never delivered (no `bwn`/`MSI` line in `vmstat -i`). Because
|
||||||
|
the only place that clears a TX slot's node reference is the TX-completion
|
||||||
|
path (`bwn_dma_handle_txeof`), TX frames never complete -> "device timeout"
|
||||||
|
and the cascade of use-after-free / heap-corruption panics. Forcing legacy
|
||||||
|
INTx (the `NO_MSI` quirk) makes TX interrupts arrive, so completions run
|
||||||
|
normally. The reclaim/ref_node fixes (6-8) are defence-in-depth that keep the
|
||||||
|
driver robust even if completions are delayed.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Apply the bwn BCM43224 (Apple MacBook Air A1370) patches to a clean
|
||||||
|
# FreeBSD 15.1 source tree. Idempotent: it skips any patch that is already
|
||||||
|
# applied.
|
||||||
|
#
|
||||||
|
# Usage: sudo ./apply.sh [SRC_ROOT]
|
||||||
|
# Default SRC_ROOT=/usr/src
|
||||||
|
#
|
||||||
|
# The patches:
|
||||||
|
# patches/patch-if_bwn.c - driver fixes (Fixes 1-4, 6-8)
|
||||||
|
# patches/patch-Makefile - GPL PHY build wiring (Fix 1)
|
||||||
|
# patches/patch-bhndb_pci.c - NO_MSI quirk for BCM43224 (Fix 7)
|
||||||
|
# patches/patch-bhndb_pcivar.h - NO_MSI quirk definition (Fix 7)
|
||||||
|
set -e
|
||||||
|
SRC="${1:-/usr/src}"
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
if [ ! -f "$SRC/sys/dev/bwn/if_bwn.c" ]; then
|
||||||
|
echo "ERROR: $SRC/sys/dev/bwn/if_bwn.c not found." >&2
|
||||||
|
echo " Point SRC_ROOT at a FreeBSD 15.1 src tree (e.g. /usr/src)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Applying patches into: $SRC"
|
||||||
|
for p in patch-if_bwn.c patch-Makefile patch-bhndb_pci.c patch-bhndb_pcivar.h; do
|
||||||
|
if (cd "$SRC" && patch -p1 -N --dry-run < "$HERE/patches/$p" >/dev/null 2>&1); then
|
||||||
|
(cd "$SRC" && patch -p1 -N < "$HERE/patches/$p")
|
||||||
|
echo " applied $p"
|
||||||
|
else
|
||||||
|
echo " skip $p (already applied or not applicable)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Done. Source patched. Now run ./build.sh (or 'make' in sys/modules/bwn)."
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Build and install the patched bwn driver (if_bwn.ko), the patched PCIe
|
||||||
|
# bridge (bhndb_pci.ko) and the Broadcom v4 firmware (bwn_v4_n.ko) on a
|
||||||
|
# clean FreeBSD 15.1 system.
|
||||||
|
#
|
||||||
|
# Usage: sudo ./build.sh [SRC_ROOT]
|
||||||
|
# Default SRC_ROOT=/usr/src
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# 1. Builds if_bwn.ko (sys/modules/bwn) -> /boot/modules/if_bwn.ko
|
||||||
|
# 2. Builds bhndb_pci.ko(sys/modules/bhnd/bhndb_pci) -> /boot/kernel/bhndb_pci.ko
|
||||||
|
# 3. Builds bwn_v4_n.ko (src/firmware) -> /boot/modules/bwn_v4_n.ko
|
||||||
|
# 4. Adds "bwn_v4_n_load=YES" to /boot/loader.conf (Fix 5) if missing.
|
||||||
|
#
|
||||||
|
# All changes take effect on the NEXT reboot (modules are only loaded at
|
||||||
|
# boot). The running kernel is NOT touched, so an active SSH session (e.g.
|
||||||
|
# over the fallback rtwn0/wlan0 interface) is never disrupted by this script.
|
||||||
|
#
|
||||||
|
# After reboot, bwn0 attaches on the BCM43224 and uses legacy INTx (the
|
||||||
|
# NO_MSI quirk), so TX interrupts are delivered and the "device timeout" /
|
||||||
|
# detach-panic / heap-corruption issues are resolved.
|
||||||
|
set -e
|
||||||
|
SRC="${1:-/usr/src}"
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
if [ ! -d "$SRC/sys" ]; then
|
||||||
|
echo "ERROR: $SRC/sys not found. Point SRC_ROOT at a FreeBSD 15.1 src tree." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> [1/3] Building if_bwn.ko"
|
||||||
|
make -C "$SRC/sys/modules/bwn" SRCTOP="$SRC" SYSDIR="$SRC/sys" clean
|
||||||
|
make -C "$SRC/sys/modules/bwn" SRCTOP="$SRC" SYSDIR="$SRC/sys"
|
||||||
|
install -m 444 -o root -g wheel "$SRC/sys/modules/bwn/if_bwn.ko" /boot/modules/if_bwn.ko
|
||||||
|
sync
|
||||||
|
echo " -> /boot/modules/if_bwn.ko"
|
||||||
|
|
||||||
|
echo "==> [2/3] Building bhndb_pci.ko (NO_MSI quirk)"
|
||||||
|
make -C "$SRC/sys/modules/bhnd/bhndb_pci" SRCTOP="$SRC" SYSDIR="$SRC/sys" clean
|
||||||
|
make -C "$SRC/sys/modules/bhnd/bhndb_pci" SRCTOP="$SRC" SYSDIR="$SRC/sys"
|
||||||
|
install -m 444 -o root -g wheel "$SRC/sys/modules/bhnd/bhndb_pci/bhndb_pci.ko" /boot/kernel/bhndb_pci.ko
|
||||||
|
sync
|
||||||
|
echo " -> /boot/kernel/bhndb_pci.ko"
|
||||||
|
|
||||||
|
echo "==> [3/3] Building + installing bwn_v4_n.ko (firmware)"
|
||||||
|
make -C "$HERE/src/firmware" SRCTOP="$SRC" SYSDIR="$SRC/sys" clean
|
||||||
|
make -C "$HERE/src/firmware" SRCTOP="$SRC" SYSDIR="$SRC/sys"
|
||||||
|
install -m 444 -o root -g wheel "$HERE/src/firmware/bwn_v4_n.ko" /boot/modules/bwn_v4_n.ko
|
||||||
|
sync
|
||||||
|
echo " -> /boot/modules/bwn_v4_n.ko"
|
||||||
|
|
||||||
|
echo "==> Adding firmware load to /boot/loader.conf (Fix 5)"
|
||||||
|
if ! grep -q 'bwn_v4_n_load="YES"' /boot/loader.conf 2>/dev/null; then
|
||||||
|
cp /boot/loader.conf "$HERE/rc.conf.backups/loader.conf.before_build_$(date +%Y%m%d%H%M%S)"
|
||||||
|
printf '\n# bwn driver: load Broadcom v4 firmware at loader time (Fix 5)\nbwn_v4_n_load="YES"\n' >> /boot/loader.conf
|
||||||
|
echo " added bwn_v4_n_load=YES"
|
||||||
|
else
|
||||||
|
echo " already present, skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "==> All modules built and installed. REBOOT to activate."
|
||||||
|
echo " After reboot, create the interface with:"
|
||||||
|
echo " ifconfig wlan create wlandev bwn0 [wap]"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Fix 5 (bwn driver): load the Broadcom v4 firmware module at loader time so it
|
||||||
|
# is present BEFORE if_bwn attaches bwn0 (if_bwn auto-loads via devmatch when
|
||||||
|
# bwn_pci0 attaches, but the firmware-only module has no modalias and would
|
||||||
|
# otherwise only load from kld_list, which is fragile). Without this the driver
|
||||||
|
# panics with "firmware bwn_v4_ucode16_mimo not found" during cleanup.
|
||||||
|
bwn_v4_n_load="YES"
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
diff --git a/sys/dev/bhnd/bhndb/bhndb_pci.c b/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
index 0cdcba1d..7b6ae0f3 100644
|
||||||
|
--- a/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
+++ b/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
@@ -195,6 +195,16 @@ static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
|
||||||
|
static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
|
||||||
|
/* All PCIe-G1 core revisions require the SRSH work-around */
|
||||||
|
BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR),
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * BCM43224 (and other Broadcom PCIe-G1 endpoints) do not have MSI
|
||||||
|
+ * delivered on some host platforms (e.g. NVIDIA MCP89 based Apple
|
||||||
|
+ * MacBook Air); force legacy INTx instead.
|
||||||
|
+ */
|
||||||
|
+ { { BHND_MATCH_CHIP_ID(BCM43224) },
|
||||||
|
+ { BHND_MATCH_ANY },
|
||||||
|
+ BHNDB_PCI_QUIRK_NO_MSI },
|
||||||
|
+
|
||||||
|
BHNDB_PCI_QUIRK_END
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -327,6 +337,13 @@ bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count)
|
||||||
|
{
|
||||||
|
int error, count;
|
||||||
|
|
||||||
|
+ /* MSI disabled by bridge quirk? */
|
||||||
|
+ if (sc->pci_quirks & BHNDB_PCI_QUIRK_NO_MSI) {
|
||||||
|
+ device_printf(sc->dev, "MSI disabled by quirk; using INTx on "
|
||||||
|
+ "%s\n", device_get_nameunit(sc->parent));
|
||||||
|
+ return (ENXIO);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* Is MSI available? */
|
||||||
|
if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
|
||||||
|
return (ENXIO);
|
||||||
|
diff --git a/sys/dev/bhnd/bhndb/bhndb_pcivar.h b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
index ed749518..7b473fa6 100644
|
||||||
|
--- a/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
+++ b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
@@ -70,6 +70,14 @@ enum {
|
||||||
|
* interrupt flags via the SIBA_CFG0_INTVEC register.
|
||||||
|
*/
|
||||||
|
BHNDB_PCI_QUIRK_SIBA_INTVEC = (1<<1),
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * The PCI/PCIe bridge must not use MSI; instead, fall back to legacy
|
||||||
|
+ * INTx line interrupts. Required on some systems (e.g. NVIDIA MCP89
|
||||||
|
+ * based Apple MacBook Air) where MSI messages from the Broadcom
|
||||||
|
+ * PCIe-G1 endpoint are never delivered, causing device timeouts.
|
||||||
|
+ */
|
||||||
|
+ BHNDB_PCI_QUIRK_NO_MSI = (1<<2),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** bhndb_pci quirk table entry */
|
||||||
|
diff --git a/sys/dev/bwn/if_bwn.c b/sys/dev/bwn/if_bwn.c
|
||||||
|
index 38bf6f5d..7a5b4230 100644
|
||||||
|
--- a/sys/dev/bwn/if_bwn.c
|
||||||
|
+++ b/sys/dev/bwn/if_bwn.c
|
||||||
|
@@ -147,6 +147,7 @@ static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
||||||
|
const struct ieee80211_bpf_params *);
|
||||||
|
static void bwn_updateslot(struct ieee80211com *);
|
||||||
|
static void bwn_update_promisc(struct ieee80211com *);
|
||||||
|
+static void bwn_update_mcast(struct ieee80211com *);
|
||||||
|
static void bwn_wme_init(struct bwn_mac *);
|
||||||
|
static int bwn_wme_update(struct ieee80211com *);
|
||||||
|
static void bwn_wme_clear(struct bwn_softc *);
|
||||||
|
@@ -250,6 +251,7 @@ static void bwn_dma_setup(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_free_ringmemory(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_cleanup(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_free_descbufs(struct bwn_dma_ring *);
|
||||||
|
+static void bwn_dma_reclaim_tx(struct bwn_mac *);
|
||||||
|
static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int);
|
||||||
|
static void bwn_dma_rx(struct bwn_dma_ring *);
|
||||||
|
static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int);
|
||||||
|
@@ -812,6 +814,7 @@ bwn_attach_post(struct bwn_softc *sc)
|
||||||
|
ic->ic_raw_xmit = bwn_raw_xmit;
|
||||||
|
ic->ic_updateslot = bwn_updateslot;
|
||||||
|
ic->ic_update_promisc = bwn_update_promisc;
|
||||||
|
+ ic->ic_update_mcast = bwn_update_mcast;
|
||||||
|
ic->ic_wme.wme_update = bwn_wme_update;
|
||||||
|
ic->ic_scan_start = bwn_scan_start;
|
||||||
|
ic->ic_scan_end = bwn_scan_end;
|
||||||
|
@@ -855,7 +858,6 @@ bwn_detach(device_t dev)
|
||||||
|
BWN_LOCK(sc);
|
||||||
|
bwn_stop(sc);
|
||||||
|
BWN_UNLOCK(sc);
|
||||||
|
- bwn_dma_free(mac);
|
||||||
|
callout_drain(&sc->sc_led_blink_ch);
|
||||||
|
callout_drain(&sc->sc_rfswitch_ch);
|
||||||
|
callout_drain(&sc->sc_task_ch);
|
||||||
|
@@ -863,7 +865,17 @@ bwn_detach(device_t dev)
|
||||||
|
bwn_phy_detach(mac);
|
||||||
|
ieee80211_draintask(ic, &mac->mac_hwreset);
|
||||||
|
ieee80211_draintask(ic, &mac->mac_txpower);
|
||||||
|
+ /*
|
||||||
|
+ * Drop any TX-ring node references BEFORE ieee80211_ifdetach()
|
||||||
|
+ * frees all nodes. bwn_dma_free()/detach would otherwise find a
|
||||||
|
+ * dangling node pointer (the ring slot still holds mt_ni for a
|
||||||
|
+ * frame whose TX completion never ran) and double-free it
|
||||||
|
+ * (vmcore.5). This is independent of mac_status: a frame can be
|
||||||
|
+ * queued in a ring without the MAC ever reaching STARTED.
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_reclaim_tx(mac);
|
||||||
|
ieee80211_ifdetach(ic);
|
||||||
|
+ bwn_dma_free(mac);
|
||||||
|
}
|
||||||
|
taskqueue_drain(sc->sc_tq, &mac->mac_intrtask);
|
||||||
|
taskqueue_free(sc->sc_tq);
|
||||||
|
@@ -1094,7 +1106,12 @@ bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||||||
|
tq = bwn_pio_select(mac, M_WME_GETAC(m));
|
||||||
|
KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__));
|
||||||
|
tp = TAILQ_FIRST(&tq->tq_pktlist);
|
||||||
|
- tp->tp_ni = ni;
|
||||||
|
+ /*
|
||||||
|
+ * Take our own reference on the node (see bwn_dma_tx_start for the
|
||||||
|
+ * rationale); the PIO path otherwise shares the same use-after-free
|
||||||
|
+ * hazard as the DMA path.
|
||||||
|
+ */
|
||||||
|
+ tp->tp_ni = ieee80211_ref_node(ni);
|
||||||
|
tp->tp_m = m;
|
||||||
|
|
||||||
|
error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp));
|
||||||
|
@@ -1218,7 +1235,15 @@ bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||||||
|
KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY &&
|
||||||
|
mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__));
|
||||||
|
mt->mt_m = m;
|
||||||
|
- mt->mt_ni = ni;
|
||||||
|
+ /*
|
||||||
|
+ * Take our own reference on the node. The mbuf's rcvif reference
|
||||||
|
+ * belongs to the net80211 stack and may be released (e.g. when the
|
||||||
|
+ * VAP is torn down or a scan is cancelled) while the frame is still
|
||||||
|
+ * pending in the TX ring. Without our own reference the slot would
|
||||||
|
+ * point at a freed node, and freeing it later (on TX completion or
|
||||||
|
+ * at detach) would double-free / use-after-free and panic.
|
||||||
|
+ */
|
||||||
|
+ mt->mt_ni = ieee80211_ref_node(ni);
|
||||||
|
|
||||||
|
error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m,
|
||||||
|
bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT);
|
||||||
|
@@ -1892,6 +1917,33 @@ bwn_update_promisc(struct ieee80211com *ic)
|
||||||
|
BWN_UNLOCK(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void
|
||||||
|
+bwn_update_mcast(struct ieee80211com *ic)
|
||||||
|
+{
|
||||||
|
+ struct bwn_softc *sc = ic->ic_softc;
|
||||||
|
+ struct bwn_mac *mac;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * bwn has no hardware multicast hash filter; the only RX filter bit
|
||||||
|
+ * available for multicast is BWN_MACCTL_PROMISC (the same bit used by
|
||||||
|
+ * bwn_update_promisc). When any VAP needs all-multicast (e.g. for
|
||||||
|
+ * IPv6 ND/MLD), set PROMISC so the frames are passed up to net80211,
|
||||||
|
+ * which then does the multicast filtering in software. Without this
|
||||||
|
+ * callback net80211 uses the null stub and prints
|
||||||
|
+ * "need multicast update callback", and IPv6 multicast breaks.
|
||||||
|
+ */
|
||||||
|
+ BWN_LOCK(sc);
|
||||||
|
+ mac = sc->sc_curmac;
|
||||||
|
+ if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
||||||
|
+ if (ic->ic_allmulti > 0)
|
||||||
|
+ sc->sc_filters |= BWN_MACCTL_PROMISC;
|
||||||
|
+ else
|
||||||
|
+ sc->sc_filters &= ~BWN_MACCTL_PROMISC;
|
||||||
|
+ bwn_set_opmode(mac);
|
||||||
|
+ }
|
||||||
|
+ BWN_UNLOCK(sc);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Callback from the 802.11 layer to update WME parameters.
|
||||||
|
*/
|
||||||
|
@@ -2999,7 +3051,17 @@ static void
|
||||||
|
bwn_dma_ringfree(struct bwn_dma_ring **dr)
|
||||||
|
{
|
||||||
|
|
||||||
|
- if (dr == NULL)
|
||||||
|
+ /*
|
||||||
|
+ * bwn_dma_attach() sets BWN_MAC_FLAG_DMA early, before the individual
|
||||||
|
+ * rings (rx/wme[]/mcast) are allocated via bwn_dma_ringsetup(). If
|
||||||
|
+ * attach fails partway through (or the device is detached after a
|
||||||
|
+ * partial attach), bwn_dma_free() is still invoked because the flag is
|
||||||
|
+ * set, but the ring pointers are still NULL. Dereferencing them here
|
||||||
|
+ * faults (the crash in bwn_dma_ringfree+0x14c reading at offset 0x30,
|
||||||
|
+ * i.e. (*dr)->dr_mac). Guard against a NULL ring so teardown after a
|
||||||
|
+ * partial attach is safe.
|
||||||
|
+ */
|
||||||
|
+ if (dr == NULL || *dr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bwn_dma_free_descbufs(*dr);
|
||||||
|
@@ -3364,8 +3426,18 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||||||
|
struct bwn_softc *sc = mac->mac_sc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
- if (!dr->dr_usedslot)
|
||||||
|
- return;
|
||||||
|
+ /*
|
||||||
|
+ * Do NOT skip this loop based on dr_usedslot. dr_usedslot is only an
|
||||||
|
+ * accounting hint for free-slot tracking and is not a reliable
|
||||||
|
+ * indicator of whether a frame (and its node reference) is pending;
|
||||||
|
+ * after a HW reset / ring re-init it can be inconsistent with the
|
||||||
|
+ * actual slot contents. We must unconditionally walk every slot and
|
||||||
|
+ * release any mbuf + node reference still attached, otherwise a
|
||||||
|
+ * dangling node pointer survives to bwn_dma_free()/detach and
|
||||||
|
+ * double-frees the node (vmcore.5). bwn_dma_free_descbuf() is
|
||||||
|
+ * idempotent (NULLs mt_m/mt_ni and skips empty slots), so walking
|
||||||
|
+ * all slots is safe even when none hold a frame.
|
||||||
|
+ */
|
||||||
|
for (i = 0; i < dr->dr_numslots; i++) {
|
||||||
|
dr->getdesc(dr, i, &desc, &meta);
|
||||||
|
|
||||||
|
@@ -3388,6 +3460,31 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void
|
||||||
|
+bwn_dma_reclaim_tx(struct bwn_mac *mac)
|
||||||
|
+{
|
||||||
|
+ struct bwn_dma *dma;
|
||||||
|
+
|
||||||
|
+ if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0)
|
||||||
|
+ return;
|
||||||
|
+ dma = &mac->mac_method.dma;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Drop the mbuf + ieee80211_node references for any frame still
|
||||||
|
+ * pending in the TX rings. This must be called BEFORE
|
||||||
|
+ * ieee80211_ifdetach() (which frees all nodes) and is safe to
|
||||||
|
+ * call even if the MAC was never fully started, because a
|
||||||
|
+ * frame can be queued in a ring without the MAC reaching
|
||||||
|
+ * STARTED (e.g. the watchdog fired right after a transmit).
|
||||||
|
+ * See Fix 6/7 and vmcore.5 (node double-free) / vmcore.4.
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BK]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BE]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VI]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VO]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->mcast);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int
|
||||||
|
bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base,
|
||||||
|
int type)
|
||||||
|
@@ -7349,6 +7446,45 @@ bwn_dma_stop(struct bwn_mac *mac)
|
||||||
|
bwn_dma_ringstop(&dma->wme[WME_AC_VI]);
|
||||||
|
bwn_dma_ringstop(&dma->wme[WME_AC_VO]);
|
||||||
|
bwn_dma_ringstop(&dma->mcast);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Reclaim any frames still pending in the DMA rings.
|
||||||
|
+ *
|
||||||
|
+ * When TX never completes (e.g. interrupts are not delivered, which
|
||||||
|
+ * on this chip manifests as "device timeout"), the ring slots keep
|
||||||
|
+ * their mbuf + ieee80211_node references until a TX-completion
|
||||||
|
+ * callback (bwn_dma_handle_txeof) runs. bwn_dma_handle_txeof is the
|
||||||
|
+ * ONLY place that clears mt_ni, so without it the references linger
|
||||||
|
+ * here. If net80211 later frees that node (scan cancel / VAP
|
||||||
|
+ * teardown) while we still point at it, the reference becomes
|
||||||
|
+ * dangling; then bwn_dma_free() -> bwn_dma_ringfree() ->
|
||||||
|
+ * bwn_dma_free_descbufs() -> ieee80211_free_node() walks freed
|
||||||
|
+ * memory and panics (vmcore.4: page fault in node_cleanup /
|
||||||
|
+ * ieee80211_ageq_remove).
|
||||||
|
+ *
|
||||||
|
+ * Releasing the pending frames here, at device-stop time, drops the
|
||||||
|
+ * node references while the node is still alive (this runs before
|
||||||
|
+ * ieee80211_ifdetach() during detach and during every HW reset),
|
||||||
|
+ * so no dangling reference survives to detach. bwn_dma_free_descbufs
|
||||||
|
+ * is idempotent (it NULLs mt_m/mt_ni), so the later free at detach
|
||||||
|
+ * simply finds empty slots and skips them.
|
||||||
|
+ *
|
||||||
|
+ * IMPORTANT: only the TX rings (wme[] + mcast) are reclaimed here.
|
||||||
|
+ * The RX ring MUST NOT be reclaimed at stop time: bwn_dma_stop() runs
|
||||||
|
+ * on every HW reset (every "device timeout"), and bwn_dma_free_descbufs()
|
||||||
|
+ * frees the RX mbuf WITHOUT clearing the DMA descriptor's physical
|
||||||
|
+ * address. The RX ring is persistent (it is refilled in place by
|
||||||
|
+ * bwn_rxeof() -> bwn_dma_newbuf(), never re-allocated), so after a
|
||||||
|
+ * reset the descriptor would still point at freed memory and the NIC
|
||||||
|
+ * would DMA received frames into freed kernel memory -> heap
|
||||||
|
+ * corruption (vmcore.6: TCP sb_mb NULL while sb_ccc != 0). Reclaiming
|
||||||
|
+ * the RX ring here also bought nothing: the RX ring holds no node
|
||||||
|
+ * reference, so it cannot cause the node double-free this fix targets.
|
||||||
|
+ *
|
||||||
|
+ * The actual TX-ring reclaim is factored into bwn_dma_reclaim_tx()
|
||||||
|
+ * so it can also be called from bwn_detach() before ieee80211_ifdetach().
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_reclaim_tx(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
diff --git a/sys/modules/bwn/Makefile b/sys/modules/bwn/Makefile
|
||||||
|
index 37c7b35d..962aa268 100644
|
||||||
|
--- a/sys/modules/bwn/Makefile
|
||||||
|
+++ b/sys/modules/bwn/Makefile
|
||||||
|
@@ -24,15 +24,33 @@ SRCS+= bhndb_bus_if.h \
|
||||||
|
# Other
|
||||||
|
SRCS+= device_if.h bus_if.h gpio_if.h pci_if.h opt_bwn.h opt_wlan.h
|
||||||
|
|
||||||
|
-# The following need the BWN_GPL_PHY kenrel option to opt-in
|
||||||
|
-# to the GPL'd 802.11n PHY support for this driver.
|
||||||
|
+# BWN_GPL_PHY: enable GPLv2 802.11n PHY support (required for N-PHY chips like BCM43224).
|
||||||
|
+# Without this, bwn(4) attaches but bwn_phy_n_attach() fails with:
|
||||||
|
+# "BWN_GPL_PHY not in kernel config; no PHY-N support".
|
||||||
|
+# NOTE: We define it here (not via the kernel config) because this is a
|
||||||
|
+# standalone module build; bsd.kmod.mk only honours SRCS.<OPT> when <OPT> is
|
||||||
|
+# present in KERN_OPTS, which it is not for out-of-tree module builds.
|
||||||
|
+opt_bwn.h:
|
||||||
|
+ @echo "#define BWN_GPL_PHY 1" > ${.TARGET}
|
||||||
|
+
|
||||||
|
+# The following are the GPL'd 802.11n PHY support files for this driver.
|
||||||
|
+# They MUST be added unconditionally to SRCS (not via SRCS.BWN_GPL_PHY),
|
||||||
|
+# because a standalone module build does not populate KERN_OPTS with
|
||||||
|
+# BWN_GPL_PHY, so the SRCS.<OPT> mechanism would skip them entirely.
|
||||||
|
.PATH: ${SRCTOP}/sys/gnu/dev/bwn/phy_n
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2055.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2056.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2057.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_sprom.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_tables.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_ppr.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_core.c
|
||||||
|
+SRCS+= if_bwn_radio_2055.c
|
||||||
|
+SRCS+= if_bwn_radio_2056.c
|
||||||
|
+SRCS+= if_bwn_radio_2057.c
|
||||||
|
+SRCS+= if_bwn_phy_n_sprom.c
|
||||||
|
+SRCS+= if_bwn_phy_n_tables.c
|
||||||
|
+SRCS+= if_bwn_phy_n_ppr.c
|
||||||
|
+SRCS+= if_bwn_phy_n_core.c
|
||||||
|
|
||||||
|
.include <bsd.kmod.mk>
|
||||||
|
+
|
||||||
|
+# Temporary debug build
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
|
+
|
||||||
|
+# Temporary debug build
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
diff --git a/sys/modules/bwn/Makefile b/sys/modules/bwn/Makefile
|
||||||
|
index 37c7b35d..962aa268 100644
|
||||||
|
--- a/sys/modules/bwn/Makefile
|
||||||
|
+++ b/sys/modules/bwn/Makefile
|
||||||
|
@@ -24,15 +24,33 @@ SRCS+= bhndb_bus_if.h \
|
||||||
|
# Other
|
||||||
|
SRCS+= device_if.h bus_if.h gpio_if.h pci_if.h opt_bwn.h opt_wlan.h
|
||||||
|
|
||||||
|
-# The following need the BWN_GPL_PHY kenrel option to opt-in
|
||||||
|
-# to the GPL'd 802.11n PHY support for this driver.
|
||||||
|
+# BWN_GPL_PHY: enable GPLv2 802.11n PHY support (required for N-PHY chips like BCM43224).
|
||||||
|
+# Without this, bwn(4) attaches but bwn_phy_n_attach() fails with:
|
||||||
|
+# "BWN_GPL_PHY not in kernel config; no PHY-N support".
|
||||||
|
+# NOTE: We define it here (not via the kernel config) because this is a
|
||||||
|
+# standalone module build; bsd.kmod.mk only honours SRCS.<OPT> when <OPT> is
|
||||||
|
+# present in KERN_OPTS, which it is not for out-of-tree module builds.
|
||||||
|
+opt_bwn.h:
|
||||||
|
+ @echo "#define BWN_GPL_PHY 1" > ${.TARGET}
|
||||||
|
+
|
||||||
|
+# The following are the GPL'd 802.11n PHY support files for this driver.
|
||||||
|
+# They MUST be added unconditionally to SRCS (not via SRCS.BWN_GPL_PHY),
|
||||||
|
+# because a standalone module build does not populate KERN_OPTS with
|
||||||
|
+# BWN_GPL_PHY, so the SRCS.<OPT> mechanism would skip them entirely.
|
||||||
|
.PATH: ${SRCTOP}/sys/gnu/dev/bwn/phy_n
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2055.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2056.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_radio_2057.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_sprom.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_tables.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_ppr.c
|
||||||
|
-SRCS.BWN_GPL_PHY+= if_bwn_phy_n_core.c
|
||||||
|
+SRCS+= if_bwn_radio_2055.c
|
||||||
|
+SRCS+= if_bwn_radio_2056.c
|
||||||
|
+SRCS+= if_bwn_radio_2057.c
|
||||||
|
+SRCS+= if_bwn_phy_n_sprom.c
|
||||||
|
+SRCS+= if_bwn_phy_n_tables.c
|
||||||
|
+SRCS+= if_bwn_phy_n_ppr.c
|
||||||
|
+SRCS+= if_bwn_phy_n_core.c
|
||||||
|
|
||||||
|
.include <bsd.kmod.mk>
|
||||||
|
+
|
||||||
|
+# Temporary debug build
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
|
+
|
||||||
|
+# Temporary debug build
|
||||||
|
+CFLAGS+= -DBWN_DEBUG
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
diff --git a/sys/dev/bhnd/bhndb/bhndb_pci.c b/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
index 0cdcba1d..7b6ae0f3 100644
|
||||||
|
--- a/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
+++ b/sys/dev/bhnd/bhndb/bhndb_pci.c
|
||||||
|
@@ -195,6 +195,16 @@ static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
|
||||||
|
static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
|
||||||
|
/* All PCIe-G1 core revisions require the SRSH work-around */
|
||||||
|
BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR),
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * BCM43224 (and other Broadcom PCIe-G1 endpoints) do not have MSI
|
||||||
|
+ * delivered on some host platforms (e.g. NVIDIA MCP89 based Apple
|
||||||
|
+ * MacBook Air); force legacy INTx instead.
|
||||||
|
+ */
|
||||||
|
+ { { BHND_MATCH_CHIP_ID(BCM43224) },
|
||||||
|
+ { BHND_MATCH_ANY },
|
||||||
|
+ BHNDB_PCI_QUIRK_NO_MSI },
|
||||||
|
+
|
||||||
|
BHNDB_PCI_QUIRK_END
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -327,6 +337,13 @@ bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count)
|
||||||
|
{
|
||||||
|
int error, count;
|
||||||
|
|
||||||
|
+ /* MSI disabled by bridge quirk? */
|
||||||
|
+ if (sc->pci_quirks & BHNDB_PCI_QUIRK_NO_MSI) {
|
||||||
|
+ device_printf(sc->dev, "MSI disabled by quirk; using INTx on "
|
||||||
|
+ "%s\n", device_get_nameunit(sc->parent));
|
||||||
|
+ return (ENXIO);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* Is MSI available? */
|
||||||
|
if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
|
||||||
|
return (ENXIO);
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
diff --git a/sys/dev/bhnd/bhndb/bhndb_pcivar.h b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
index ed749518..7b473fa6 100644
|
||||||
|
--- a/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
+++ b/sys/dev/bhnd/bhndb/bhndb_pcivar.h
|
||||||
|
@@ -70,6 +70,14 @@ enum {
|
||||||
|
* interrupt flags via the SIBA_CFG0_INTVEC register.
|
||||||
|
*/
|
||||||
|
BHNDB_PCI_QUIRK_SIBA_INTVEC = (1<<1),
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * The PCI/PCIe bridge must not use MSI; instead, fall back to legacy
|
||||||
|
+ * INTx line interrupts. Required on some systems (e.g. NVIDIA MCP89
|
||||||
|
+ * based Apple MacBook Air) where MSI messages from the Broadcom
|
||||||
|
+ * PCIe-G1 endpoint are never delivered, causing device timeouts.
|
||||||
|
+ */
|
||||||
|
+ BHNDB_PCI_QUIRK_NO_MSI = (1<<2),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** bhndb_pci quirk table entry */
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
diff --git a/sys/dev/bwn/if_bwn.c b/sys/dev/bwn/if_bwn.c
|
||||||
|
index 38bf6f5d..7a5b4230 100644
|
||||||
|
--- a/sys/dev/bwn/if_bwn.c
|
||||||
|
+++ b/sys/dev/bwn/if_bwn.c
|
||||||
|
@@ -147,6 +147,7 @@ static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
||||||
|
const struct ieee80211_bpf_params *);
|
||||||
|
static void bwn_updateslot(struct ieee80211com *);
|
||||||
|
static void bwn_update_promisc(struct ieee80211com *);
|
||||||
|
+static void bwn_update_mcast(struct ieee80211com *);
|
||||||
|
static void bwn_wme_init(struct bwn_mac *);
|
||||||
|
static int bwn_wme_update(struct ieee80211com *);
|
||||||
|
static void bwn_wme_clear(struct bwn_softc *);
|
||||||
|
@@ -250,6 +251,7 @@ static void bwn_dma_setup(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_free_ringmemory(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_cleanup(struct bwn_dma_ring *);
|
||||||
|
static void bwn_dma_free_descbufs(struct bwn_dma_ring *);
|
||||||
|
+static void bwn_dma_reclaim_tx(struct bwn_mac *);
|
||||||
|
static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int);
|
||||||
|
static void bwn_dma_rx(struct bwn_dma_ring *);
|
||||||
|
static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int);
|
||||||
|
@@ -812,6 +814,7 @@ bwn_attach_post(struct bwn_softc *sc)
|
||||||
|
ic->ic_raw_xmit = bwn_raw_xmit;
|
||||||
|
ic->ic_updateslot = bwn_updateslot;
|
||||||
|
ic->ic_update_promisc = bwn_update_promisc;
|
||||||
|
+ ic->ic_update_mcast = bwn_update_mcast;
|
||||||
|
ic->ic_wme.wme_update = bwn_wme_update;
|
||||||
|
ic->ic_scan_start = bwn_scan_start;
|
||||||
|
ic->ic_scan_end = bwn_scan_end;
|
||||||
|
@@ -855,7 +858,6 @@ bwn_detach(device_t dev)
|
||||||
|
BWN_LOCK(sc);
|
||||||
|
bwn_stop(sc);
|
||||||
|
BWN_UNLOCK(sc);
|
||||||
|
- bwn_dma_free(mac);
|
||||||
|
callout_drain(&sc->sc_led_blink_ch);
|
||||||
|
callout_drain(&sc->sc_rfswitch_ch);
|
||||||
|
callout_drain(&sc->sc_task_ch);
|
||||||
|
@@ -863,7 +865,17 @@ bwn_detach(device_t dev)
|
||||||
|
bwn_phy_detach(mac);
|
||||||
|
ieee80211_draintask(ic, &mac->mac_hwreset);
|
||||||
|
ieee80211_draintask(ic, &mac->mac_txpower);
|
||||||
|
+ /*
|
||||||
|
+ * Drop any TX-ring node references BEFORE ieee80211_ifdetach()
|
||||||
|
+ * frees all nodes. bwn_dma_free()/detach would otherwise find a
|
||||||
|
+ * dangling node pointer (the ring slot still holds mt_ni for a
|
||||||
|
+ * frame whose TX completion never ran) and double-free it
|
||||||
|
+ * (vmcore.5). This is independent of mac_status: a frame can be
|
||||||
|
+ * queued in a ring without the MAC ever reaching STARTED.
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_reclaim_tx(mac);
|
||||||
|
ieee80211_ifdetach(ic);
|
||||||
|
+ bwn_dma_free(mac);
|
||||||
|
}
|
||||||
|
taskqueue_drain(sc->sc_tq, &mac->mac_intrtask);
|
||||||
|
taskqueue_free(sc->sc_tq);
|
||||||
|
@@ -1094,7 +1106,12 @@ bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||||||
|
tq = bwn_pio_select(mac, M_WME_GETAC(m));
|
||||||
|
KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__));
|
||||||
|
tp = TAILQ_FIRST(&tq->tq_pktlist);
|
||||||
|
- tp->tp_ni = ni;
|
||||||
|
+ /*
|
||||||
|
+ * Take our own reference on the node (see bwn_dma_tx_start for the
|
||||||
|
+ * rationale); the PIO path otherwise shares the same use-after-free
|
||||||
|
+ * hazard as the DMA path.
|
||||||
|
+ */
|
||||||
|
+ tp->tp_ni = ieee80211_ref_node(ni);
|
||||||
|
tp->tp_m = m;
|
||||||
|
|
||||||
|
error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp));
|
||||||
|
@@ -1218,7 +1235,15 @@ bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||||||
|
KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY &&
|
||||||
|
mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__));
|
||||||
|
mt->mt_m = m;
|
||||||
|
- mt->mt_ni = ni;
|
||||||
|
+ /*
|
||||||
|
+ * Take our own reference on the node. The mbuf's rcvif reference
|
||||||
|
+ * belongs to the net80211 stack and may be released (e.g. when the
|
||||||
|
+ * VAP is torn down or a scan is cancelled) while the frame is still
|
||||||
|
+ * pending in the TX ring. Without our own reference the slot would
|
||||||
|
+ * point at a freed node, and freeing it later (on TX completion or
|
||||||
|
+ * at detach) would double-free / use-after-free and panic.
|
||||||
|
+ */
|
||||||
|
+ mt->mt_ni = ieee80211_ref_node(ni);
|
||||||
|
|
||||||
|
error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m,
|
||||||
|
bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT);
|
||||||
|
@@ -1892,6 +1917,33 @@ bwn_update_promisc(struct ieee80211com *ic)
|
||||||
|
BWN_UNLOCK(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void
|
||||||
|
+bwn_update_mcast(struct ieee80211com *ic)
|
||||||
|
+{
|
||||||
|
+ struct bwn_softc *sc = ic->ic_softc;
|
||||||
|
+ struct bwn_mac *mac;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * bwn has no hardware multicast hash filter; the only RX filter bit
|
||||||
|
+ * available for multicast is BWN_MACCTL_PROMISC (the same bit used by
|
||||||
|
+ * bwn_update_promisc). When any VAP needs all-multicast (e.g. for
|
||||||
|
+ * IPv6 ND/MLD), set PROMISC so the frames are passed up to net80211,
|
||||||
|
+ * which then does the multicast filtering in software. Without this
|
||||||
|
+ * callback net80211 uses the null stub and prints
|
||||||
|
+ * "need multicast update callback", and IPv6 multicast breaks.
|
||||||
|
+ */
|
||||||
|
+ BWN_LOCK(sc);
|
||||||
|
+ mac = sc->sc_curmac;
|
||||||
|
+ if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
||||||
|
+ if (ic->ic_allmulti > 0)
|
||||||
|
+ sc->sc_filters |= BWN_MACCTL_PROMISC;
|
||||||
|
+ else
|
||||||
|
+ sc->sc_filters &= ~BWN_MACCTL_PROMISC;
|
||||||
|
+ bwn_set_opmode(mac);
|
||||||
|
+ }
|
||||||
|
+ BWN_UNLOCK(sc);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Callback from the 802.11 layer to update WME parameters.
|
||||||
|
*/
|
||||||
|
@@ -2999,7 +3051,17 @@ static void
|
||||||
|
bwn_dma_ringfree(struct bwn_dma_ring **dr)
|
||||||
|
{
|
||||||
|
|
||||||
|
- if (dr == NULL)
|
||||||
|
+ /*
|
||||||
|
+ * bwn_dma_attach() sets BWN_MAC_FLAG_DMA early, before the individual
|
||||||
|
+ * rings (rx/wme[]/mcast) are allocated via bwn_dma_ringsetup(). If
|
||||||
|
+ * attach fails partway through (or the device is detached after a
|
||||||
|
+ * partial attach), bwn_dma_free() is still invoked because the flag is
|
||||||
|
+ * set, but the ring pointers are still NULL. Dereferencing them here
|
||||||
|
+ * faults (the crash in bwn_dma_ringfree+0x14c reading at offset 0x30,
|
||||||
|
+ * i.e. (*dr)->dr_mac). Guard against a NULL ring so teardown after a
|
||||||
|
+ * partial attach is safe.
|
||||||
|
+ */
|
||||||
|
+ if (dr == NULL || *dr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bwn_dma_free_descbufs(*dr);
|
||||||
|
@@ -3364,8 +3426,18 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||||||
|
struct bwn_softc *sc = mac->mac_sc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
- if (!dr->dr_usedslot)
|
||||||
|
- return;
|
||||||
|
+ /*
|
||||||
|
+ * Do NOT skip this loop based on dr_usedslot. dr_usedslot is only an
|
||||||
|
+ * accounting hint for free-slot tracking and is not a reliable
|
||||||
|
+ * indicator of whether a frame (and its node reference) is pending;
|
||||||
|
+ * after a HW reset / ring re-init it can be inconsistent with the
|
||||||
|
+ * actual slot contents. We must unconditionally walk every slot and
|
||||||
|
+ * release any mbuf + node reference still attached, otherwise a
|
||||||
|
+ * dangling node pointer survives to bwn_dma_free()/detach and
|
||||||
|
+ * double-frees the node (vmcore.5). bwn_dma_free_descbuf() is
|
||||||
|
+ * idempotent (NULLs mt_m/mt_ni and skips empty slots), so walking
|
||||||
|
+ * all slots is safe even when none hold a frame.
|
||||||
|
+ */
|
||||||
|
for (i = 0; i < dr->dr_numslots; i++) {
|
||||||
|
dr->getdesc(dr, i, &desc, &meta);
|
||||||
|
|
||||||
|
@@ -3388,6 +3460,31 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void
|
||||||
|
+bwn_dma_reclaim_tx(struct bwn_mac *mac)
|
||||||
|
+{
|
||||||
|
+ struct bwn_dma *dma;
|
||||||
|
+
|
||||||
|
+ if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0)
|
||||||
|
+ return;
|
||||||
|
+ dma = &mac->mac_method.dma;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Drop the mbuf + ieee80211_node references for any frame still
|
||||||
|
+ * pending in the TX rings. This must be called BEFORE
|
||||||
|
+ * ieee80211_ifdetach() (which frees all nodes) and is safe to
|
||||||
|
+ * call even if the MAC was never fully started, because a
|
||||||
|
+ * frame can be queued in a ring without the MAC reaching
|
||||||
|
+ * STARTED (e.g. the watchdog fired right after a transmit).
|
||||||
|
+ * See Fix 6/7 and vmcore.5 (node double-free) / vmcore.4.
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BK]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BE]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VI]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VO]);
|
||||||
|
+ bwn_dma_free_descbufs(dma->mcast);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int
|
||||||
|
bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base,
|
||||||
|
int type)
|
||||||
|
@@ -7349,6 +7446,45 @@ bwn_dma_stop(struct bwn_mac *mac)
|
||||||
|
bwn_dma_ringstop(&dma->wme[WME_AC_VI]);
|
||||||
|
bwn_dma_ringstop(&dma->wme[WME_AC_VO]);
|
||||||
|
bwn_dma_ringstop(&dma->mcast);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Reclaim any frames still pending in the DMA rings.
|
||||||
|
+ *
|
||||||
|
+ * When TX never completes (e.g. interrupts are not delivered, which
|
||||||
|
+ * on this chip manifests as "device timeout"), the ring slots keep
|
||||||
|
+ * their mbuf + ieee80211_node references until a TX-completion
|
||||||
|
+ * callback (bwn_dma_handle_txeof) runs. bwn_dma_handle_txeof is the
|
||||||
|
+ * ONLY place that clears mt_ni, so without it the references linger
|
||||||
|
+ * here. If net80211 later frees that node (scan cancel / VAP
|
||||||
|
+ * teardown) while we still point at it, the reference becomes
|
||||||
|
+ * dangling; then bwn_dma_free() -> bwn_dma_ringfree() ->
|
||||||
|
+ * bwn_dma_free_descbufs() -> ieee80211_free_node() walks freed
|
||||||
|
+ * memory and panics (vmcore.4: page fault in node_cleanup /
|
||||||
|
+ * ieee80211_ageq_remove).
|
||||||
|
+ *
|
||||||
|
+ * Releasing the pending frames here, at device-stop time, drops the
|
||||||
|
+ * node references while the node is still alive (this runs before
|
||||||
|
+ * ieee80211_ifdetach() during detach and during every HW reset),
|
||||||
|
+ * so no dangling reference survives to detach. bwn_dma_free_descbufs
|
||||||
|
+ * is idempotent (it NULLs mt_m/mt_ni), so the later free at detach
|
||||||
|
+ * simply finds empty slots and skips them.
|
||||||
|
+ *
|
||||||
|
+ * IMPORTANT: only the TX rings (wme[] + mcast) are reclaimed here.
|
||||||
|
+ * The RX ring MUST NOT be reclaimed at stop time: bwn_dma_stop() runs
|
||||||
|
+ * on every HW reset (every "device timeout"), and bwn_dma_free_descbufs()
|
||||||
|
+ * frees the RX mbuf WITHOUT clearing the DMA descriptor's physical
|
||||||
|
+ * address. The RX ring is persistent (it is refilled in place by
|
||||||
|
+ * bwn_rxeof() -> bwn_dma_newbuf(), never re-allocated), so after a
|
||||||
|
+ * reset the descriptor would still point at freed memory and the NIC
|
||||||
|
+ * would DMA received frames into freed kernel memory -> heap
|
||||||
|
+ * corruption (vmcore.6: TCP sb_mb NULL while sb_ccc != 0). Reclaiming
|
||||||
|
+ * the RX ring here also bought nothing: the RX ring holds no node
|
||||||
|
+ * reference, so it cannot cause the node double-free this fix targets.
|
||||||
|
+ *
|
||||||
|
+ * The actual TX-ring reclaim is factored into bwn_dma_reclaim_tx()
|
||||||
|
+ * so it can also be called from bwn_detach() before ieee80211_ifdetach().
|
||||||
|
+ */
|
||||||
|
+ bwn_dma_reclaim_tx(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
hint.asmc.0.disabled="1"
|
||||||
|
acpi_video_load="YES"
|
||||||
|
acpi_ibm_load="YES"
|
||||||
|
backlight_load="YES"
|
||||||
|
snd_driver_load="YES"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
clear_tmp_enable="YES"
|
||||||
|
hostname="luxair"
|
||||||
|
wlans_rtwn0="wlan0"
|
||||||
|
ifconfig_wlan0="WPA DHCP"
|
||||||
|
ifconfig_wlan0_ipv6="inet6 accept_rtadv"
|
||||||
|
sshd_enable="YES"
|
||||||
|
ntpd_enable="YES"
|
||||||
|
powerd_enable="YES"
|
||||||
|
moused_enable="YES"
|
||||||
|
# Set dumpdev to AUTO to enable crash dumps
|
||||||
|
dumpdev="AUTO"
|
||||||
|
kld_list="nouveau bwn_v4_n if_bwn"
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,17 @@
|
|||||||
|
clear_tmp_enable="YES"
|
||||||
|
hostname="luxair"
|
||||||
|
wlans_rtwn0="wlan0"
|
||||||
|
ifconfig_wlan0="WPA DHCP"
|
||||||
|
ifconfig_wlan0_ipv6="inet6 accept_rtadv"
|
||||||
|
sshd_enable="YES"
|
||||||
|
ntpd_enable="YES"
|
||||||
|
powerd_enable="YES"
|
||||||
|
moused_enable="YES"
|
||||||
|
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
|
||||||
|
dumpdev="AUTO"
|
||||||
|
|
||||||
|
kld_list="nouveau"
|
||||||
|
|
||||||
|
# Broadcom BCM43224 (bwn) — switch primary Wi-Fi from rtwn0 USB dongle to internal bwn0
|
||||||
|
wlans_bwn0="wlan0"
|
||||||
|
kld_list="nouveau bwn_v4_n if_bwn"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
KMOD= bwn_v4_n
|
||||||
|
FIRMWS= ucode16_mimo.fw:bwn_v4_ucode16_mimo \
|
||||||
|
n0initvals11.fw:bwn_v4_n0initvals11 \
|
||||||
|
n0bsinitvals11.fw:bwn_v4_n0bsinitvals11 \
|
||||||
|
n0initvals16.fw:bwn_v4_n0initvals16 \
|
||||||
|
n0bsinitvals16.fw:bwn_v4_n0bsinitvals16 \
|
||||||
|
n0initvals24.fw:bwn_v4_n0initvals24 \
|
||||||
|
n0bsinitvals24.fw:bwn_v4_n0bsinitvals24
|
||||||
|
.include <bsd.kmod.mk>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
KMOD= bwn_v4_n
|
||||||
|
FIRMWS= ucode16_mimo.fw:bwn_v4_n_ucode16_mimo n0initvals11.fw:bwn_v4_n_n0initvals11 n0bsinitvals11.fw:bwn_v4_n_n0bsinitvals11 n0initvals16.fw:bwn_v4_n_n0initvals16 n0initvals24.fw:bwn_v4_n_n0initvals24 n0bsinitvals16.fw:bwn_v4_n_n0bsinitvals16 n0bsinitvals24.fw:bwn_v4_n_n0bsinitvals24
|
||||||
|
.include <bsd.kmod.mk>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/* * Automatically @generated */
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/linker.h>
|
||||||
|
#include <sys/firmware.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
|
|
||||||
|
extern char _binary_ucode16_mimo_fw_start[], _binary_ucode16_mimo_fw_end[];
|
||||||
|
extern char _binary_n0initvals11_fw_start[], _binary_n0initvals11_fw_end[];
|
||||||
|
extern char _binary_n0bsinitvals11_fw_start[], _binary_n0bsinitvals11_fw_end[];
|
||||||
|
extern char _binary_n0initvals16_fw_start[], _binary_n0initvals16_fw_end[];
|
||||||
|
extern char _binary_n0bsinitvals16_fw_start[], _binary_n0bsinitvals16_fw_end[];
|
||||||
|
extern char _binary_n0initvals24_fw_start[], _binary_n0initvals24_fw_end[];
|
||||||
|
extern char _binary_n0bsinitvals24_fw_start[], _binary_n0bsinitvals24_fw_end[];
|
||||||
|
|
||||||
|
static int
|
||||||
|
bwn_v4_n_fw_modevent(module_t mod, int type, void *unused){ const struct firmware *fp;
|
||||||
|
const struct firmware *parent;
|
||||||
|
int error; switch (type) { case MOD_LOAD:
|
||||||
|
|
||||||
|
fp = firmware_register("bwn_v4_ucode16_mimo", _binary_ucode16_mimo_fw_start , (size_t)(_binary_ucode16_mimo_fw_end - _binary_ucode16_mimo_fw_start), 0, NULL);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_0;
|
||||||
|
parent = fp;
|
||||||
|
fp = firmware_register("bwn_v4_n0initvals11", _binary_n0initvals11_fw_start , (size_t)(_binary_n0initvals11_fw_end - _binary_n0initvals11_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_1;
|
||||||
|
fp = firmware_register("bwn_v4_n0bsinitvals11", _binary_n0bsinitvals11_fw_start , (size_t)(_binary_n0bsinitvals11_fw_end - _binary_n0bsinitvals11_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_2;
|
||||||
|
fp = firmware_register("bwn_v4_n0initvals16", _binary_n0initvals16_fw_start , (size_t)(_binary_n0initvals16_fw_end - _binary_n0initvals16_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_3;
|
||||||
|
fp = firmware_register("bwn_v4_n0bsinitvals16", _binary_n0bsinitvals16_fw_start , (size_t)(_binary_n0bsinitvals16_fw_end - _binary_n0bsinitvals16_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_4;
|
||||||
|
fp = firmware_register("bwn_v4_n0initvals24", _binary_n0initvals24_fw_start , (size_t)(_binary_n0initvals24_fw_end - _binary_n0initvals24_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_5;
|
||||||
|
fp = firmware_register("bwn_v4_n0bsinitvals24", _binary_n0bsinitvals24_fw_start , (size_t)(_binary_n0bsinitvals24_fw_end - _binary_n0bsinitvals24_fw_start), 0, parent);
|
||||||
|
if (fp == NULL)
|
||||||
|
goto fail_6;
|
||||||
|
return (0);
|
||||||
|
fail_6:
|
||||||
|
(void)firmware_unregister("bwn_v4_n0initvals24");
|
||||||
|
fail_5:
|
||||||
|
(void)firmware_unregister("bwn_v4_n0bsinitvals16");
|
||||||
|
fail_4:
|
||||||
|
(void)firmware_unregister("bwn_v4_n0initvals16");
|
||||||
|
fail_3:
|
||||||
|
(void)firmware_unregister("bwn_v4_n0bsinitvals11");
|
||||||
|
fail_2:
|
||||||
|
(void)firmware_unregister("bwn_v4_n0initvals11");
|
||||||
|
fail_1:
|
||||||
|
(void)firmware_unregister("bwn_v4_ucode16_mimo");
|
||||||
|
fail_0:
|
||||||
|
return (ENXIO);
|
||||||
|
case MOD_UNLOAD:
|
||||||
|
error = firmware_unregister("bwn_v4_n0initvals11");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_n0bsinitvals11");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_n0initvals16");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_n0bsinitvals16");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_n0initvals24");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_n0bsinitvals24");
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
error = firmware_unregister("bwn_v4_ucode16_mimo");
|
||||||
|
return (error); } return (EINVAL);}static moduledata_t bwn_v4_n_fw_mod = { "bwn_v4_n_fw", bwn_v4_n_fw_modevent, 0};DECLARE_MODULE(bwn_v4_n_fw, bwn_v4_n_fw_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);MODULE_VERSION(bwn_v4_n_fw, 1);MODULE_DEPEND(bwn_v4_n_fw, firmware, 1, 1, 1);
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user