uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead

https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948
this can do it nicely.

Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
Anton Arapov 2021-04-03 12:58:10 +02:00 committed by Alan Daniels
commit 56de2bcd43
30691 changed files with 3076956 additions and 0 deletions

View file

@ -0,0 +1,16 @@
{ runCommand }:
firmware:
runCommand "${firmware.name}-xz" {} ''
mkdir -p $out/lib
(cd ${firmware} && find lib/firmware -type d -print0) |
(cd $out && xargs -0 mkdir -v --)
(cd ${firmware} && find lib/firmware -type f -print0) |
(cd $out && xargs -0rtP "$NIX_BUILD_CORES" -n1 \
sh -c 'xz -9c -T1 -C crc32 --lzma2=dict=2MiB "${firmware}/$1" > "$1.xz"' --)
(cd ${firmware} && find lib/firmware -type l) | while read link; do
target="$(readlink "${firmware}/$link")"
ln -vs -- "''${target/^${firmware}/$out}.xz" "$out/$link.xz"
done
''

View file

@ -0,0 +1,53 @@
rec {
cat = {
executable = pkgs: "cat";
ubootName = "none";
extension = ".cpio";
};
gzip = {
executable = pkgs: "${pkgs.gzip}/bin/gzip";
defaultArgs = ["-9n"];
ubootName = "gzip";
extension = ".gz";
};
bzip2 = {
executable = pkgs: "${pkgs.bzip2}/bin/bzip2";
ubootName = "bzip2";
extension = ".bz2";
};
xz = {
executable = pkgs: "${pkgs.xz}/bin/xz";
defaultArgs = ["--check=crc32" "--lzma2=dict=512KiB"];
extension = ".xz";
};
lzma = {
executable = pkgs: "${pkgs.xz}/bin/lzma";
defaultArgs = ["--check=crc32" "--lzma1=dict=512KiB"];
ubootName = "lzma";
extension = ".lzma";
};
lz4 = {
executable = pkgs: "${pkgs.lz4}/bin/lz4";
defaultArgs = ["-l"];
ubootName = "lz4";
extension = ".lz4";
};
lzop = {
executable = pkgs: "${pkgs.lzop}/bin/lzop";
ubootName = "lzo";
extension = ".lzo";
};
zstd = {
executable = pkgs: "${pkgs.zstd}/bin/zstd";
defaultArgs = ["-10"];
ubootName = "zstd";
extension = ".zst";
};
pigz = gzip // {
executable = pkgs: "${pkgs.pigz}/bin/pigz";
};
pixz = xz // {
executable = pkgs: "${pkgs.pixz}/bin/pixz";
defaultArgs = [];
};
}

View file

@ -0,0 +1,16 @@
{ rustPlatform, lib, makeWrapper, patchelf, glibc, binutils }:
rustPlatform.buildRustPackage {
pname = "make-initrd-ng";
version = "0.1.0";
src = ./make-initrd-ng;
cargoLock.lockFile = ./make-initrd-ng/Cargo.lock;
nativeBuildInputs = [ makeWrapper ];
postInstall = ''
wrapProgram $out/bin/make-initrd-ng \
--prefix PATH : ${lib.makeBinPath [ patchelf glibc binutils ]}
'';
}

View file

