apple/t2: add Wi-Fi and Bluetooth firmware option

This commit is contained in:
mkorje
2025-03-17 12:40:06 +11:00
committed by mergify[bot]
parent e8c83f0759
commit 36d0027ef4
6 changed files with 354 additions and 0 deletions

View File

@@ -63,6 +63,19 @@ in
example = "latest"; example = "latest";
description = "The kernel release stream to use."; description = "The kernel release stream to use.";
}; };
firmware = {
enable = lib.mkEnableOption "automatic and declarative Wi-Fi and Bluetooth firmware configuration";
version = lib.mkOption {
type = types.enum [
"monterey"
"ventura"
"sonoma"
];
default = "sonoma";
example = "ventura";
description = "The macOS version to use.";
};
};
}; };
config = lib.mkMerge [ config = lib.mkMerge [
@@ -105,5 +118,11 @@ in
options apple-gmux force_igd=y options apple-gmux force_igd=y
''; '';
}) })
(lib.mkIf t2Cfg.firmware.enable {
# Configure Wi-Fi and Bluetooth firmware
hardware.firmware = [
(pkgs.callPackage ./pkgs/brcm-firmware { version = t2Cfg.firmware.version; })
];
})
]; ];
} }

View File

@@ -0,0 +1,89 @@
{
lib,
stdenvNoCC,
callPackage,
vmTools,
util-linux,
linux,
kmod,
version,
}:
let
get-firmware = callPackage ./get-firmware.nix { };
fetchmacos = callPackage ./fetchmacos.nix { };
# See https://github.com/kholia/OSX-KVM/blob/master/fetch-macOS-v2.py#L534-L546.
# Versions before macOS Monterey don't have Bluetooth firmware.
# Whereas macOS Sequoia doesn't have firmware for MacBook Air 2018 and 2019.
boards = {
monterey = {
boardId = "Mac-B809C3757DA9BB8D";
mlb = "00000000000000000";
osType = "latest";
hash = "sha256-My8FLnqHZn+THfGPIhTSApW/kIWM0ZZhjBxWujhhWPM=";
};
ventura = {
boardId = "Mac-4B682C642B45593E";
mlb = "00000000000000000";
osType = "latest";
hash = "sha256-Qy9Whu8pqHo+m6wHnCIqURAR53LYQKc0r87g9eHgnS4=";
};
sonoma = {
boardId = "Mac-827FAC58A8FDFA22";
mlb = "00000000000000000";
osType = "default";
hash = "sha256-phlpwNTYhugqX2KGljqxpbfGtCFDgggQPzB7U29XSmM=";
};
};
in
vmTools.runInLinuxVM (
stdenvNoCC.mkDerivation {
pname = "brcm-firmware";
inherit version;
src = fetchmacos {
name = version;
inherit (boards.${version})
boardId
mlb
osType
hash
;
};
dontUnpack = true;
nativeBuildInputs = [
util-linux
get-firmware
];
buildPhase = ''
ln -s ${linux}/lib /lib
${kmod}/bin/modprobe loop
${kmod}/bin/modprobe hfsplus
imgdir=$(mktemp -d)
loopdev=$(losetup -f | cut -d "/" -f 3)
losetup -P $loopdev $src
loopdev_partition=/dev/$(lsblk -o KNAME,TYPE,MOUNTPOINT -n | grep $loopdev | tail -1 | awk '{print $1}')
mount $loopdev_partition $imgdir
get-bluetooth $imgdir/usr/share/firmware/bluetooth bluetooth/
get-wifi $imgdir/usr/share/firmware/wifi wifi/
'';
installPhase = ''
mkdir -p $out/lib/firmware/brcm
cp bluetooth/brcm/* $out/lib/firmware/brcm/
cp wifi/brcm/* $out/lib/firmware/brcm/
'';
meta = with lib; {
description = "Wi-Fi and Bluetooth firmware for T2 Macs";
license = licenses.unfree;
maintainers = with maintainers; [ mkorje ];
platforms = platforms.linux;
};
}
)

View File

@@ -0,0 +1,42 @@
{
lib,
stdenvNoCC,
fetchFromGitHub,
callPackage,
dmg2img,
}:
let
macrecovery = callPackage ./macrecovery.nix { };
in
{
name,
boardId,
mlb,
osType,
hash,
}:
stdenvNoCC.mkDerivation {
name = name;
dontUnpack = true;
nativeBuildInputs = [
macrecovery
dmg2img
];
buildPhase = ''
macrecovery download -o . -b ${boardId} -m ${mlb} -os ${osType}
dmg2img -s BaseSystem.dmg fw.img
'';
installPhase = ''
cp fw.img $out
'';
outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = hash;
}

View File

@@ -0,0 +1,133 @@
diff --git a/asahi_firmware/bluetooth.py b/asahi_firmware/bluetooth.py
index 0934225..3eaa442 100644
--- a/asahi_firmware/bluetooth.py
+++ b/asahi_firmware/bluetooth.py
@@ -1,8 +1,25 @@
+#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import logging, os, os.path, re, sys
from collections import namedtuple, defaultdict
+from hashlib import sha256
-from .core import FWFile
+class FWFile(object):
+ def __init__(self, name, data):
+ self.name = name
+ self.data = data
+ self.sha = sha256(data).hexdigest()
+
+ def __repr__(self):
+ return f"FWFile({self.name!r}, <{self.sha[:16]}>)"
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+ return self.sha == other.sha
+
+ def __hash__(self):
+ return hash(self.sha)
log = logging.getLogger("asahi_firmware.bluetooth")
@@ -127,16 +144,16 @@ class BluetoothFWCollection(object):
if __name__ == "__main__":
col = BluetoothFWCollection(sys.argv[1])
-
- if len(sys.argv) > 2:
- from . import FWPackage
-
- pkg = FWPackage(sys.argv[2])
- pkg.add_files(sorted(col.files()))
- pkg.close()
-
- for i in pkg.manifest:
- print(i)
- else:
- for name, fwfile in col.files():
- print(name, f"{fwfile.name} ({len(fwfile.data)} bytes)")
+
+ dir = os.path.join(sys.argv[2], "brcm")
+ os.makedirs(dir)
+
+ hashes = {}
+ for name, data in sorted(col.files()):
+ path = os.path.join(sys.argv[2], name)
+ if data.sha in hashes:
+ os.link(hashes[data.sha], path)
+ else:
+ with open(path, "wb") as f:
+ f.write(data.data)
+ hashes[data.sha] = path
diff --git a/asahi_firmware/wifi.py b/asahi_firmware/wifi.py
index 346965c..261aa32 100644
--- a/asahi_firmware/wifi.py
+++ b/asahi_firmware/wifi.py
@@ -1,6 +1,24 @@
+#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import sys, os, os.path, pprint, statistics, logging
-from .core import FWFile
+from hashlib import sha256
+
+class FWFile(object):
+ def __init__(self, name, data):
+ self.name = name
+ self.data = data
+ self.sha = sha256(data).hexdigest()
+
+ def __repr__(self):
+ return f"FWFile({self.name!r}, <{self.sha[:16]}>)"
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+ return self.sha == other.sha
+
+ def __hash__(self):
+ return hash(self.sha)
log = logging.getLogger("asahi_firmware.wifi")
@@ -40,7 +58,9 @@ class WiFiFWCollection(object):
self.prune()
def load(self, source_path):
+ included_folders = ["C-4355__s-C1", "C-4364__s-B2", "C-4364__s-B3", "C-4377__s-B3"]
for dirpath, dirnames, filenames in os.walk(source_path):
+ dirnames[:] = [d for d in dirnames if d in included_folders]
if "perf" in dirnames:
dirnames.remove("perf")
if "assert" in dirnames:
@@ -141,18 +161,16 @@ class WiFiFWCollection(object):
if __name__ == "__main__":
col = WiFiFWCollection(sys.argv[1])
- if len(sys.argv) > 2:
- from .core import FWPackage
-
- pkg = FWPackage(sys.argv[2])
- pkg.add_files(sorted(col.files()))
- pkg.close()
-
- for i in pkg.manifest:
- print(i)
- else:
- for name, fwfile in col.files():
- if isinstance(fwfile, str):
- print(name, "->", fwfile)
- else:
- print(name, f"({len(fwfile.data)} bytes)")
+
+ dir = os.path.join(sys.argv[2], "brcm")
+ os.makedirs(dir)
+
+ hashes = {}
+ for name, data in sorted(col.files()):
+ path = os.path.join(sys.argv[2], name)
+ if data.sha in hashes:
+ os.link(hashes[data.sha], path)
+ else:
+ with open(path, "wb") as f:
+ f.write(data.data)
+ hashes[data.sha] = path

View File

@@ -0,0 +1,35 @@
{
lib,
stdenvNoCC,
fetchFromGitHub,
python3,
}:
stdenvNoCC.mkDerivation {
name = "get-firmware";
src = fetchFromGitHub {
owner = "AsahiLinux";
repo = "asahi-installer";
rev = "v0.7.9";
hash = "sha256-vbhepoZ52k5tW2Gd7tfQTZ5CLqzhV7dUcVh6+AYwECk=";
};
patches = [ ./get-firmware-standalone.patch ];
buildInputs = [ python3 ];
installPhase = ''
cd asahi_firmware
install -Dm755 bluetooth.py $out/bin/get-bluetooth
install -Dm755 wifi.py $out/bin/get-wifi
'';
meta = with lib; {
description = "Patched Asahi Linux Installer scripts to get brcm firmware";
homepage = "https://github.com/AsahiLinux/asahi-installer";
license = licenses.mit;
maintainers = with maintainers; [ mkorje ];
platforms = platforms.all;
};
}

View File

@@ -0,0 +1,36 @@
{
lib,
stdenvNoCC,
fetchFromGitHub,
python3,
}:
stdenvNoCC.mkDerivation {
name = "macrecovery";
src = fetchFromGitHub {
owner = "acidanthera";
repo = "OpenCorePkg";
rev = "1.0.4";
hash = "sha256-5Eypza9teSJSulHaK7Sxh562cTKedXKn3y+Z3+fC6sM=";
};
buildInputs = [ python3 ];
installPhase = ''
cd Utilities/macrecovery
install -Dm755 macrecovery.py $out/opt/macrecovery
cp boards.json $out/opt/boards.json
mkdir $out/bin
ln -s $out/opt/macrecovery $out/bin/macrecovery
'';
meta = with lib; {
description = "A tool that helps to automate recovery interaction";
homepage = "https://github.com/acidanthera/OpenCorePkg";
license = licenses.bsd3;
maintainers = with maintainers; [ mkorje ];
mainProgram = "macrecovery";
platforms = platforms.all;
};
}