Apply the Broadcom Wi-Fi quirk to Macs without a T2 (#6652)
* Apply the Broadcom Wi-Fi quirk to Macs without a T2 brcmfmac lets the Wi-Fi firmware run the WPA handshake itself, and on Apple hardware that offload fails against an access point in WPA2/WPA3 transition mode: the client associates, the four-way handshake never completes, and NetworkManager reports the password as wrong. feature_disable=0x82000 turns off the firmware supplicant and authenticator so wpa_supplicant does the handshake in software. That quirk already shipped, but only for Macs with a T2 chip. The bug is in the Broadcom firmware rather than in the T2 bridge, so it was never the right thing to gate on: a MacBookPro11,4 has BCM43602 with 2015 firmware, fails exactly this way, and got nothing. Gate on the hardware that actually has the firmware — an Apple machine with a Broadcom wireless part — which covers both. Moving it out of fix-t2.sh also leaves one owner for the file. Two leaves writing the same config would have meant the later one silently winning, decided by an ordering in all.sh nobody would think to check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Gate the Broadcom Wi-Fi quirk on the T2 ID or a brcmfmac chip ID Sniffing lspci for an Apple vendor with a Broadcom network controller made T2 Macs depend on a detection line they never needed: they carry a T2 PCI ID that is always there, and the class name half of `lspci -nn` comes from the pci.ids database. Keep their original gate untouched. Naming the rest by DMI model does not hold up either, because the model year does not predict the part. A MacBookPro11,4 from Mid 2015 carries a BCM43602 and needs this; a MacBookAir7,2 from Early 2015 carries a BCM4360 and does not. Covering the lineup by name takes around twenty identifiers across four product lines and grows every time Apple ships hardware. The set has an exact definition already: the PCI IDs brcmfmac binds, from the driver's own brcm_hw_ids.h. That reaches the 2016 and 2017 MacBook Pros and the T2-less iMac19,1 and iMac19,2 that a hand-written list missed, and it leaves out the BCM4360 Macs for free, since their out-of-tree wl driver would never read a brcmfmac option anyway. Matching an exact vendor:device ID also drops the piped `grep -q`, which returns 141 under pipefail once the producer is killed by SIGPIPE (#6608). The test runs the leaf with pipefail so the chatty lspci stub proves it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fix Macs already installed without the Broadcom Wi-Fi quirk The quirk is written at install time, so a machine set up before it shipped never gets it, and no pre-T2 Mac ever did. Those installs still fail the WPA four-way handshake against an access point in WPA2/WPA3 transition mode, which is the state the reporter had to repair by hand. Appending leaves anything else in the config alone: modprobe reads every options line for a module, and nothing else sets feature_disable. Only an active options line counts as already applied, and the driver keeps the old behaviour until it reloads, so this asks for a reboot rather than pulling brcmfmac out from under a connection that currently works. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: David Heinemeier Hansson <david@hey.com>
This commit is contained in:
co-authored by
Claude Opus 5
David Heinemeier Hansson
parent
8bc854069b
commit
186668a70f
@@ -35,6 +35,7 @@ run_logged "$OMARCHY_INSTALL/hardware/framework/qmk-hid.sh"
|
|||||||
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-spi-keyboard.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-spi-keyboard.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-suspend-nvme.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-suspend-nvme.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-t2.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-t2.sh"
|
||||||
|
run_logged "$OMARCHY_INSTALL/hardware/apple/fix-brcmfmac-supplicant.sh"
|
||||||
|
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/lenovo/fix-yoga-pro7-bass-speakers.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/lenovo/fix-yoga-pro7-bass-speakers.sh"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Apple Macs ship Broadcom Wi-Fi driven by brcmfmac, whose firmware runs the WPA
|
||||||
|
# handshake itself. On these parts that offload fails against an access point in
|
||||||
|
# WPA2/WPA3 transition mode: the client associates, the four-way handshake never
|
||||||
|
# completes, and NetworkManager reports the password as wrong.
|
||||||
|
#
|
||||||
|
# feature_disable turns off the firmware supplicant (FWSUP, 0x2000) and firmware
|
||||||
|
# authenticator (FWAUTH, 0x80000), handing the handshake back to wpa_supplicant
|
||||||
|
# in software.
|
||||||
|
#
|
||||||
|
# This quirk already shipped for T2 Macs, detected by the T2 PCI ID that every
|
||||||
|
# one of them carries. The bug is in the Broadcom firmware, not in the T2 bridge,
|
||||||
|
# so every Mac whose Wi-Fi brcmfmac drives needs it: a MacBookPro11,4 with
|
||||||
|
# BCM43602 and 2015 firmware fails exactly this way, and connects once the
|
||||||
|
# offload is disabled.
|
||||||
|
#
|
||||||
|
# The IDs are brcmfmac's own, from brcm_hw_ids.h: BCM43602 and its single-band
|
||||||
|
# variants in 2015-2017 Macs, BCM4350, BCM4355 and BCM4364 in the 2018-2019
|
||||||
|
# machines including the T2-less iMac19,1 and iMac19,2, and BCM4377/4378/4387
|
||||||
|
# from the T2 era on. The BCM4360 in 2013-2015 Macs is deliberately absent: it
|
||||||
|
# runs the out-of-tree wl driver, which never reads a brcmfmac option.
|
||||||
|
sys_vendor="$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if lspci -nn | grep "106b:180[12]" >/dev/null ||
|
||||||
|
{ [[ $sys_vendor == Apple* ]] &&
|
||||||
|
lspci -nn | grep -E "14e4:(43ba|43bb|43bc|43a3|43dc|4464|4488|4425|4433)" >/dev/null; }; then
|
||||||
|
echo "Detected a Mac with Broadcom Wi-Fi; running the WPA handshake in software"
|
||||||
|
|
||||||
|
mkdir -p /etc/modprobe.d
|
||||||
|
cat > /etc/modprobe.d/brcmfmac.conf <<'EOF'
|
||||||
|
# Broadcom's firmware supplicant and authenticator fail the WPA four-way
|
||||||
|
# handshake on Apple hardware, which surfaces as a rejected password. Disable
|
||||||
|
# both so wpa_supplicant performs the handshake instead.
|
||||||
|
options brcmfmac feature_disable=0x82000
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
@@ -26,12 +26,6 @@ if lspci -nn | grep "106b:180[12]" >/dev/null; then
|
|||||||
echo "MODULES+=(t2bce_vhci usbhid hid_apple hid_generic xhci_pci xhci_hcd)" > \
|
echo "MODULES+=(t2bce_vhci usbhid hid_apple hid_generic xhci_pci xhci_hcd)" > \
|
||||||
/etc/mkinitcpio.conf.d/apple-t2.conf
|
/etc/mkinitcpio.conf.d/apple-t2.conf
|
||||||
|
|
||||||
mkdir -p /etc/modprobe.d
|
|
||||||
cat > /etc/modprobe.d/brcmfmac.conf <<'EOF'
|
|
||||||
# Fix for T2 MacBook WiFi connectivity issues
|
|
||||||
options brcmfmac feature_disable=0x82000
|
|
||||||
EOF
|
|
||||||
|
|
||||||
mkdir -p /etc/limine-entry-tool.d
|
mkdir -p /etc/limine-entry-tool.d
|
||||||
cat > /etc/limine-entry-tool.d/t2-mac.conf <<'EOF'
|
cat > /etc/limine-entry-tool.d/t2-mac.conf <<'EOF'
|
||||||
# Generated by Omarchy installer for T2 Mac support
|
# Generated by Omarchy installer for T2 Mac support
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
echo "Run the WPA handshake in software on Macs with Broadcom Wi-Fi"
|
||||||
|
|
||||||
|
# The install-time quirk only reaches machines set up after it shipped, and it
|
||||||
|
# never covered Macs without a T2 at all, so an existing install on one still
|
||||||
|
# cannot join a WPA2/WPA3 transition-mode network. See
|
||||||
|
# install/hardware/apple/fix-brcmfmac-supplicant.sh for the failure it fixes and
|
||||||
|
# for where this list of brcmfmac PCI IDs comes from.
|
||||||
|
dmi_vendor="${OMARCHY_BRCMFMAC_DMI_VENDOR:-/sys/class/dmi/id/sys_vendor}"
|
||||||
|
conf="${OMARCHY_BRCMFMAC_CONF:-/etc/modprobe.d/brcmfmac.conf}"
|
||||||
|
|
||||||
|
sys_vendor="$(cat "$dmi_vendor" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if ! lspci -nn | grep "106b:180[12]" >/dev/null &&
|
||||||
|
! { [[ $sys_vendor == Apple* ]] &&
|
||||||
|
lspci -nn | grep -E "14e4:(43ba|43bb|43bc|43a3|43dc|4464|4488|4425|4433)" >/dev/null; }; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# T2 installs already carry this from the installer, so the common case is a
|
||||||
|
# no-op for the first user and every user after them. Only an active options
|
||||||
|
# line counts: someone who commented theirs out still needs this.
|
||||||
|
if [[ -f $conf ]] &&
|
||||||
|
grep -Eq '^[[:space:]]*options[[:space:]]+brcmfmac[[:space:]].*feature_disable=0x82000' "$conf"; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo mkdir -p "$(dirname "$conf")"
|
||||||
|
|
||||||
|
# Append rather than overwrite, so anything else a user keeps here survives:
|
||||||
|
# modprobe reads every options line for a module, and nothing else sets
|
||||||
|
# feature_disable. The leading newline also covers a file that ends without one.
|
||||||
|
sudo tee -a "$conf" >/dev/null <<'EOF'
|
||||||
|
|
||||||
|
# Broadcom's firmware supplicant and authenticator fail the WPA four-way
|
||||||
|
# handshake on Apple hardware, which surfaces as a rejected password. Disable
|
||||||
|
# both so wpa_supplicant performs the handshake instead.
|
||||||
|
options brcmfmac feature_disable=0x82000
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# modprobe only reads this when the module loads. Reloading brcmfmac here would
|
||||||
|
# drop a Wi-Fi connection that works on the network the user is on right now,
|
||||||
|
# including the one carrying this update.
|
||||||
|
omarchy-state set reboot-required
|
||||||
Executable
+178
@@ -0,0 +1,178 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
|
leaf="$ROOT/install/hardware/apple/fix-brcmfmac-supplicant.sh"
|
||||||
|
fix_t2="$ROOT/install/hardware/apple/fix-t2.sh"
|
||||||
|
all="$ROOT/install/hardware/all.sh"
|
||||||
|
migration="$ROOT/migrations/1786391100.sh"
|
||||||
|
|
||||||
|
grep -q 'apple/fix-brcmfmac-supplicant.sh' "$all" ||
|
||||||
|
fail "the brcmfmac quirk runs during hardware setup"
|
||||||
|
|
||||||
|
# Two leaves writing one config means the later one silently wins, and which is
|
||||||
|
# later is a detail of all.sh nobody would think to check.
|
||||||
|
! grep -q 'brcmfmac' "$fix_t2" ||
|
||||||
|
fail "only one leaf owns /etc/modprobe.d/brcmfmac.conf"
|
||||||
|
pass "the brcmfmac quirk has a single owner and runs during setup"
|
||||||
|
|
||||||
|
test_tmp=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$test_tmp"' EXIT
|
||||||
|
|
||||||
|
stub_bin="$test_tmp/bin"
|
||||||
|
calls="$test_tmp/calls.log"
|
||||||
|
conf="$test_tmp/etc/modprobe.d/brcmfmac.conf"
|
||||||
|
mkdir -p "$stub_bin" "$test_tmp/dmi"
|
||||||
|
|
||||||
|
cat >"$stub_bin/lspci" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Chatty like real lspci: keep writing well past the pipe buffer after the
|
||||||
|
# match, so a grep -q consumer would kill this stub with SIGPIPE and pipefail
|
||||||
|
# would read that as "no such hardware" (#6608).
|
||||||
|
if (( ${T2_HARDWARE:-0} == 1 )); then
|
||||||
|
echo '01:00.0 Bridge [0680]: Apple Inc. T2 Security Chip [106b:1801]'
|
||||||
|
fi
|
||||||
|
if [[ -n ${WIFI_ID:-} ]]; then
|
||||||
|
echo "03:00.0 Network controller [0280]: Broadcom Inc. Wireless [14e4:$WIFI_ID]"
|
||||||
|
fi
|
||||||
|
for _ in {1..4096}; do
|
||||||
|
echo '02:00.0 Host bridge [0600]: Filler Device [ffff:0000]'
|
||||||
|
done
|
||||||
|
SH
|
||||||
|
|
||||||
|
cat >"$stub_bin/sudo" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
printf 'sudo' >>"$TEST_LOG"
|
||||||
|
printf '\t%s' "$@" >>"$TEST_LOG"
|
||||||
|
printf '\n' >>"$TEST_LOG"
|
||||||
|
"$@"
|
||||||
|
SH
|
||||||
|
|
||||||
|
# Stubbed rather than run: the real one would write the running user's state.
|
||||||
|
cat >"$stub_bin/omarchy-state" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
printf 'omarchy-state' >>"$TEST_LOG"
|
||||||
|
printf '\t%s' "$@" >>"$TEST_LOG"
|
||||||
|
printf '\n' >>"$TEST_LOG"
|
||||||
|
SH
|
||||||
|
|
||||||
|
chmod +x "$stub_bin"/*
|
||||||
|
|
||||||
|
# The leaf reads the vendor from an absolute path, so point it at a fixture by
|
||||||
|
# running with a fake root on PATH-independent state. pipefail is on, so a
|
||||||
|
# grep -q gate would go silent here the way #6608 did.
|
||||||
|
run_leaf() {
|
||||||
|
local vendor="$1" wifi_id="${2:-}" t2="${3:-0}"
|
||||||
|
rm -rf "$test_tmp/etc"
|
||||||
|
mkdir -p "$test_tmp/etc"
|
||||||
|
printf '%s' "$vendor" >"$test_tmp/dmi/sys_vendor"
|
||||||
|
|
||||||
|
# Redirect both absolute paths the leaf touches into the sandbox.
|
||||||
|
local script="$test_tmp/leaf.sh"
|
||||||
|
sed -e "s|/sys/class/dmi/id/sys_vendor|$test_tmp/dmi/sys_vendor|g" \
|
||||||
|
-e "s|/etc/modprobe.d|$test_tmp/etc/modprobe.d|g" \
|
||||||
|
"$leaf" >"$script"
|
||||||
|
|
||||||
|
WIFI_ID="$wifi_id" T2_HARDWARE="$t2" PATH="$stub_bin:$PATH" \
|
||||||
|
bash -eE -o pipefail -c 'source "$1"' bash "$script" </dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# The quirk shipped for T2 Macs first, and the move to its own leaf must not
|
||||||
|
# drop them.
|
||||||
|
run_leaf "Apple Inc." 4488 1 >/dev/null
|
||||||
|
grep -q 'feature_disable=0x82000' "$conf" 2>/dev/null ||
|
||||||
|
fail "a T2 Mac still gets the quirk" "$(ls -R "$test_tmp/etc" 2>&1)"
|
||||||
|
pass "a T2 Mac still gets the quirk"
|
||||||
|
|
||||||
|
# Every Broadcom part brcmfmac drives, on a Mac with no T2 to detect: BCM43602
|
||||||
|
# and its single-band variants, BCM4350, BCM4355, BCM4364, BCM4378, BCM4387.
|
||||||
|
for wifi_id in 43ba 43bb 43bc 43a3 43dc 4464 4425 4433; do
|
||||||
|
run_leaf "Apple Inc." "$wifi_id" 0 >/dev/null
|
||||||
|
[[ -f $conf ]] || fail "a Mac without a T2 gets the quirk" "14e4:$wifi_id"
|
||||||
|
done
|
||||||
|
pass "every brcmfmac part on a Mac without a T2 gets the quirk"
|
||||||
|
|
||||||
|
# Older Macs report the vendor differently.
|
||||||
|
run_leaf "Apple Computer, Inc." 43ba 0 >/dev/null
|
||||||
|
[[ -f $conf ]] || fail "the older Apple vendor string is recognized"
|
||||||
|
pass "the older Apple vendor string is recognized"
|
||||||
|
|
||||||
|
# BCM4360 Macs run the out-of-tree wl driver, which never reads this option, so
|
||||||
|
# writing it would only look like the machine had been dealt with.
|
||||||
|
run_leaf "Apple Inc." 43a0 0 >/dev/null
|
||||||
|
[[ ! -f $conf ]] || fail "a Mac whose Wi-Fi brcmfmac does not drive is left alone"
|
||||||
|
pass "a Mac whose Wi-Fi brcmfmac does not drive is left alone"
|
||||||
|
|
||||||
|
# Plenty of non-Apple hardware uses brcmfmac and does not share this bug.
|
||||||
|
run_leaf "LENOVO" 43ba 0 >/dev/null
|
||||||
|
[[ ! -f $conf ]] || fail "non-Apple hardware is left alone"
|
||||||
|
pass "non-Apple hardware is left alone"
|
||||||
|
|
||||||
|
run_leaf "Apple Inc." "" 0 >/dev/null
|
||||||
|
[[ ! -f $conf ]] || fail "a Mac with no wireless device is left alone"
|
||||||
|
pass "a Mac with no wireless device is left alone"
|
||||||
|
|
||||||
|
# Installs that predate the quirk never ran the leaf, so the migration has to
|
||||||
|
# reach them. It runs as the user under pipefail, the context #6608 was about.
|
||||||
|
run_migration() {
|
||||||
|
local vendor="$1" wifi_id="${2:-}" t2="${3:-0}"
|
||||||
|
printf '%s' "$vendor" >"$test_tmp/dmi/sys_vendor"
|
||||||
|
: >"$calls"
|
||||||
|
|
||||||
|
WIFI_ID="$wifi_id" T2_HARDWARE="$t2" PATH="$stub_bin:$PATH" TEST_LOG="$calls" \
|
||||||
|
OMARCHY_BRCMFMAC_DMI_VENDOR="$test_tmp/dmi/sys_vendor" \
|
||||||
|
OMARCHY_BRCMFMAC_CONF="$conf" \
|
||||||
|
bash -euo pipefail "$migration" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# A T2 install from before the quirk shipped has no config at all, so this is
|
||||||
|
# the case that proves the T2 gate itself still fires -- and it is the piped
|
||||||
|
# grep, run under pipefail, that #6608 was about.
|
||||||
|
rm -rf "$test_tmp/etc"
|
||||||
|
run_migration "Apple Inc." 4488 1
|
||||||
|
grep -q '^options brcmfmac feature_disable=0x82000$' "$conf" 2>/dev/null ||
|
||||||
|
fail "the migration fixes a T2 install that never got the quirk" "$(ls -R "$test_tmp/etc" 2>&1)"
|
||||||
|
# The option only reaches the driver when brcmfmac next loads.
|
||||||
|
grep -Fq $'omarchy-state\tset\treboot-required' "$calls" ||
|
||||||
|
fail "the migration asks for the reboot that applies it" "$(cat "$calls")"
|
||||||
|
pass "the migration fixes a T2 install that never got the quirk"
|
||||||
|
|
||||||
|
run_migration "Apple Inc." 4488 1
|
||||||
|
(( $(grep -c '^options brcmfmac feature_disable=0x82000$' "$conf") == 1 )) ||
|
||||||
|
fail "the migration is idempotent" "$(cat "$conf")"
|
||||||
|
[[ ! -s $calls ]] || fail "a repaired install is left untouched" "$(cat "$calls")"
|
||||||
|
pass "the migration is idempotent"
|
||||||
|
|
||||||
|
# The machine this was written for, with no T2 to fall back on.
|
||||||
|
rm -rf "$test_tmp/etc"
|
||||||
|
run_migration "Apple Inc." 43ba 0
|
||||||
|
grep -q '^options brcmfmac feature_disable=0x82000$' "$conf" 2>/dev/null ||
|
||||||
|
fail "the migration fixes an install on a Mac without a T2" "$(ls -R "$test_tmp/etc" 2>&1)"
|
||||||
|
pass "the migration fixes an install on a Mac without a T2"
|
||||||
|
|
||||||
|
# Written without a trailing newline, the way a hand-edited config often is.
|
||||||
|
printf '%s' 'options brcmfmac roamoff=1' >"$conf"
|
||||||
|
run_migration "Apple Inc." 43ba 0
|
||||||
|
grep -qx 'options brcmfmac roamoff=1' "$conf" ||
|
||||||
|
fail "the migration keeps other options in the config" "$(cat "$conf")"
|
||||||
|
grep -qx 'options brcmfmac feature_disable=0x82000' "$conf" ||
|
||||||
|
fail "the migration appends the quirk to an existing config" "$(cat "$conf")"
|
||||||
|
pass "the migration appends to a config that holds other options"
|
||||||
|
|
||||||
|
# Someone who commented their line out is still on the broken default.
|
||||||
|
printf '#options brcmfmac feature_disable=0x82000\n' >"$conf"
|
||||||
|
run_migration "Apple Inc." 43ba 0
|
||||||
|
grep -qx 'options brcmfmac feature_disable=0x82000' "$conf" ||
|
||||||
|
fail "a commented-out option does not count as applied" "$(cat "$conf")"
|
||||||
|
pass "a commented-out option does not count as applied"
|
||||||
|
|
||||||
|
rm -rf "$test_tmp/etc"
|
||||||
|
run_migration "Apple Inc." 43a0 0
|
||||||
|
[[ ! -e $conf ]] || fail "the migration skips a Mac brcmfmac does not drive" "$(cat "$conf")"
|
||||||
|
[[ ! -s $calls ]] || fail "the migration escalates nothing on unaffected Macs" "$(cat "$calls")"
|
||||||
|
pass "the migration skips hardware brcmfmac does not drive"
|
||||||
Reference in New Issue
Block a user