@ -0,0 +1,93 @@
let
# Some metadata on various compression programs, relevant to naming
# the initramfs file and, if applicable, generating a u-boot image
# from it.
compressors = import ./initrd-compressor-meta.nix;
# Get the basename of the actual compression program from the whole
# compression command, for the purpose of guessing the u-boot
# compression type and filename extension.
compressorName = fullCommand: builtins.elemAt (builtins.match "([^ ]*/)?([^ ]+).*" fullCommand) 1;
in
{ stdenvNoCC, perl, cpio, ubootTools, lib, pkgsBuildHost, makeInitrdNGTool, patchelf, runCommand
# Name of the derivation (not of the resulting file!)
, name ? "initrd"
# Program used to compress the cpio archive; use "cat" for no compression.
# This can also be a function which takes a package set and returns the path to the compressor,
# such as `pkgs: "${pkgs.lzop}/bin/lzop"`.
, compressor ? "gzip"
, _compressorFunction ?
if lib.isFunction compressor then compressor
else if ! builtins.hasContext compressor && builtins.hasAttr compressor compressors then compressors.${compressor}.executable
else _: compressor
, _compressorExecutable ? _compressorFunction pkgsBuildHost
, _compressorName ? compressorName _compressorExecutable
, _compressorMeta ? compressors.${_compressorName} or {}
# List of arguments to pass to the compressor program, or null to use its defaults
, compressorArgs ? null
, _compressorArgsReal ? if compressorArgs == null then _compressorMeta.defaultArgs or [] else compressorArgs
# Filename extension to use for the compressed initramfs. This is
# included for clarity, but $out/initrd will always be a symlink to
# the final image.
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
, extension ? _compressorMeta.extension or
(throw "Unrecognised compressor ${_compressorName}, please specify filename extension")
# List of { object = path_or_derivation; symlink = "/path"; }
# The paths are copied into the initramfs in their nix store path
# form, then linked at the root according to `symlink`.
, contents
# List of uncompressed cpio files to prepend to the initramfs. This
# can be used to add files in specified paths without them becoming
# symlinks to store paths.
, prepend ? []
# Whether to wrap the initramfs in a u-boot image.
, makeUInitrd ? stdenvNoCC.hostPlatform.linux-kernel.target == "uImage"
# If generating a u-boot image, the architecture to use. The default
# guess may not align with u-boot's nomenclature correctly, so it can
# be overridden.
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 for a list.
, uInitrdArch ? stdenvNoCC.hostPlatform.linuxArch
# The name of the compression, as recognised by u-boot.
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L195-204 for a list.
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
, uInitrdCompression ? _compressorMeta.ubootName or
(throw "Unrecognised compressor ${_compressorName}, please specify uInitrdCompression")
}: runCommand name {
compress = "${_compressorExecutable} ${lib.escapeShellArgs _compressorArgsReal}";
passthru = {
compressorExecutableFunction = _compressorFunction;
compressorArgs = _compressorArgsReal;
};
inherit extension makeUInitrd uInitrdArch prepend;
${if makeUInitrd then "uInitrdCompression" else null} = uInitrdCompression;
passAsFile = ["contents"];
contents = lib.concatMapStringsSep "\n" ({ object, symlink, ... }: "${object}\n${if symlink == null then "" else symlink}") contents + "\n";
nativeBuildInputs = [makeInitrdNGTool patchelf cpio] ++ lib.optional makeUInitrd ubootTools;
} ''
mkdir ./root
make-initrd-ng "$contentsPath" ./root
mkdir "$out"
(cd root && find * .[^.*] -exec touch -h -d '@1' '{}' +)
for PREP in $prepend; do
cat $PREP >> $out/initrd
done
(cd root && find * .[^.*] -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null | eval -- $compress >> "$out/initrd")
if [ -n "$makeUInitrd" ]; then
mkimage -A "$uInitrdArch" -O linux -T ramdisk -C "$uInitrdCompression" -d "$out/initrd" $out/initrd.img
# Compatibility symlink
ln -sf "initrd.img" "$out/initrd"
else
ln -s "initrd" "$out/initrd$extension"
fi
''

View file

@ -0,0 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "make-initrd-ng"
version = "0.1.0"

View file

