From 751920c8fc230e8fe263ce546d10954298dbf401 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Wed, 22 Feb 2023 18:41:41 +0000 Subject: [PATCH] qemu: switch to building wireless as kernel modules This is a hefty change * add support for kernel backports project * build wireless stack/drivers as modules from a backported kernel * create a service to load/unload the modules --- devices/qemu/default.nix | 9 +- modules/base.nix | 5 + modules/wlan.nix | 51 +++--- overlay.nix | 4 + pkgs/kernel-backport/default.nix | 80 +++++++++ .../gentree-writable-outputs.patch | 97 ++++++++++ pkgs/mac80211/default.nix | 166 ++++++++++++++++++ tests/wlan/configuration.nix | 7 +- vanilla-configuration.nix | 1 + 9 files changed, 388 insertions(+), 32 deletions(-) create mode 100644 pkgs/kernel-backport/default.nix create mode 100644 pkgs/kernel-backport/gentree-writable-outputs.patch create mode 100644 pkgs/mac80211/default.nix diff --git a/devices/qemu/default.nix b/devices/qemu/default.nix index d6dd3c0..ab54e31 100644 --- a/devices/qemu/default.nix +++ b/devices/qemu/default.nix @@ -17,8 +17,8 @@ kernel = { src = pkgs.pkgsBuildBuild.fetchurl { name = "linux.tar.gz"; - url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.16.tar.gz"; - hash = "sha256-m4NeoEsCEK0HSIKTZ6zYTgk1fD3W0PSOMXN6fyHpkP8="; + url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz"; + hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8="; }; config = { MIPS_MALTA= "y"; @@ -41,6 +41,9 @@ SERIAL_8250_CONSOLE= "y"; }; }; - device.defaultOutput = "vmroot"; + device = { + defaultOutput = "vmroot"; + radios = ["mac80211_hwsim"]; + }; }; } diff --git a/modules/base.nix b/modules/base.nix index 06996f5..af1e233 100644 --- a/modules/base.nix +++ b/modules/base.nix @@ -60,6 +60,11 @@ in { }; loadAddress = mkOption { default = null; }; entryPoint = mkOption { }; + radios = mkOption { + type = types.listOf types.str; + default = []; + example = ["ath9k" "ath10k"]; + }; }; }; config = { diff --git a/modules/wlan.nix b/modules/wlan.nix index bfb4569..8fcd3a8 100644 --- a/modules/wlan.nix +++ b/modules/wlan.nix @@ -3,35 +3,38 @@ let inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ; inherit (pkgs.pseudofile) dir symlink; inherit (pkgs) busybox; + mac80211 = pkgs.mac80211.override { + drivers = config.device.radios; + klibBuild = config.outputs.kernel.modulesupport; + }; in { config = { + services.wlan_module = mac80211; + kernel = rec { config = { - CFG80211= "y"; - MAC80211= "y"; - MAC80211_MESH= "y"; - RFKILL= "y"; - WLAN = "y"; - # if/when we switch to using backported mac80211 drivers built - # as modules, based on nixwrt code we expect we will need this config - # to enable them - # "ASN1" = "y"; - # "ASYMMETRIC_KEY_TYPE" = "y"; - # "ASYMMETRIC_PUBLIC_KEY_SUBTYPE" = "y"; - # "CRC_CCITT" = "y"; - # "CRYPTO" = "y"; - # "CRYPTO_ARC4" = "y"; - # "CRYPTO_CBC" = "y"; - # "CRYPTO_CCM" = "y"; - # "CRYPTO_CMAC" = "y"; - # "CRYPTO_GCM" = "y"; - # "CRYPTO_HASH_INFO" = "y"; - # "CRYPTO_LIB_ARC4" = "y"; - # "CRYPTO_RSA" = "y"; - # "CRYPTO_SHA1" = "y"; - # "ENCRYPTED_KEYS" = "y"; - # "KEYS" = "y"; + # Most of this is necessary infra to allow wireless stack/ + # drivers to be built as module + ASN1 = "y"; + ASYMMETRIC_KEY_TYPE = "y"; + ASYMMETRIC_PUBLIC_KEY_SUBTYPE = "y"; + CRC_CCITT = "y"; + CRYPTO = "y"; + CRYPTO_ARC4 = "y"; + CRYPTO_CBC = "y"; + CRYPTO_CCM = "y"; + CRYPTO_CMAC = "y"; + CRYPTO_GCM = "y"; + CRYPTO_HASH_INFO = "y"; + CRYPTO_USER_API = "y"; # ARC4 needs this + CRYPTO_USER_API_HASH = "y"; + CRYPTO_USER_API_ENABLE_OBSOLETE = "y"; # ARC4 needs this + CRYPTO_LIB_ARC4 = "y"; # for WEP + CRYPTO_RSA = "y"; + CRYPTO_SHA1 = "y"; + ENCRYPTED_KEYS = "y"; + KEYS = "y"; }; }; }; diff --git a/overlay.nix b/overlay.nix index 0c98d30..1c05260 100644 --- a/overlay.nix +++ b/overlay.nix @@ -26,6 +26,10 @@ final: prev: { mips-vm = final.callPackage ./pkgs/mips-vm {}; pppoe = final.callPackage ./pkgs/pppoe {}; + + kernel-backport = final.callPackage ./pkgs/kernel-backport {}; + mac80211 = final.callPackage ./pkgs/mac80211 {}; + pppBuild = prev.ppp; ppp = (prev.ppp.override { diff --git a/pkgs/kernel-backport/default.nix b/pkgs/kernel-backport/default.nix new file mode 100644 index 0000000..19b1bd4 --- /dev/null +++ b/pkgs/kernel-backport/default.nix @@ -0,0 +1,80 @@ +{ stdenv +, git +, python2 +, which +, fetchgit +, fetchpatch +, fetchFromGitHub +, autoreconfHook +, coccinelle +}: +let + donorTree = fetchFromGitHub { + owner = "torvalds"; + repo = "linux"; + rev = "e2c1a934fd8e4288e7a32f4088ceaccf469eb74c"; # 5.15.94 + hash= "sha256-Jg3EgL86CseuzYMAlUG3CDWPCo8glMSIZs10l7EuhWI="; + }; + backports = stdenv.mkDerivation { + name = "linux-backports"; + version = "dfe0f60ca8a"; + nativeBuildInputs = [ python2 ]; + + src = fetchgit { + url = "https://git.kernel.org/pub/scm/linux/kernel/git/backports/backports.git"; + name = "backports"; + rev = "dfe0f60ca8a1065e63b4db703b3bd2708ee23a0e"; + hash = "sha256-V+unO0rCom+TZS7WuaXFrb3C1EBImmflCPuOoP+LvBY="; + }; + buildPhase = '' + patchShebangs . + ''; + installPhase = '' + mkdir -p $out + cp -a . $out + rm $out/patches/0073-netdevice-mtu-range.cocci + # fq.patch is obsoleted by kernel commit 48a54f6bc45 and no longer + # applies + # rm $out/patches/0091-fq-no-siphash_key_t/fq.patch + # don't know why this doesn't apply but it's only important for + # compiling against linux < 4.1 + # rm $out/patches/0058-ptp_getsettime64/ptp_getsettime64.cocci + ''; + patches = [ + # (fetchpatch { + # url = "https://github.com/telent/nixwrt/blob/28ff2559e811c740b0a2922f52291b335804857b/nixwrt/kernel/gentree-writable-outputs.patch"; + # hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + # })# + ./gentree-writable-outputs.patch +# ./update-usb-sg-backport-patch.patch +# ./backport_kfree_sensitive.patch + ]; + }; +in stdenv.mkDerivation rec { + inherit donorTree; + KERNEL_VERSION = builtins.substring 0 11 donorTree.rev; + BACKPORTS_VERSION = backports.version; + name = "backported-kernel-${KERNEL_VERSION}-${BACKPORTS_VERSION}"; + + # gentree uses "which" at runtime to test for the presence of git, + # and I don't have the patience to patch it out. There is no other + # reason we need either of them as build inputs. + depsBuildBuild = [ coccinelle ]; + nativeBuildInputs = [ which git python2 ]; + + phases = [ + "backportFromFuture" "installPhase" + ]; + + backportFromFuture = '' + echo $KERNEL_VERSION $BACKPORTS_VERSION + WORK=`pwd`/build + mkdir -p $WORK + cat ${backports}/copy-list > copy-list + echo 'include/linux/key.h' >> copy-list + python ${backports}/gentree.py --verbose --clean --copy-list copy-list ${donorTree} $WORK + ''; + installPhase = '' + cp -a ./build/ $out + ''; +} diff --git a/pkgs/kernel-backport/gentree-writable-outputs.patch b/pkgs/kernel-backport/gentree-writable-outputs.patch new file mode 100644 index 0000000..b96a992 --- /dev/null +++ b/pkgs/kernel-backport/gentree-writable-outputs.patch @@ -0,0 +1,97 @@ +--- /nix/store/swnksqfa53bv4c5n376zpw8zmzs47f4b-backports3/gentree.py 1970-01-01 01:00:01.000000000 +0100 ++++ ./gentree.py 2020-09-18 21:55:30.100918501 +0100 +@@ -2,6 +2,6 @@ + # + # Generate the output tree into a specified directory. + # +- + import argparse, sys, os, errno, shutil, re, subprocess ++import stat + import tarfile, gzip, time +@@ -127,6 +127,10 @@ + if e.errno != errno.ENOENT: + raise + ++def makeWritable(filename): ++ os.chmod(filename, ++ os.stat(filename).st_mode | 0700) ++ + + def copytree(src, dst, symlinks=False, ignore=None): + """ +@@ -141,6 +145,7 @@ + + if not os.path.isdir(dst): + os.makedirs(dst) ++ makeWritable(dst) + errors = [] + for name in names: + if name in ignored_names: +@@ -154,7 +161,8 @@ + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore) + else: +- shutil.copy2(srcname, dstname) ++ shutil.copy(srcname, dstname) ++ makeWritable(dstname) + except (IOError, os.error) as why: + errors.append((srcname, dstname, str(why))) + # catch the Error from the recursive copytree so that we can +@@ -163,6 +172,7 @@ + errors.extend(err.args[0]) + try: + shutil.copystat(src, dst) ++ makeWritable(dst) + except WindowsError: + # can't copy file access times on Windows + pass +@@ -187,6 +197,7 @@ + for srcitem, tgtitem in copy_list: + if tgtitem == '': + copytree(srcpath, outdir, ignore=shutil.ignore_patterns('*~')) ++ makeWritable(outdir) + elif tgtitem[-1] == '/': + def copy_ignore(dir, entries): + r = [] +@@ -199,14 +210,17 @@ + ignore=copy_ignore) + else: + try: +- os.makedirs(os.path.join(outdir, os.path.dirname(tgtitem))) ++ n = os.path.join(outdir, os.path.dirname(tgtitem)) ++ os.makedirs(n, 0755) ++ makeWritable(n) + except OSError as e: + # ignore dirs we might have created just now + if e.errno != errno.EEXIST: + raise +- shutil.copy(os.path.join(srcpath, srcitem), +- os.path.join(outdir, tgtitem)) +- ++ outpath = os.path.join(outdir, tgtitem) ++ if os.path.exists(outpath): os.remove(outpath) ++ shutil.copy(os.path.join(srcpath, srcitem), outpath) ++ makeWritable(outpath) + + def copy_git_files(srcpath, copy_list, rev, outdir): + """ +@@ -886,7 +904,10 @@ + git_debug_snapshot(args, 'Add driver sources') + + disable_list = add_automatic_backports(args) +- if git_tracked_version: ++ if os.environ['BACKPORTS_VERSION']: ++ backports_version = os.environ['BACKPORTS_VERSION'] ++ kernel_version = os.environ['KERNEL_VERSION'] ++ elif git_tracked_version: + backports_version = "(see git)" + kernel_version = "(see git)" + else: +@@ -1037,6 +1030,7 @@ def process(kerneldir, copy_list_file, git_revision=None, + break + if copy_defconfig: + shutil.copy(dfsrc, os.path.join(bpid.target_dir, 'defconfigs', dfbase)) ++ makeWritable(os.path.join(bpid.target_dir, 'defconfigs', dfbase)) + + git_debug_snapshot(args, "add (useful) defconfig files") + diff --git a/pkgs/mac80211/default.nix b/pkgs/mac80211/default.nix new file mode 100644 index 0000000..3f6a846 --- /dev/null +++ b/pkgs/mac80211/default.nix @@ -0,0 +1,166 @@ +# make out-of-tree modules given a backported kernel source tree + +{ + extraConfig ? {} +, drivers ? [ ] +, kernel-backport +, stdenv +, writeText +, klibBuild ? null +, buildPackages +, fetchFromGitHub + +, liminix +, lib +}: +let + openwrtSrc = fetchFromGitHub { + name = "openwrt-source"; + repo = "openwrt"; + owner = "openwrt"; + rev = "a5265497a4f6da158e95d6a450cb2cb6dc085cab"; + hash = "sha256-YYi4gkpLjbOK7bM2MGQjAyEBuXJ9JNXoz/JEmYf8xE8="; + }; + inherit (liminix.services) oneshot longrun; + inherit (lib.lists) foldl; + configs = { + ath9k = { + WLAN_VENDOR_ATH = "y"; + ATH_COMMON = "m"; + ATH9K = "m"; + ATH9K_AHB = "y"; + # ATH9K_DEBUGFS = "y"; + ATH_DEBUG = "y"; + BACKPORTED_ATH9K_AHB = "y"; + }; + ath10k_pci = { + WLAN_VENDOR_ATH = "y"; + ATH_COMMON = "m"; + ATH10K = "m"; + # BACKPORTED_ATH10K_AHB = "y"; + # ATH10K_AHB = "y"; + ATH10K_PCI = "y"; + ATH10K_DEBUG = "y"; + }; + rt2800soc = { + WLAN_VENDOR_RALINK = "y"; + RT2800SOC = "m"; + RT2X00 = "m"; + }; + mt7603e = { # XXX find a better name for this + WLAN_VENDOR_RALINK = "y"; + WLAN_VENDOR_MEDIATEK = "y"; + MT7603E = "y"; + }; + mac80211_hwsim = { + MAC80211_HWSIM = "y"; + }; + }; + kconfig = (foldl (config: d: (config // configs.${d})) { + WLAN = "y"; + CFG80211 = "m"; + MAC80211 = "m"; + + # (nixwrt comment) I am reluctant to have to enable this but + # can't transmit on 5GHz bands without it (they are all marked + # NO-IR) + CFG80211_CERTIFICATION_ONUS = "y"; + # (nixwrt comment) can't get signed regdb to work rn, it just + # gives me "loaded regulatory.db is malformed or signature is + # missing/invalid" + CFG80211_REQUIRE_SIGNED_REGDB = "n"; # depends on ONUS + + CFG80211_CRDA_SUPPORT = "n"; + + MAC80211_MESH = "y"; + + } drivers) // extraConfig; + + writeConfig = name : config: writeText name + (builtins.concatStringsSep + "\n" + (lib.mapAttrsToList + (name: value: (if value == "n" then "# CPTCFG_${name} is not set" else "CPTCFG_${name}=${value}")) + config + )); + kconfigFile = writeConfig "backports_kconfig" kconfig; + src = kernel-backport; + CROSS_COMPILE = stdenv.cc.bintools.targetPrefix; + CC = "${buildPackages.stdenv.cc}/bin/gcc"; + module = stdenv.mkDerivation { + name = "mac80211"; + inherit src; + + hardeningDisable = ["all"]; + nativeBuildInputs = [buildPackages.stdenv.cc] ++ + (with buildPackages.pkgs; + [bc bison flex pkgconfig openssl + which kmod cpio + ]); + inherit CC CROSS_COMPILE; + ARCH = "mips"; # kernel uses "mips" here for both mips and mipsel + dontStrip = true; + dontPatchELF = true; + phases = [ + "unpackPhase" + "patchFromOpenwrt" + "configurePhase" + "checkConfigurationPhase" + "buildPhase" + "installPhase" + ]; + + patchFromOpenwrt = '' + mac80211=${openwrtSrc}/package/kernel/mac80211 + echo $mac80211 + for i in $(ls $mac80211/patches/build/ | grep -v 015-ipw200); do + echo $i; (patch -p1 -N <$mac80211/patches/build/$i) + done + for i in $(ls $mac80211/patches/rt2x00/ | grep -v 009-rt2x00-don-t | grep -v 013-rt2x00-set-correct | grep -v 014 | grep -v 015 | grep -v 016); do + echo $i; (patch -p1 -N <$mac80211/patches/rt2x00/$i) + done + for i in $mac80211/patches/ath/*; do echo $i; (patch -p1 -N <$i) ;done + for i in $mac80211/patches/ath9k/*; do echo $i; (patch -p1 -N <$i) ;done + ''; + + configurePhase = '' + cp ${kconfigFile} .config + cp ${kconfigFile} .config.orig + chmod +w .config .config.orig + make V=1 CC=${CC} SHELL=`type -p bash` LEX=flex KLIB_BUILD=${klibBuild} olddefconfig + ''; + + checkConfigurationPhase = '' + echo Checking required config items: + if comm -2 -3 <(awk -F= '/CPTCFG/ {print $1}' ${kconfigFile} |sort) <(awk -F= '/CPTCFG/ {print $1}' .config|sort) |grep '.' ; then + echo -e "^^^ Some configuration lost :-(\nPerhaps you have mutually incompatible settings, or have disabled options on which these depend.\n" + exit 0 + fi + echo "OK" + ''; + + KBUILD_BUILD_HOST = "liminix.builder"; + + buildPhase = '' + patchShebangs scripts/ + echo ${klibBuild} + make V=1 SHELL=`type -p bash` KLIB_BUILD=${klibBuild} modules + find . -name \*.ko | xargs ${CROSS_COMPILE}strip --strip-debug + ''; + + installPhase = '' + mkdir -p $out/lib/modules/0.0 + find . -name \*.ko | cpio --make-directories -p $out/lib/modules/0.0 + depmod -b $out -v 0.0 + touch $out/load.sh + for i in ${lib.concatStringsSep " " drivers}; do + modprobe -S 0.0 -d $out --show-depends $i >> $out/load.sh + done + tac < $out/load.sh | sed 's/^insmod/rmmod/g' > $out/unload.sh + ''; + }; +in oneshot { + name = "wlan.module"; + up = "sh ${module}/load.sh"; + down = "sh {module}/unload.sh"; + } diff --git a/tests/wlan/configuration.nix b/tests/wlan/configuration.nix index 66a6ba5..2a49785 100644 --- a/tests/wlan/configuration.nix +++ b/tests/wlan/configuration.nix @@ -15,10 +15,6 @@ in rec { imports = [ ../../modules/wlan.nix ]; - kernel.config = { - MAC80211_HWSIM = "y"; - }; - services.wlan = interface { type = "hardware"; device = "wlan0"; }; @@ -41,9 +37,10 @@ in rec { services.default = target { name = "default"; - contents = with services; [ + contents = with config.services; [ loopback hostap + wlan_module ]; }; defaultProfile.packages = with pkgs; [ tcpdump ] ; diff --git a/vanilla-configuration.nix b/vanilla-configuration.nix index 4f0b3cc..5c9efd2 100644 --- a/vanilla-configuration.nix +++ b/vanilla-configuration.nix @@ -5,6 +5,7 @@ let in rec { imports = [ ./modules/phram.nix + ./modules/wlan.nix ]; services.loopback = let iface = interface { type = "loopback"; device = "lo";};