commit 751920c8fc230e8fe263ce546d10954298dbf401
parent 891db9edc593f5acd9f4a8de7c5018a1215eca02
Author: Daniel Barlow <dan@telent.net>
Date: Wed, 22 Feb 2023 18:41:41 +0000
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
Diffstat:
9 files changed, 388 insertions(+), 32 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -5,6 +5,7 @@ let
in rec {
imports = [
./modules/phram.nix
+ ./modules/wlan.nix
];
services.loopback =
let iface = interface { type = "loopback"; device = "lo";};