@ -0,0 +1,9 @@
[package]
name = "make-initrd-ng"
version = "0.1.0"
authors = ["Will Fancher <elvishjerricco@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,79 @@
# What is this for?
NixOS's traditional initrd is generated by listing the paths that
should be included in initrd and copying the full runtime closure of
those paths into the archive. For most things, like almost any
executable, this involves copying the entirety of huge packages like
glibc, when only things like the shared library files are needed. To
solve this, NixOS does a variety of patchwork to edit the files being
copied in so they only refer to small, patched up paths. For instance,
executables and their shared library dependencies are copied into an
`extraUtils` derivation, and every ELF file is patched to refer to
files in that output.
The problem with this is that it is often difficult to correctly patch
some things. For instance, systemd bakes the path to the `mount`
command into the binary, so patchelf is no help. Instead, it's very
often easier to simply copy the desired files to their original store
locations in initrd and not copy their entire runtime closure. This
does mean that it is the burden of the developer to ensure that all
necessary dependencies are copied in, as closures won't be
consulted. However, it is rare that full closures are actually
desirable, so in the traditional initrd, the developer was likely to
do manual work on patching the dependencies explicitly anyway.
# How it works
This program is similar to its inspiration (`find-libs` from the
traditional initrd), except that it also handles symlinks and
directories according to certain rules. As input, it receives a
sequence of pairs of paths. The first path is an object to copy into
initrd. The second path (if not empty) is the path to a symlink that
should be placed in the initrd, pointing to that object. How that
object is copied depends on its type.
1. A regular file is copied directly to the same absolute path in the
initrd.
- If it is *also* an ELF file, then all of its direct shared
library dependencies are also listed as objects to be copied.
2. A directory's direct children are listed as objects to be copied,
and a directory at the same absolute path in the initrd is created.
3. A symlink's target is listed as an object to be copied.
There are a couple of quirks to mention here. First, the term "object"
refers to the final file path that the developer intends to have
copied into initrd. This means any parent directory is not considered
an object just because its child was listed as an object in the
program input; instead those intermediate directories are simply
created in support of the target object. Second, shared libraries,
directory children, and symlink targets aren't immediately recursed,
because they simply get listed as objects themselves, and are
therefore traversed when they themselves are processed. Finally,
symlinks in the intermediate directories leading to an object are
preserved, meaning an input object `/a/symlink/b` will just result in
initrd containing `/a/symlink -> /target/b` and `/target/b`, even if
`/target` has other children. Preserving symlinks in this manner is
important for things like systemd.
These rules automate the most important and obviously necessary
copying that needs to be done in most cases, allowing programs and
configuration files to go unpatched, while keeping the content of the
initrd to a minimum.
# Why Rust?
- A prototype of this logic was written in Bash, in an attempt to keep
with its `find-libs` ancestor, but that program was difficult to
write, and ended up taking several minutes to run. This program runs
in less than a second, and the code is substantially easier to work
with.
- This will not require end users to install a rust toolchain to use
NixOS, as long as this tool is cached by Hydra. And if you're
bootstrapping NixOS from source, rustc is already required anyway.
- Rust was favored over Python for its type system, and because if you
want to go fast, why not go *really fast*?

View file

@ -0,0 +1,216 @@
use std::collections::{HashSet, VecDeque};
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::hash::Hash;
use std::io::{BufReader, BufRead, Error, ErrorKind};
use std::os::unix;
use std::path::{Component, Path, PathBuf};
use std::process::Command;
struct NonRepeatingQueue<T> {
queue: VecDeque<T>,
seen: HashSet<T>,
}
impl<T> NonRepeatingQueue<T> {
fn new() -> NonRepeatingQueue<T> {
NonRepeatingQueue {
queue: VecDeque::new(),
seen: HashSet::new(),
}
}
}
impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> {
fn push_back(&mut self, value: T) -> bool {
if self.seen.contains(&value) {
false
} else {
self.seen.insert(value.clone());
self.queue.push_back(value);
true
}
}
fn pop_front(&mut self) -> Option<T> {
self.queue.pop_front()
}
}
fn patch_elf<S: AsRef<OsStr>, P: AsRef<OsStr>>(mode: S, path: P) -> Result<String, Error> {
let output = Command::new("patchelf")
.arg(&mode)
.arg(&path)
.output()?;
if output.status.success() {
Ok(String::from_utf8(output.stdout).expect("Failed to parse output"))
} else {
Err(Error::new(ErrorKind::Other, format!("failed: patchelf {:?} {:?}", OsStr::new(&mode), OsStr::new(&path))))
}
}
fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path> + AsRef<OsStr>>(
source: P,
target: S,
queue: &mut NonRepeatingQueue<Box<Path>>,
) -> Result<(), Error> {
fs::copy(&source, &target)?;
if !Command::new("ldd").arg(&source).output()?.status.success() {
// Not dynamically linked - no need to recurse
return Ok(());
}
let rpath_string = patch_elf("--print-rpath", &source)?;
let needed_string = patch_elf("--print-needed", &source)?;
// Shared libraries don't have an interpreter
if let Ok(interpreter_string) = patch_elf("--print-interpreter", &source) {
queue.push_back(Box::from(Path::new(&interpreter_string.trim())));
}
let rpath = rpath_string.trim().split(":").map(|p| Box::<Path>::from(Path::new(p))).collect::<Vec<_>>();
for line in needed_string.lines() {
let mut found = false;
for path in &rpath {
let lib = path.join(line);
if lib.exists() {
// No need to recurse. The queue will bring it back round.
queue.push_back(Box::from(lib.as_path()));
found = true;
break;
}
}
if !found {
// glibc makes it tricky to make this an error because
// none of the files have a useful rpath.
println!("Warning: Couldn't satisfy dependency {} for {:?}", line, OsStr::new(&source));
}
}
// Make file writable to strip it
let mut permissions = fs::metadata(&target)?.permissions();
permissions.set_readonly(false);
fs::set_permissions(&target, permissions)?;
// Strip further than normal
if !Command::new("strip").arg("--strip-all").arg(OsStr::new(&target)).output()?.status.success() {
println!("{:?} was not successfully stripped.", OsStr::new(&target));
}
Ok(())
}
fn queue_dir<P: AsRef<Path>>(
source: P,
queue: &mut NonRepeatingQueue<Box<Path>>,
) -> Result<(), Error> {
for entry in fs::read_dir(source)? {
let entry = entry?;
// No need to recurse. The queue will bring us back round here on its own.
queue.push_back(Box::from(entry.path().as_path()));
}
Ok(())
}
fn handle_path(
root: &Path,
p: &Path,
queue: &mut NonRepeatingQueue<Box<Path>>,
) -> Result<(), Error> {
let mut source = PathBuf::new();
let mut target = Path::new(root).to_path_buf();
let mut iter = p.components().peekable();
while let Some(comp) = iter.next() {
match comp {
Component::Prefix(_) => panic!("This tool is not meant for Windows"),
Component::RootDir => {
target.clear();
target.push(root);
source.clear();
source.push("/");
}
Component::CurDir => {}
Component::ParentDir => {
// Don't over-pop the target if the path has too many ParentDirs
if source.pop() {
target.pop();
}
}
Component::Normal(name) => {
target.push(name);
source.push(name);
let typ = fs::symlink_metadata(&source)?.file_type();
if typ.is_file() && !target.exists() {
copy_file(&source, &target, queue)?;
} else if typ.is_symlink() {
let link_target = fs::read_link(&source)?;
// Create the link, then push its target to the queue
if !target.exists() {
unix::fs::symlink(&link_target, &target)?;
}
source.pop();
source.push(link_target);
while let Some(c) = iter.next() {
source.push(c);
}
let link_target_path = source.as_path();
if link_target_path.exists() {
queue.push_back(Box::from(link_target_path));
}
break;
} else if typ.is_dir() {
if !target.exists() {
fs::create_dir(&target)?;
}
// Only recursively copy if the directory is the target object
if iter.peek().is_none() {
queue_dir(&source, queue)?;
}
}
}
}
}
Ok(())
}
fn main() -> Result<(), Error> {
let args: Vec<String> = env::args().collect();
let input = fs::File::open(&args[1])?;
let output = &args[2];
let out_path = Path::new(output);
let mut queue = NonRepeatingQueue::<Box<Path>>::new();
let mut lines = BufReader::new(input).lines();
while let Some(obj) = lines.next() {
// Lines should always come in pairs
let obj = obj?;
let sym = lines.next().unwrap()?;
let obj_path = Path::new(&obj);
queue.push_back(Box::from(obj_path));
if !sym.is_empty() {
println!("{} -> {}", &sym, &obj);
// We don't care about preserving symlink structure here
// nearly as much as for the actual objects.
let link_string = format!("{}/{}", output, sym);
let link_path = Path::new(&link_string);
let mut link_parent = link_path.to_path_buf();
link_parent.pop();
fs::create_dir_all(link_parent)?;
unix::fs::symlink(obj_path, link_path)?;
}
}
while let Some(obj) = queue.pop_front() {
handle_path(out_path, &*obj, &mut queue)?;
}
Ok(())
}

View file

@ -0,0 +1,113 @@
# Create an initramfs containing the closure of the specified
# file system objects. An initramfs is used during the initial
# stages of booting a Linux system. It is loaded by the boot loader
# along with the kernel image. It's supposed to contain everything
# (such as kernel modules) necessary to allow us to mount the root
# file system. Once the root file system is mounted, the `real' boot
# script can be called.
#
# An initramfs is a cpio archive, and may be compressed with a number
# of algorithms.
let
# Some metadata on various compression programs, relevant to naming
# the initramfs file and, if applicable, generating a u-boot image
# from it.
compressors = import ./initrd-compressor-meta.nix;
# Get the basename of the actual compression program from the whole
# compression command, for the purpose of guessing the u-boot
# compression type and filename extension.
compressorName = fullCommand: builtins.elemAt (builtins.match "([^ ]*/)?([^ ]+).*" fullCommand) 1;
in
{ stdenvNoCC, perl, libarchive, ubootTools, lib, pkgsBuildHost
# Name of the derivation (not of the resulting file!)
, name ? "initrd"
# Program used to compress the cpio archive; use "cat" for no compression.
# This can also be a function which takes a package set and returns the path to the compressor,
# such as `pkgs: "${pkgs.lzop}/bin/lzop"`.
, compressor ? "gzip"
, _compressorFunction ?
if lib.isFunction compressor then compressor
else if ! builtins.hasContext compressor && builtins.hasAttr compressor compressors then compressors.${compressor}.executable
else _: compressor
, _compressorExecutable ? _compressorFunction pkgsBuildHost
, _compressorName ? compressorName _compressorExecutable
, _compressorMeta ? compressors.${_compressorName} or {}
# List of arguments to pass to the compressor program, or null to use its defaults
, compressorArgs ? null
, _compressorArgsReal ? if compressorArgs == null then _compressorMeta.defaultArgs or [] else compressorArgs
# Filename extension to use for the compressed initramfs. This is
# included for clarity, but $out/initrd will always be a symlink to
# the final image.
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
, extension ? _compressorMeta.extension or
(throw "Unrecognised compressor ${_compressorName}, please specify filename extension")
# List of { object = path_or_derivation; symlink = "/path"; }
# The paths are copied into the initramfs in their nix store path
# form, then linked at the root according to `symlink`.
, contents
# List of uncompressed cpio files to prepend to the initramfs. This
# can be used to add files in specified paths without them becoming
# symlinks to store paths.
, prepend ? []
# Whether to wrap the initramfs in a u-boot image.
, makeUInitrd ? stdenvNoCC.hostPlatform.linux-kernel.target == "uImage"
# If generating a u-boot image, the architecture to use. The default
# guess may not align with u-boot's nomenclature correctly, so it can
# be overridden.
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 for a list.
, uInitrdArch ? stdenvNoCC.hostPlatform.linuxArch
# The name of the compression, as recognised by u-boot.
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L195-204 for a list.
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
, uInitrdCompression ? _compressorMeta.ubootName or
(throw "Unrecognised compressor ${_compressorName}, please specify uInitrdCompression")
}:
let
# !!! Move this into a public lib function, it is probably useful for others
toValidStoreName = x: with builtins;
lib.concatStringsSep "-" (filter (x: !(isList x)) (split "[^a-zA-Z0-9_=.?-]+" x));
in stdenvNoCC.mkDerivation rec {
inherit name makeUInitrd extension uInitrdArch prepend;
${if makeUInitrd then "uInitrdCompression" else null} = uInitrdCompression;
builder = ./make-initrd.sh;
nativeBuildInputs = [ perl libarchive ]
++ lib.optional makeUInitrd ubootTools;
compress = "${_compressorExecutable} ${lib.escapeShellArgs _compressorArgsReal}";
# Pass the function through, for reuse in append-initrd-secrets. The
# function is used instead of the string, in order to support
# cross-compilation (append-initrd-secrets running on a different
# architecture than what the main initramfs is built on).
passthru = {
compressorExecutableFunction = _compressorFunction;
compressorArgs = _compressorArgsReal;
};
# !!! should use XML.
objects = map (x: x.object) contents;
symlinks = map (x: x.symlink) contents;
suffices = map (x: if x ? suffix then x.suffix else "none") contents;
# For obtaining the closure of `contents'.
# Note: we don't use closureInfo yet, as that won't build with nix-1.x.
# See #36268.
exportReferencesGraph =
lib.zipListsWith
(x: i: [("closure-${toValidStoreName (baseNameOf x.symlink)}-${toString i}") x.object])
contents
(lib.range 0 (lib.length contents - 1));
pathsFromGraph = ./paths-from-graph.pl;
}

View file

@ -0,0 +1,51 @@
source $stdenv/setup
set -o pipefail
objects=($objects)
symlinks=($symlinks)
suffices=($suffices)
mkdir root
# Needed for splash_helper, which gets run before init.
mkdir root/dev
mkdir root/sys
mkdir root/proc
for ((n = 0; n < ${#objects[*]}; n++)); do
object=${objects[$n]}
symlink=${symlinks[$n]}
suffix=${suffices[$n]}
if test "$suffix" = none; then suffix=; fi
mkdir -p $(dirname root/$symlink)
ln -s $object$suffix root/$symlink
done
# Get the paths in the closure of `object'.
storePaths=$(perl $pathsFromGraph closure-*)
# Paths in cpio archives *must* be relative, otherwise the kernel
# won't unpack 'em.
(cd root && cp -prP --parents $storePaths .)
# Put the closure in a gzipped cpio archive.
mkdir -p $out
for PREP in $prepend; do
cat $PREP >> $out/initrd
done
(cd root && find * .[^.*] -exec touch -h -d '@1' '{}' +)
(cd root && find * .[^.*] -print0 | sort -z | bsdtar --uid 0 --gid 0 -cnf - -T - | bsdtar --null -cf - --format=newc @- | eval -- $compress >> "$out/initrd")
if [ -n "$makeUInitrd" ]; then
mkimage -A "$uInitrdArch" -O linux -T ramdisk -C "$uInitrdCompression" -d "$out/initrd" $out/initrd.img
# Compatibility symlink
ln -sf "initrd.img" "$out/initrd"
else
ln -s "initrd" "$out/initrd$extension"
fi

View file

@ -0,0 +1,15 @@
# Given a kernel build (with modules in $kernel/lib/modules/VERSION),
# produce a module tree in $out/lib/modules/VERSION that contains only
# the modules identified by `rootModules', plus their dependencies.
# Also generate an appropriate modules.dep.
{ stdenvNoCC, kernel, firmware, nukeReferences, rootModules
, kmod, allowMissing ? false }:
stdenvNoCC.mkDerivation {
name = kernel.name + "-shrunk";
builder = ./modules-closure.sh;
nativeBuildInputs = [ nukeReferences kmod ];
inherit kernel firmware rootModules allowMissing;
allowedReferences = ["out"];
}

View file

@ -0,0 +1,101 @@
source $stdenv/setup
# When no modules are built, the $out/lib/modules directory will not
# exist. Because the rest of the script assumes it does exist, we
# handle this special case first.
if ! test -d "$kernel/lib/modules"; then
if test -z "$rootModules" || test -n "$allowMissing"; then
mkdir -p "$out"
exit 0
else
echo "Required modules: $rootModules"
echo "Can not derive a closure of kernel modules because no modules were provided."
exit 1
fi
fi
version=$(cd $kernel/lib/modules && ls -d *)
echo "kernel version is $version"
# Determine the dependencies of each root module.
mkdir -p $out/lib/modules/"$version"
touch closure
for module in $rootModules; do
echo "root module: $module"
modprobe --config no-config -d $kernel --set-version "$version" --show-depends "$module" \
| while read cmd module args; do
case "$cmd" in
builtin)
touch found
echo "$module" >>closure
echo " builtin dependency: $module";;
insmod)
touch found
if ! test -e "$module"; then
echo " dependency not found: $module"
exit 1
fi
target=$(echo "$module" | sed "s^$NIX_STORE.*/lib/modules/^$out/lib/modules/^")
if test -e "$target"; then
echo " dependency already copied: $module"
continue
fi
echo "$module" >>closure
echo " copying dependency: $module"
mkdir -p $(dirname $target)
cp "$module" "$target"
# If the kernel is compiled with coverage instrumentation, it
# contains the paths of the *.gcda coverage data output files
# (which it doesn't actually use...). Get rid of them to prevent
# the whole kernel from being included in the initrd.
nuke-refs "$target"
echo "$target" >> $out/insmod-list;;
*)
echo " unexpected modprobe output: $cmd $module"
exit 1;;
esac
done || test -n "$allowMissing"
if ! test -e found; then
echo " not found"
if test -z "$allowMissing"; then
exit 1
fi
else
rm found
fi
done
mkdir -p $out/lib/firmware
for module in $(cat closure); do
# for builtin modules, modinfo will reply with a wrong output looking like:
# $ modinfo -F firmware unix
# name: unix
#
# There is a pending attempt to fix this:
# https://github.com/NixOS/nixpkgs/pull/96153
# https://lore.kernel.org/linux-modules/20200823215433.j5gc5rnsmahpf43v@blumerang/T/#u
#
# For now, the workaround is just to filter out the extraneous lines out
# of its output.
for i in $(modinfo -b $kernel --set-version "$version" -F firmware $module | grep -v '^name:'); do
mkdir -p "$out/lib/firmware/$(dirname "$i")"
echo "firmware for $module: $i"
for name in "$i" "$i.xz" ""; do
[ -z "$name" ] && echo "WARNING: missing firmware $i for module $module"
if cp "$firmware/lib/firmware/$name" "$out/lib/firmware/$name" 2>/dev/null; then
break
fi
done
done
done
# copy module ordering hints for depmod
cp $kernel/lib/modules/"$version"/modules.order $out/lib/modules/"$version"/.
cp $kernel/lib/modules/"$version"/modules.builtin $out/lib/modules/"$version"/.
depmod -b $out -a $version
# remove original hints from final derivation
rm $out/lib/modules/"$version"/modules.order
rm $out/lib/modules/"$version"/modules.builtin

View file

@ -0,0 +1,68 @@
# NOTE: this script is deprecated. Use closureInfo instead.
# Parses a /nix/store/*-closure file and prints
# various information.
# By default, the nodes in the graph are printed to stdout.
# If printRegistration is set, then the graph is written
# as a registration file for a manifest is written
# in the `nix-store --load-db' format.
use strict;
use File::Basename;
my %storePaths;
my %refs;
# Each argument on the command line is a graph file.
# The graph file contains line-triples and a variable
# number of references:
# <store-path>
# <deriver>
# <count>
# <ref-#1>
# ...
# <ref-#count>
foreach my $graph (@ARGV) {
open GRAPH, "<$graph" or die;
while (<GRAPH>) {
chomp;
my $storePath = "$_";
$storePaths{$storePath} = 1;
my $deriver = <GRAPH>; chomp $deriver;
my $count = <GRAPH>; chomp $count;
my @refs = ();
for (my $i = 0; $i < $count; ++$i) {
my $ref = <GRAPH>; chomp $ref;
push @refs, $ref;
}
$refs{$storePath} = \@refs;
}
close GRAPH;
}
if ($ENV{"printRegistration"} eq "1") {
# This is the format used by `nix-store --register-validity
# --hash-given' / `nix-store --load-db'.
foreach my $storePath (sort (keys %storePaths)) {
print "$storePath\n";
print "0000000000000000000000000000000000000000000000000000000000000000\n"; # !!! fix
print "0\n"; # !!! fix
print "\n"; # don't care about preserving the deriver
print scalar(@{$refs{$storePath}}), "\n";
foreach my $ref (@{$refs{$storePath}}) {
print "$ref\n";
}
}
}
else {
foreach my $storePath (sort (keys %storePaths)) {
print "$storePath\n";
}
}