nixpkgs/nixos/modules/hardware/device-tree.nix
Richard Marko 6c9df40a4b 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 <georgerw@gmail.com>
Co-authored-by: Kai Wohlfahrt <kai.wohlfahrt@gmail.com>
2020-09-09 16:34:58 +02:00

206 lines
6.3 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.hardware.deviceTree;
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 {
default = pkgs.stdenv.hostPlatform.platform.kernelDTB or false;
type = types.bool;
description = ''
Build device tree files. These are used to describe the
non-discoverable hardware of a system.
'';
};
kernelPackage = mkOption {
default = config.boot.kernelPackages.kernel;
defaultText = "config.boot.kernelPackages.kernel";
example = literalExample "pkgs.linux_latest";
type = types.path;
description = ''
Kernel package containing the base device-tree (.dtb) to boot. Uses
device trees bundled with the Linux kernel by default.
'';
};
name = mkOption {
default = null;
example = "some-dtb.dtb";
type = types.nullOr types.str;
description = ''
The name of an explicit dtb to be loaded, relative to the dtb base.
Useful in extlinux scenarios if the bootloader doesn't pick the
right .dtb file from FDTDIR.
'';
};
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 ''
[
{ 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 = ''
List of overlays to apply to base device-tree (.dtb) files.
'';
};
package = mkOption {
default = null;
type = types.nullOr types.path;
internal = true;
description = ''
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 (filterDTBs dtbsWithSymbols) (withDTBOs cfg.overlays)
else (filterDTBs cfg.kernelPackage);
};
}