Skip to content

Commit

Permalink
nixos/device-tree: improve overlays support
Browse files Browse the repository at this point in the history
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 <georgerw@gmail.com>
Co-authored-by: Kai Wohlfahrt <kai.wohlfahrt@gmail.com>
  • Loading branch information
3 people committed Sep 9, 2020
1 parent 51428e8 commit 6c9df40
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 19 deletions.
11 changes: 11 additions & 0 deletions nixos/doc/manual/release-notes/rl-2009.xml
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,17 @@ CREATE ROLE postgres LOGIN SUPERUSER;
of the default <literal>out</literal> output anymore - if you relied on the
<literal>notmuch-emacs-mua</literal> binary or the emacs lisp files, access them via
the <literal>notmuch.emacs</literal> output.

Device tree overlay support was improved in
<link xlink:href="https://github.com/NixOS/nixpkgs/pull/79370">#79370</link>
and now uses <xref linkend="opt-hardware.deviceTree.kernelPackage"/>
instead of <option>hardware.deviceTree.base</option>.

<xref linkend="opt-hardware.deviceTree.overlays"/> configuration was
extended to support <literal>.dts</literal> files with symbols.

Device trees can now be filtered by setting
<xref linkend="opt-hardware.deviceTree.filter"/> option.
</para>
</listitem>
<listitem>
Expand Down
164 changes: 151 additions & 13 deletions nixos/modules/hardware/device-tree.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
'';
};
Expand All @@ -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.
'';
};

Expand All @@ -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);
};
}
27 changes: 21 additions & 6 deletions pkgs/os-specific/linux/device-tree/default.nix
Original file line number Diff line number Diff line change
@@ -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
'';
});
Expand Down

0 comments on commit 6c9df40

Please sign in to comment.