Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

441 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

linux_builder

The IGLOO kernels: pristine upstream release tarballs plus an explicit IGLOO patch series, cross-built by kernelsmith for every target penguin emulates.

Two kernel versions (4.10, 6.13) across up to eleven targets — 19 buildable cells in all. Each cell ships a boot image, a vmlinux, a minimal kernel-devel tree, perf, and the OSI/COSI analysis artifacts penguin needs.

Quick start

Everything is a Nix flake output. There is no container to build first.

nix build .#packages.x86_64-linux."kernel-6.13-armel"   # one cell
nix build .#all                                          # every buildable cell
nix build .#kernels                                      # the payload as a tree
nix build .#kernels-latest                               # ...and as a tarball

Cell names contain a ., which Nix parses as an attribute-path separator, so the quoting above is mandatory.#kernel-6.13-armel does not resolve.

Where the source comes from

Not from a submodule. patches/ carries the IGLOO delta as an ordered series applied to a pristine kernel.org tarball, pinned by hash in patches/base.json. See patches/README.md for the layout, how much is really shared between versions, and how to add or refresh a patch.

To prove a series still reproduces the fork branch it replaced:

./scripts/verify-series.sh 4.10 <tarball> <fork-ref> <git-dir>

scripts/import-series.sh and scripts/export-series.sh move patches between this repo and a fork branch.

Checks

command what it proves
nix build .#boot-check every kernel actually boots under the qemu machine penguin runs it on
nix build .#shape-check each artifact's ELF class, byte order and machine match its target name
nix build .#config-required every kernel ended up with the options IGLOO needs (see Modifying configs)
nix flake check the above, plus evaluation of every output

boot-check is the one that matters most, and it is newer than the rest. nixdev_0.1.0 shipped a 4.10/x86_64 kernel that compiled, linked, packaged, had the correct ELF shape and was the same size as the Docker build's — and printed nothing before dying. Every other gate passed it; it was caught three repos downstream in penguin's integration tests. Booting it is the only check that would have said so. See nix/boot.nix.

32-bit powerpc is boot-tested as an explicit SKIP: penguin's arch_registry.py records no qemu machine for it, so it is unbootable by declaration rather than by oversight.

Modifying configs

Configs live in configs/<version>/<target>, with shared fragments pulled in by #include (all-common.inc, arm-common.inc, ...). They are assembled with cpp -P -undef and then olddefconfig.

The file you edit is almost never the file an option comes from, and the config the kernel is built with is a third thing again — olddefconfig runs last and drops anything whose dependencies are unmet. Three tools follow from that, one per question:

question command
Did every cell end up with what IGLOO needs? nix build .#config-required
Where does CONFIG_X for this cell come from? nix run .#config-explain -- 6.13 x86_64 CONFIG_IGLOO
Which lines am I writing that do nothing? nix build .#config-redundant

config-required is a gate, and it reads the shipped .config rather than the fragments — that distinction is the entire point. It asserts the options whose absence is silent: a kernel that builds, boots, and then does not do its job. CONFIG_MODVERSIONS is the sharpest of them; without it a mismatched igloo.ko loads quietly instead of being rejected, which is strictly worse than the mismatch. See nix/config-tools.nix, where every entry says what breaks without it.

config-explain builds no kernel, so it is instant. It exists because grep does not answer the question: CONFIG_IGLOO is set in all-common.inc and no target sets it directly, so grepping configs/6.13/x86_64 finds nothing.

$ nix run .#config-explain -- 6.13 armel CONFIG_MODULES
  CONFIG_MODULES=y
      configs/6.13/all-common.inc:159
      via armel -> arm-common.inc -> all-common.inc
* CONFIG_MODULES=y
      configs/6.13/all-common.inc:162
      via armel -> arm-common.inc -> all-common.inc

  2 assignments; the one marked * wins (cpp: last wins).

config-redundant separates two things that look alike and are not: an option assigned twice (always a bug — only the last has effect) and an option that did not survive olddefconfig (usually a dependency you did not notice). It currently reports 595 duplicate assignments across the matrix.

And the raw savedefconfig lint, per cell or across the matrix:

nix build .#packages.x86_64-linux."config-lint-6.13-armel"   # one cell
nix build .#config-lint                                       # every cell

Each produces config_<version>_<target>.linted (the savedefconfig form) and a .diff against the assembled config. This is advisory, not a gate. savedefconfig prunes options that are already the arch default and de-duplicates, so a readable config fragment and its savedefconfig form never match — the diff tells you which of your lines were redundant, and that is all it is for. Turning it into an assertion would mean replacing the #include structure with savedefconfig output, which is a bad trade.

This replaces ./build.sh --config-only, which did the same thing and likewise discarded its own exit status.

Releases

There are two, and they are deliberately separate.

vX.Y.Z — the real version line. Cut automatically on every merge to main. The full matrix runs, boot-tests, and publishes kernels-latest.tar.gz

  • kernel-devel-all.tar.gz. The version comes from reecetech/version-increment with use_api: true, which reads git tags, takes the highest by sort -V, and bumps the patch.

To move the version line (say 3.6.x → 4.0.x), push a bare marker tag at main's tip and let the patch increment continue from it:

git tag v4.0.0 <main-tip> && git push origin v4.0.0   # marker only, no release
# the next merge to main then cuts v4.0.1

Do not set increment: minor in the workflow to force a jump. It is not self-clearing, so it silently bumps the release after it as well, until someone remembers to revert. The marker tag has no such landmine.

nixdev_* — prereleases, off the version line. The tag is the version; no increment. For handing a downstream repo a fixed, immutable kernel set to test against without consuming a version number.

git tag -a nixdev_0.1.2 -m "..." && git push origin nixdev_0.1.2

Both run the full matrix, and both are push events — so nix-setup's skip-push default does not apply and every cell lands in the rehosting-tools Cachix. That is what lets downstream repos substitute these kernels instead of cross-building all 19 in their own CI.

Consumers

  • penguin takes this repo as a flake input and stages .#kernels directly. The tarball is kept for other consumers, but the flake is the seam that records which compiler built these kernels — a tarball pin cannot.
  • igloo_driver builds igloo.ko against the kernel derivations here, so a mismatched (kernel, module) pair is not expressible. Note that a nix-built kernel-devel-all.tar.gz is not usable from a non-nix container: it ships host tools linked against /nix/store.

History: the Docker path

This repo used to build inside rehosting/embedded-toolchains via build.sh, _in_container_build.sh and a Dockerfile, with the kernel source arriving as two git submodules. All of that is removed; it lives in git history.

Comments throughout nix/ name _in_container_build.sh where the Nix code is a faithful port of a specific step in it. Those references are deliberate provenance — they say why a piece of the build looks the way it does — and point at the file as it existed before its removal.

Two things worth knowing about what that path did, because both were silent:

  • Its toolchains were unversioned wget musl.cc/*-cross.tgz downloads, so every kernel this repo has ever released has no recorded compiler identity. kernelsmith exists to fix that.
  • It hard-coded a hand-built x86_64-legacy toolchain for (x86_64, 4.10) alone, pinning binutils 2.30. The reason was never written down. It is that binutils ≥ 2.31 emits R_X86_64_PLT32, which Linux only learned in 4.16 — and that omission is exactly what shipped a dead kernel in nixdev_0.1.0. It is now a resolver decision in kernelsmith with the reason attached.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

6 watching

Forks

Releases

Used by

Contributors

Languages