From 6c9df40a4bc819fcab0836ad28ee944c8ce66db0 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Wed, 5 Feb 2020 15:35:34 +0100 Subject: [PATCH] nixos/device-tree: improve overlays support Now allows applying external overlays either in form of .dts file, literal dts context added to store or precompiled .dtbo. If overlays are defined, kernel device-trees are compiled with '-@' so the .dtb files contain symbols which we can reference in our overlays. Since `fdtoverlay` doesn't respect `/ compatible` by itself we query compatible strings of both `dtb` and `dtbo(verlay)` and apply only if latter is substring of the former. Also adds support for filtering .dtb files (as there are now nearly 1k dtbs). Co-authored-by: georgewhewell Co-authored-by: Kai Wohlfahrt --- nixos/doc/manual/release-notes/rl-2009.xml | 11 ++ nixos/modules/hardware/device-tree.nix | 164 ++++++++++++++++-- .../os-specific/linux/device-tree/default.nix | 27 ++- 3 files changed, 183 insertions(+), 19 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml index c5863f3219ea..886cc403fe17 100644 --- a/nixos/doc/manual/release-notes/rl-2009.xml +++ b/nixos/doc/manual/release-notes/rl-2009.xml @@ -833,6 +833,17 @@ CREATE ROLE postgres LOGIN SUPERUSER; of the default out output anymore - if you relied on the notmuch-emacs-mua binary or the emacs lisp files, access them via the notmuch.emacs output. + + Device tree overlay support was improved in + #79370 + and now uses + instead of . + + configuration was + extended to support .dts files with symbols. + + Device trees can now be filtered by setting + option. diff --git a/nixos/modules/hardware/device-tree.nix b/nixos/modules/hardware/device-tree.nix index b3f1dda98c89..e0ab37bca63a 100644 --- a/nixos/modules/hardware/device-tree.nix +++ b/nixos/modules/hardware/device-tree.nix @@ -4,7 +4,114 @@ with lib; let cfg = config.hardware.deviceTree; -in { + + overlayType = types.submodule { + options = { + name = mkOption { + type = types.str; + description = '' + Name of this overlay + ''; + }; + + dtsFile = mkOption { + type = types.nullOr types.path; + description = '' + Path to .dts overlay file, overlay is applied to + each .dtb file matching "compatible" of the overlay. + ''; + default = null; + example = literalExample "./dts/overlays.dts"; + }; + + dtsText = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Literal DTS contents, overlay is applied to + each .dtb file matching "compatible" of the overlay. + ''; + example = literalExample '' + /dts-v1/; + /plugin/; + / { + compatible = "raspberrypi"; + fragment@0 { + target-path = "/soc"; + __overlay__ { + pps { + compatible = "pps-gpio"; + status = "okay"; + }; + }; + }; + }; + ''; + }; + + dtboFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to .dtbo compiled overlay file. + ''; + }; + }; + }; + + # this requires kernel package + dtbsWithSymbols = pkgs.stdenv.mkDerivation { + name = "dtbs-with-symbols"; + inherit (cfg.kernelPackage) src nativeBuildInputs depsBuildBuild; + patches = map (patch: patch.patch) cfg.kernelPackage.kernelPatches; + buildPhase = '' + patchShebangs scripts/* + substituteInPlace scripts/Makefile.lib \ + --replace 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget))' 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget)) -@' + make ${pkgs.stdenv.hostPlatform.platform.kernelBaseConfig} ARCH="${pkgs.stdenv.hostPlatform.platform.kernelArch}" + make dtbs ARCH="${pkgs.stdenv.hostPlatform.platform.kernelArch}" + ''; + installPhase = '' + make dtbs_install INSTALL_DTBS_PATH=$out/dtbs ARCH="${pkgs.stdenv.hostPlatform.platform.kernelArch}" + ''; + }; + + filterDTBs = src: if isNull cfg.filter + then "${src}/dtbs" + else + pkgs.runCommand "dtbs-filtered" {} '' + mkdir -p $out + cd ${src}/dtbs + find . -type f -name '${cfg.filter}' -print0 \ + | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents + ''; + + # Compile single Device Tree overlay source + # file (.dts) into its compiled variant (.dtbo) + compileDTS = name: f: pkgs.callPackage({ dtc }: pkgs.stdenv.mkDerivation { + name = "${name}-dtbo"; + + nativeBuildInputs = [ dtc ]; + + buildCommand = '' + dtc -I dts ${f} -O dtb -@ -o $out + ''; + }) {}; + + # Fill in `dtboFile` for each overlay if not set already. + # Existence of one of these is guarded by assertion below + withDTBOs = xs: flip map xs (o: o // { dtboFile = + if isNull o.dtboFile then + if !isNull o.dtsFile then compileDTS o.name o.dtsFile + else compileDTS o.name (pkgs.writeText "dts" o.dtsText) + else o.dtboFile; } ); + +in +{ + imports = [ + (mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead") + ]; + options = { hardware.deviceTree = { enable = mkOption { @@ -16,13 +123,13 @@ in { ''; }; - base = mkOption { - default = "${config.boot.kernelPackages.kernel}/dtbs"; - defaultText = "\${config.boot.kernelPackages.kernel}/dtbs"; - example = literalExample "pkgs.device-tree_rpi"; + kernelPackage = mkOption { + default = config.boot.kernelPackages.kernel; + defaultText = "config.boot.kernelPackages.kernel"; + example = literalExample "pkgs.linux_latest"; type = types.path; description = '' - The path containing the base device-tree (.dtb) to boot. Contains + Kernel package containing the base device-tree (.dtb) to boot. Uses device trees bundled with the Linux kernel by default. ''; }; @@ -38,14 +145,32 @@ in { ''; }; + filter = mkOption { + type = types.nullOr types.str; + default = null; + example = "*rpi*.dtb"; + description = '' + Only include .dtb files matching glob expression. + ''; + }; + overlays = mkOption { default = []; - example = literalExample - "[\"\${pkgs.device-tree_rpi.overlays}/w1-gpio.dtbo\"]"; - type = types.listOf types.path; + example = literalExample '' + [ + { name = "pps"; dtsFile = ./dts/pps.dts; } + { name = "spi"; + dtsText = "..."; + } + { name = "precompiled"; dtboFile = ./dtbos/example.dtbo; } + ] + ''; + type = types.listOf (types.coercedTo types.path (path: { + name = baseNameOf path; + dtboFile = path; + }) overlayType); description = '' - A path containing device tree overlays (.dtbo) to be applied to all - base device-trees. + List of overlays to apply to base device-tree (.dtb) files. ''; }; @@ -54,14 +179,27 @@ in { type = types.nullOr types.path; internal = true; description = '' - A path containing the result of applying `overlays` to `base`. + A path containing the result of applying `overlays` to `kernelPackage`. ''; }; }; }; config = mkIf (cfg.enable) { + + assertions = let + invalidOverlay = o: isNull o.dtsFile && isNull o.dtsText && isNull o.dtboFile; + in lib.singleton { + assertion = lib.all (o: !invalidOverlay o) cfg.overlays; + message = '' + deviceTree overlay needs one of dtsFile, dtsText or dtboFile set. + Offending overlay(s): + ${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))} + ''; + }; + hardware.deviceTree.package = if (cfg.overlays != []) - then pkgs.deviceTree.applyOverlays cfg.base cfg.overlays else cfg.base; + then pkgs.deviceTree.applyOverlays (filterDTBs dtbsWithSymbols) (withDTBOs cfg.overlays) + else (filterDTBs cfg.kernelPackage); }; } diff --git a/pkgs/os-specific/linux/device-tree/default.nix b/pkgs/os-specific/linux/device-tree/default.nix index 13d819a08a5b..0599289ab726 100644 --- a/pkgs/os-specific/linux/device-tree/default.nix +++ b/pkgs/os-specific/linux/device-tree/default.nix @@ -1,16 +1,31 @@ { stdenvNoCC, dtc, findutils }: with stdenvNoCC.lib; { - applyOverlays = (base: overlays: stdenvNoCC.mkDerivation { + applyOverlays = (base: overlays': stdenvNoCC.mkDerivation { name = "device-tree-overlays"; nativeBuildInputs = [ dtc findutils ]; buildCommand = let - quotedDtbos = concatMapStringsSep " " (o: "\"${toString o}\"") (toList overlays); + overlays = toList overlays'; in '' - for dtb in $(find ${base} -name "*.dtb" ); do - outDtb=$out/$(realpath --relative-to "${base}" "$dtb") - mkdir -p "$(dirname "$outDtb")" - fdtoverlay -o "$outDtb" -i "$dtb" ${quotedDtbos}; + mkdir -p $out + cd ${base} + find . -type f -name '*.dtb' -print0 \ + | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents + + for dtb in $(find $out -type f -name '*.dtb'); do + dtbCompat="$( fdtget -t s $dtb / compatible )" + + ${flip (concatMapStringsSep "\n") overlays (o: '' + overlayCompat="$( fdtget -t s ${o.dtboFile} / compatible )" + # overlayCompat in dtbCompat + if [[ "$dtbCompat" =~ "$overlayCompat" ]]; then + echo "Applying overlay ${o.name} to $( basename $dtb )" + mv $dtb{,.in} + fdtoverlay -o "$dtb" -i "$dtb.in" ${o.dtboFile}; + rm $dtb.in + fi + '')} + done ''; });