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:
commit
56de2bcd43
30691 changed files with 3076956 additions and 0 deletions
16
pkgs/build-support/kernel/compress-firmware-xz.nix
Normal file
16
pkgs/build-support/kernel/compress-firmware-xz.nix
Normal 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
|
||||
''
|
||||
53
pkgs/build-support/kernel/initrd-compressor-meta.nix
Normal file
53
pkgs/build-support/kernel/initrd-compressor-meta.nix
Normal 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 = [];
|
||||
};
|
||||
}
|
||||
16
pkgs/build-support/kernel/make-initrd-ng-tool.nix
Normal file
16
pkgs/build-support/kernel/make-initrd-ng-tool.nix
Normal 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 ]}
|
||||
'';
|
||||
}
|
||||
93
pkgs/build-support/kernel/make-initrd-ng.nix
Normal file
93
pkgs/build-support/kernel/make-initrd-ng.nix
Normal 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
|
||||
''
|
||||
5
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
Normal file
5
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
Normal 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"
|
||||
9
pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
Normal file
9
pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
Normal 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]
|
||||
79
pkgs/build-support/kernel/make-initrd-ng/README.md
Normal file
79
pkgs/build-support/kernel/make-initrd-ng/README.md
Normal 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*?
|
||||
216
pkgs/build-support/kernel/make-initrd-ng/src/main.rs
Normal file
216
pkgs/build-support/kernel/make-initrd-ng/src/main.rs
Normal 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(())
|
||||
}
|
||||
113
pkgs/build-support/kernel/make-initrd.nix
Normal file
113
pkgs/build-support/kernel/make-initrd.nix
Normal 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;
|
||||
}
|
||||
51
pkgs/build-support/kernel/make-initrd.sh
Normal file
51
pkgs/build-support/kernel/make-initrd.sh
Normal 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
|
||||
15
pkgs/build-support/kernel/modules-closure.nix
Normal file
15
pkgs/build-support/kernel/modules-closure.nix
Normal 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"];
|
||||
}
|
||||
101
pkgs/build-support/kernel/modules-closure.sh
Normal file
101
pkgs/build-support/kernel/modules-closure.sh
Normal 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
|
||||
68
pkgs/build-support/kernel/paths-from-graph.pl
Normal file
68
pkgs/build-support/kernel/paths-from-graph.pl
Normal 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";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue