Generic kernel + init= boot path for OCI-pulled images
Closes the full arc: banger kernel pull + image pull + vm create + vm ssh now works end-to-end against docker.io/library/debian:bookworm with zero manual image building. Generic kernel: - New scripts/make-generic-kernel.sh builds vmlinux from upstream kernel.org sources using Firecracker's official minimal config (configs/firecracker-x86_64-6.1.config). All critical drivers (virtio_blk, virtio_net, ext4, vsock) compiled in — no modules, no initramfs needed. - Published as generic-6.12 in the catalog (kernels.thaloco.com). - catalog.json updated with the new entry. Direct-boot init= override (vm_lifecycle.go): - For images without an initrd (direct-boot / OCI-pulled), banger now passes init=/usr/local/libexec/banger-first-boot on the kernel cmdline. The script runs as PID 1, mounts /proc /sys /dev /run, checks for systemd — if present execs it immediately; if not (container images), installs systemd-sysv + openssh-server via the guest's package manager, then execs systemd. - Also passes kernel-level ip= parameter via BuildBootArgsWithKernelIP so the kernel configures the network interface before init runs (container images don't ship iproute2, so the userspace bootstrap script can't call ip(8)). - Masks dev-ttyS0.device and dev-vdb.device systemd units that otherwise wait 90s for udev events that never fire in Firecracker guests started from container rootfses. first-boot.sh rewritten as universal init wrapper: - Works as PID 1 (mounts essential filesystems) OR as a systemd oneshot (existing behavior). - Installs both systemd-sysv AND openssh-server (container images have neither). - Dispatch updated: debian, alpine, fedora, arch, opensuse families + ID_LIKE fallback. All tests updated. Opencode capability skip for direct-boot images: - The opencode readiness check (WaitReady on vsock port 4096) now returns nil for images without an initrd, since pulled container images don't ship the opencode service. Without this, the VM would be marked as error for lacking an opinionated add-on. Docs: README and kernel-catalog.md updated to recommend generic-6.12 as the default kernel for OCI-pulled images. AGENTS.md notes the new build script. Verified live: - banger kernel pull generic-6.12 - banger image pull docker.io/library/debian:bookworm --kernel-ref generic-6.12 - banger vm create --image debian-bookworm --name testbox --nat - banger vm ssh testbox -- "id; uname -r; systemctl is-active banger-vsock-agent" → uid=0(root), kernel 6.12.8, Debian bookworm, vsock-agent active, sshd running, SSH working. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2478fe3cc3
commit
8f4be112c2
10 changed files with 3808 additions and 65 deletions
|
|
@ -20,6 +20,7 @@ Always run `make build` before commit.
|
|||
- `./build/bin/banger image build --from-image <image>` builds a managed image from an existing registered image.
|
||||
- `./build/bin/banger image register ...` registers an unmanaged host-side image stack.
|
||||
- `./build/bin/banger image promote <image>` copies an unmanaged image into daemon-owned managed artifacts.
|
||||
- `scripts/make-generic-kernel.sh` builds a Firecracker-optimized vmlinux from upstream sources (no initrd, all drivers built-in). This is the recommended kernel for OCI-pulled images.
|
||||
- `make void-kernel`, `make rootfs-void`, and `make void-register` drive the experimental Void flow under `./build/manual`.
|
||||
- `scripts/publish-kernel.sh <name>` packages a locally-imported kernel and uploads it to the catalog; see `docs/kernel-catalog.md`.
|
||||
- `banger image pull <oci-ref> --kernel-ref <name>` pulls a rootfs from any OCI registry; see `docs/oci-import.md` (experimental — file-ownership caveat).
|
||||
|
|
|
|||
|
|
@ -92,11 +92,11 @@ Or pull a pre-built kernel from the catalog and reference it by name:
|
|||
|
||||
```bash
|
||||
./build/bin/banger kernel list --available
|
||||
./build/bin/banger kernel pull void-6.12
|
||||
./build/bin/banger kernel pull generic-6.12
|
||||
./build/bin/banger image register \
|
||||
--name base \
|
||||
--rootfs /abs/path/rootfs.ext4 \
|
||||
--kernel-ref void-6.12
|
||||
--kernel-ref generic-6.12
|
||||
```
|
||||
|
||||
See [`docs/kernel-catalog.md`](docs/kernel-catalog.md) for catalog
|
||||
|
|
@ -106,7 +106,7 @@ Or pull a rootfs directly from any OCI registry (Docker Hub, GHCR, …):
|
|||
|
||||
```bash
|
||||
./build/bin/banger image pull docker.io/library/debian:bookworm \
|
||||
--kernel-ref void-6.12
|
||||
--kernel-ref generic-6.12
|
||||
```
|
||||
|
||||
`image pull` downloads the image, flattens its layers into an ext4
|
||||
|
|
|
|||
3556
configs/firecracker-x86_64-6.1.config
Normal file
3556
configs/firecracker-x86_64-6.1.config
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -31,6 +31,19 @@ against the embedded catalog entry, decompresses it (zstd), extracts it
|
|||
into `~/.local/state/banger/kernels/<name>/`, and writes a manifest. Path
|
||||
traversal entries and unsafe symlinks are rejected.
|
||||
|
||||
## Kernel types
|
||||
|
||||
**`generic-<version>`** — built from upstream kernel.org sources with
|
||||
Firecracker's official config. All essential drivers (virtio_blk,
|
||||
virtio_net, ext4, vsock) compiled in — no modules, no initramfs. This
|
||||
is the recommended kernel for OCI-pulled images (Debian, Ubuntu,
|
||||
Fedora, etc.). Build with `scripts/make-generic-kernel.sh`.
|
||||
|
||||
**`void-<version>` / `alpine-<version>`** — distro-specific kernels
|
||||
built from Void/Alpine package repos. Include initramfs + modules.
|
||||
These are for the `make rootfs-void` / `make rootfs-alpine` manual
|
||||
flows where the initramfs is paired with its matching rootfs.
|
||||
|
||||
## Adding or updating an entry
|
||||
|
||||
The repo has no CI for kernel publishing yet. Catalog updates are manual
|
||||
|
|
@ -38,22 +51,22 @@ and infrequent (kernel version bumps every few weeks at most).
|
|||
|
||||
```bash
|
||||
# 1. Build the kernel locally with the existing helper.
|
||||
make void-kernel # or: make alpine-kernel
|
||||
scripts/make-generic-kernel.sh # or: make void-kernel / make alpine-kernel
|
||||
|
||||
# 2. Import it into the local catalog so the canonical layout exists.
|
||||
banger kernel import void-6.12 \
|
||||
--from build/manual/void-kernel \
|
||||
--distro void \
|
||||
banger kernel import generic-6.12 \
|
||||
--from build/manual/generic-kernel \
|
||||
--distro generic \
|
||||
--arch x86_64
|
||||
|
||||
# 3. Package, upload, patch catalog.json.
|
||||
scripts/publish-kernel.sh void-6.12 \
|
||||
--description "Void Linux 6.12 kernel for Firecracker microVMs"
|
||||
scripts/publish-kernel.sh generic-6.12 \
|
||||
--description "Generic Firecracker kernel 6.12 (all drivers built-in, no initrd)"
|
||||
|
||||
# 4. Review and commit the catalog change.
|
||||
git diff -- internal/kernelcat/catalog.json
|
||||
git add internal/kernelcat/catalog.json
|
||||
git commit -m 'kernel catalog: add/update void-6.12'
|
||||
git commit -m 'kernel catalog: add/update generic-6.12'
|
||||
|
||||
# 5. Rebuild so the new catalog is embedded.
|
||||
make build
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package daemon
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"banger/internal/model"
|
||||
"banger/internal/opencode"
|
||||
|
|
@ -11,7 +12,13 @@ type opencodeCapability struct{}
|
|||
|
||||
func (opencodeCapability) Name() string { return "opencode" }
|
||||
|
||||
func (opencodeCapability) PostStart(ctx context.Context, d *Daemon, vm model.VMRecord, _ model.Image) error {
|
||||
func (opencodeCapability) PostStart(ctx context.Context, d *Daemon, vm model.VMRecord, image model.Image) error {
|
||||
if strings.TrimSpace(image.InitrdPath) == "" {
|
||||
// Direct-boot images (OCI pulls) don't ship the opencode
|
||||
// service — skip the readiness check so the VM isn't marked
|
||||
// as error for lacking an opinionated add-on.
|
||||
return nil
|
||||
}
|
||||
return opencode.WaitReady(ctx, d.logger, vm.Runtime.VSockPath, func(stage, detail string) {
|
||||
vmCreateStage(ctx, stage, detail)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"banger/internal/api"
|
||||
"banger/internal/firecracker"
|
||||
"banger/internal/imagepull"
|
||||
"banger/internal/model"
|
||||
"banger/internal/system"
|
||||
)
|
||||
|
|
@ -145,6 +146,20 @@ func (d *Daemon) startVMLocked(ctx context.Context, vm model.VMRecord, image mod
|
|||
}
|
||||
op.stage("firecracker_launch", "log_path", vm.Runtime.LogPath, "metrics_path", vm.Runtime.MetricsPath)
|
||||
vmCreateStage(ctx, "boot_firecracker", "starting firecracker")
|
||||
kernelArgs := system.BuildBootArgs(vm.Name)
|
||||
if strings.TrimSpace(image.InitrdPath) == "" {
|
||||
// Direct-boot image (no initramfs) — the rootfs may be a
|
||||
// container image without /sbin/init or iproute2. Use:
|
||||
// 1. Kernel-level IP config via ip= cmdline (CONFIG_IP_PNP),
|
||||
// so the network is up before init runs — no ip(8) needed.
|
||||
// 2. init= pointing at our universal wrapper which installs
|
||||
// systemd+sshd on first boot if missing.
|
||||
kernelArgs = system.BuildBootArgsWithKernelIP(
|
||||
vm.Name, vm.Runtime.GuestIP, d.config.BridgeIP, d.config.DefaultDNS,
|
||||
) + " init=" + imagepull.FirstBootScriptPath +
|
||||
" systemd.mask=dev-ttyS0.device systemd.mask=dev-vdb.device"
|
||||
}
|
||||
|
||||
machineConfig := firecracker.MachineConfig{
|
||||
BinaryPath: fcPath,
|
||||
VMID: vm.ID,
|
||||
|
|
@ -153,7 +168,7 @@ func (d *Daemon) startVMLocked(ctx context.Context, vm model.VMRecord, image mod
|
|||
MetricsPath: vm.Runtime.MetricsPath,
|
||||
KernelImagePath: image.KernelPath,
|
||||
InitrdPath: image.InitrdPath,
|
||||
KernelArgs: system.BuildBootArgs(vm.Name),
|
||||
KernelArgs: kernelArgs,
|
||||
Drives: []firecracker.DriveConfig{{
|
||||
ID: "rootfs",
|
||||
Path: vm.Runtime.DMDev,
|
||||
|
|
|
|||
|
|
@ -1,34 +1,61 @@
|
|||
#!/bin/sh
|
||||
# banger-first-boot — runs once at the first boot of a pulled OCI image.
|
||||
# Installs openssh-server via the guest's native package manager, enables
|
||||
# and starts the ssh daemon, and removes its own trigger file so the
|
||||
# service is a no-op on subsequent boots.
|
||||
# banger-first-boot — universal init wrapper for banger VMs.
|
||||
#
|
||||
# Distro dispatch is driven by /etc/os-release's ID / ID_LIKE values.
|
||||
# RUN_PLAN=1 in the environment makes this script echo the commands it
|
||||
# would run instead of executing them — used by tests.
|
||||
# When passed as init= on the kernel cmdline (direct-boot images without
|
||||
# an initramfs), this script runs as PID 1. It:
|
||||
# 1. Mounts the essential virtual filesystems (/proc, /sys, /dev, /run)
|
||||
# 2. If systemd (or any init) is already installed, execs it immediately
|
||||
# 3. Otherwise: brings up the network, installs systemd + openssh-server
|
||||
# via the guest's native package manager, then execs systemd
|
||||
#
|
||||
# On subsequent boots (after systemd is installed), step 2 fires in <10ms.
|
||||
#
|
||||
# Test hooks:
|
||||
# RUN_PLAN=1 echo the install command instead of executing it
|
||||
# OS_RELEASE_FILE=<path> override /etc/os-release for distro detection
|
||||
# BANGER_FIRST_BOOT_MARKER=<path> override the marker file path
|
||||
|
||||
set -eu
|
||||
|
||||
log() { printf '[banger-first-boot] %s\n' "$*" >&2; }
|
||||
|
||||
MARKER="${BANGER_FIRST_BOOT_MARKER:-/var/lib/banger/first-boot-pending}"
|
||||
if [ ! -f "$MARKER" ]; then
|
||||
log "marker absent; nothing to do"
|
||||
exit 0
|
||||
# --- Step 1: essential mounts (only when running as PID 1) ---
|
||||
if [ "$$" = "1" ]; then
|
||||
mount -t proc proc /proc 2>/dev/null || true
|
||||
mount -t sysfs sysfs /sys 2>/dev/null || true
|
||||
mount -t devtmpfs devtmpfs /dev 2>/dev/null || true
|
||||
mount -t tmpfs tmpfs /run 2>/dev/null || true
|
||||
mount -t tmpfs tmpfs /tmp 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# If sshd is already present, just enable + start and finish.
|
||||
# The RUN_PLAN env skips this short-circuit so tests can exercise the
|
||||
# dispatch logic on hosts that happen to have sshd installed.
|
||||
if [ "${RUN_PLAN:-0}" != "1" ] && command -v sshd >/dev/null 2>&1; then
|
||||
log "sshd already installed; enabling and starting"
|
||||
systemctl enable --now ssh.service 2>/dev/null || \
|
||||
systemctl enable --now sshd.service 2>/dev/null || true
|
||||
rm -f "$MARKER"
|
||||
exit 0
|
||||
# --- Step 2: if a real init exists, hand off immediately ---
|
||||
# (RUN_PLAN mode skips this so the dispatch logic can be tested on hosts
|
||||
# that have systemd installed.)
|
||||
if [ "${RUN_PLAN:-0}" != "1" ]; then
|
||||
for candidate_init in /usr/lib/systemd/systemd /lib/systemd/systemd /sbin/init; do
|
||||
if [ -x "$candidate_init" ]; then
|
||||
MARKER="${BANGER_FIRST_BOOT_MARKER:-/var/lib/banger/first-boot-pending}"
|
||||
if [ -f "$MARKER" ]; then
|
||||
rm -f "$MARKER"
|
||||
fi
|
||||
log "found init at $candidate_init; handing off"
|
||||
exec "$candidate_init" "$@"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# --- Step 3: no init found — we're on a container image, provision it ---
|
||||
log "no init system found; installing systemd + openssh-server"
|
||||
|
||||
# Bring up network so apt-get/apk can reach package repos.
|
||||
# banger-network-bootstrap reads IP from /proc/cmdline (kernel ip= arg)
|
||||
# or /etc/banger-network.conf (written by vm_disk.patchRootOverlay).
|
||||
if [ -x /usr/local/libexec/banger-network-bootstrap ]; then
|
||||
log "bringing up network"
|
||||
/usr/local/libexec/banger-network-bootstrap || log "network bootstrap failed (continuing anyway)"
|
||||
fi
|
||||
|
||||
# Detect distro
|
||||
DIST=""
|
||||
FAMILY=""
|
||||
OS_RELEASE_FILE="${OS_RELEASE_FILE:-/etc/os-release}"
|
||||
|
|
@ -38,50 +65,48 @@ if [ -r "$OS_RELEASE_FILE" ]; then
|
|||
DIST="${ID:-}"
|
||||
FAMILY="${ID_LIKE:-}"
|
||||
fi
|
||||
|
||||
log "detected distro: ID=$DIST ID_LIKE=$FAMILY"
|
||||
|
||||
# Dispatch. Each branch sets CMD to the single install command.
|
||||
# Dispatch install command
|
||||
CMD=""
|
||||
case "$DIST" in
|
||||
debian|ubuntu|kali|raspbian|linuxmint|pop)
|
||||
CMD="env DEBIAN_FRONTEND=noninteractive apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server"
|
||||
CMD="apt-get update && apt-get install -y systemd-sysv openssh-server"
|
||||
;;
|
||||
alpine)
|
||||
CMD="apk add --no-cache openssh"
|
||||
CMD="apk add --no-cache openrc openssh systemd"
|
||||
;;
|
||||
fedora|rhel|centos|rocky|almalinux)
|
||||
CMD="dnf install -y openssh-server"
|
||||
CMD="dnf install -y systemd openssh-server"
|
||||
;;
|
||||
arch|archlinux|manjaro)
|
||||
CMD="pacman -Sy --noconfirm openssh"
|
||||
;;
|
||||
opensuse*|suse)
|
||||
CMD="zypper --non-interactive install -y openssh"
|
||||
CMD="zypper --non-interactive install -y systemd openssh"
|
||||
;;
|
||||
*)
|
||||
# Fall back to ID_LIKE.
|
||||
case " $FAMILY " in
|
||||
*" debian "*)
|
||||
CMD="env DEBIAN_FRONTEND=noninteractive apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server"
|
||||
CMD="apt-get update && apt-get install -y systemd-sysv openssh-server"
|
||||
;;
|
||||
*" rhel "* | *" fedora "*)
|
||||
CMD="dnf install -y openssh-server"
|
||||
CMD="dnf install -y systemd openssh-server"
|
||||
;;
|
||||
*" arch "*)
|
||||
CMD="pacman -Sy --noconfirm openssh"
|
||||
;;
|
||||
*" suse "*)
|
||||
CMD="zypper --non-interactive install -y openssh"
|
||||
CMD="zypper --non-interactive install -y systemd openssh"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$CMD" ]; then
|
||||
log "no known install command for distro '$DIST' (ID_LIKE='$FAMILY')"
|
||||
log "edit /usr/local/libexec/banger-first-boot to add a branch, then restart banger-first-boot.service"
|
||||
exit 1
|
||||
log "FATAL: no known install command for distro '$DIST' (ID_LIKE='$FAMILY')"
|
||||
log "drop to emergency shell"
|
||||
exec /bin/sh
|
||||
fi
|
||||
|
||||
if [ "${RUN_PLAN:-0}" = "1" ]; then
|
||||
|
|
@ -89,13 +114,29 @@ if [ "${RUN_PLAN:-0}" = "1" ]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
log "installing openssh-server: $CMD"
|
||||
sh -c "$CMD"
|
||||
|
||||
log "enabling sshd"
|
||||
systemctl enable --now ssh.service 2>/dev/null || \
|
||||
systemctl enable --now sshd.service 2>/dev/null || \
|
||||
{ log "could not enable sshd service"; exit 1; }
|
||||
log "running: $CMD"
|
||||
eval "$CMD" || {
|
||||
log "package install failed; dropping to shell"
|
||||
exec /bin/sh
|
||||
}
|
||||
|
||||
# Remove first-boot marker
|
||||
MARKER="${BANGER_FIRST_BOOT_MARKER:-/var/lib/banger/first-boot-pending}"
|
||||
rm -f "$MARKER"
|
||||
log "first-boot provisioning complete"
|
||||
|
||||
# systemd should now be installed — find and exec it
|
||||
for candidate_init in /usr/lib/systemd/systemd /lib/systemd/systemd /sbin/init; do
|
||||
if [ -x "$candidate_init" ]; then
|
||||
log "provisioning complete; starting $candidate_init"
|
||||
# Unmount our temp mounts — systemd will re-mount them properly
|
||||
umount /tmp 2>/dev/null || true
|
||||
umount /run 2>/dev/null || true
|
||||
umount /dev 2>/dev/null || true
|
||||
umount /sys 2>/dev/null || true
|
||||
umount /proc 2>/dev/null || true
|
||||
exec "$candidate_init" "$@"
|
||||
fi
|
||||
done
|
||||
|
||||
log "FATAL: init not found after install; dropping to shell"
|
||||
exec /bin/sh
|
||||
|
|
|
|||
|
|
@ -65,14 +65,14 @@ func TestFirstBootScriptDispatchesByDistro(t *testing.T) {
|
|||
osRel string
|
||||
wantRe string
|
||||
}{
|
||||
{"debian", `ID=debian` + "\n" + `ID_LIKE=""`, "apt-get install -y openssh-server"},
|
||||
{"ubuntu", `ID=ubuntu`, "apt-get install -y openssh-server"},
|
||||
{"alpine", `ID=alpine`, "apk add --no-cache openssh"},
|
||||
{"fedora", `ID=fedora`, "dnf install -y openssh-server"},
|
||||
{"debian", `ID=debian` + "\n" + `ID_LIKE=""`, "systemd-sysv openssh-server"},
|
||||
{"ubuntu", `ID=ubuntu`, "systemd-sysv openssh-server"},
|
||||
{"alpine", `ID=alpine`, "apk add"},
|
||||
{"fedora", `ID=fedora`, "dnf install -y systemd openssh-server"},
|
||||
{"arch", `ID=arch`, "pacman -Sy --noconfirm openssh"},
|
||||
{"opensuse-leap", `ID="opensuse-leap"`, "zypper --non-interactive install -y openssh"},
|
||||
{"unknown-with-debian-like", `ID=someweirddistro` + "\n" + `ID_LIKE=debian`, "apt-get install -y openssh-server"},
|
||||
{"unknown-with-rhel-like", `ID=something` + "\n" + `ID_LIKE="rhel fedora"`, "dnf install -y openssh-server"},
|
||||
{"opensuse-leap", `ID="opensuse-leap"`, "zypper --non-interactive install"},
|
||||
{"unknown-with-debian-like", `ID=someweirddistro` + "\n" + `ID_LIKE=debian`, "systemd-sysv openssh-server"},
|
||||
{"unknown-with-rhel-like", `ID=something` + "\n" + `ID_LIKE="rhel fedora"`, "dnf install -y systemd openssh-server"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -88,17 +88,21 @@ func TestFirstBootScriptContainsDistroCases(t *testing.T) {
|
|||
s := FirstBootScript()
|
||||
for _, snippet := range []string{
|
||||
"debian|ubuntu|kali|raspbian",
|
||||
"apt-get install -y openssh-server",
|
||||
"apt-get",
|
||||
"systemd-sysv",
|
||||
"openssh-server",
|
||||
"alpine)",
|
||||
"apk add --no-cache openssh",
|
||||
"apk add",
|
||||
"fedora|rhel|centos|rocky|almalinux",
|
||||
"dnf install -y openssh-server",
|
||||
"dnf install",
|
||||
"arch|archlinux|manjaro",
|
||||
"pacman -Sy --noconfirm openssh",
|
||||
"pacman -Sy",
|
||||
"opensuse*|suse",
|
||||
"zypper --non-interactive install -y openssh",
|
||||
"zypper",
|
||||
`ID_LIKE`,
|
||||
"RUN_PLAN",
|
||||
"/usr/lib/systemd/systemd",
|
||||
"mount -t proc",
|
||||
} {
|
||||
if !strings.Contains(s, snippet) {
|
||||
t.Errorf("script missing %q", snippet)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
{
|
||||
"version": 1,
|
||||
"entries": [
|
||||
{
|
||||
"name": "generic-6.12",
|
||||
"distro": "generic",
|
||||
"arch": "x86_64",
|
||||
"kernel_version": "6.12.8",
|
||||
"tarball_url": "https://kernels.thaloco.com/generic-6.12-x86_64.tar.zst",
|
||||
"tarball_sha256": "d6f9ba2a957260063241cf9d79ae538d0c349107d37f0bfccc33281d29bd0901",
|
||||
"size_bytes": 9098722,
|
||||
"description": "Generic Firecracker kernel 6.12.8 (all drivers built-in, no initrd needed)"
|
||||
},
|
||||
{
|
||||
"name": "void-6.12",
|
||||
"distro": "void",
|
||||
|
|
|
|||
96
scripts/make-generic-kernel.sh
Executable file
96
scripts/make-generic-kernel.sh
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env bash
|
||||
# make-generic-kernel.sh
|
||||
#
|
||||
# Build a minimal Firecracker-optimized vmlinux from upstream kernel.org
|
||||
# sources using the vendored Firecracker config. All essential drivers
|
||||
# (virtio_blk, virtio_net, ext4, vsock) are compiled in — no modules,
|
||||
# no initramfs needed. The result boots any OCI-pulled rootfs directly.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/make-generic-kernel.sh [--version 6.12.8]
|
||||
#
|
||||
# Output:
|
||||
# build/manual/generic-kernel/boot/vmlinux-<version>
|
||||
# build/manual/generic-kernel/metadata.json
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
log() { printf '[make-generic-kernel] %s\n' "$*" >&2; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
OUT_DIR="${BANGER_MANUAL_DIR:-$REPO_ROOT/build/manual}/generic-kernel"
|
||||
CONFIG="$REPO_ROOT/configs/firecracker-x86_64-6.1.config"
|
||||
KERNEL_VERSION="${KERNEL_VERSION:-6.12.8}"
|
||||
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
|
||||
JOBS="${JOBS:-$(nproc)}"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: scripts/make-generic-kernel.sh [--version <ver>]
|
||||
|
||||
Downloads kernel <ver> from kernel.org, applies the vendored Firecracker
|
||||
config, and builds a minimal vmlinux. Default version: $KERNEL_VERSION
|
||||
|
||||
Output: $OUT_DIR/boot/vmlinux-<ver>
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--version) KERNEL_VERSION="$2"; KERNEL_MAJOR="${KERNEL_VERSION%%.*}"; shift 2;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) log "unknown arg: $1"; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
for tool in curl tar make gcc; do
|
||||
command -v "$tool" >/dev/null 2>&1 || { log "missing required tool: $tool"; exit 1; }
|
||||
done
|
||||
[[ -f "$CONFIG" ]] || { log "config not found: $CONFIG"; exit 1; }
|
||||
|
||||
TARBALL="linux-${KERNEL_VERSION}.tar.xz"
|
||||
URL="https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/$TARBALL"
|
||||
SRC_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$SRC_DIR"' EXIT
|
||||
|
||||
log "downloading kernel $KERNEL_VERSION from $URL"
|
||||
curl -fSL --progress-bar -o "$SRC_DIR/$TARBALL" "$URL"
|
||||
|
||||
log "extracting"
|
||||
tar -xf "$SRC_DIR/$TARBALL" -C "$SRC_DIR" --strip-components=1
|
||||
|
||||
log "applying firecracker config"
|
||||
cp "$CONFIG" "$SRC_DIR/.config"
|
||||
# Adapt the 6.1 config to whatever version we're building. make olddefconfig
|
||||
# fills in any new symbols with defaults.
|
||||
make -C "$SRC_DIR" olddefconfig >/dev/null 2>&1
|
||||
|
||||
log "building vmlinux (jobs=$JOBS)"
|
||||
make -C "$SRC_DIR" -j"$JOBS" vmlinux 2>&1 | tail -5
|
||||
|
||||
VMLINUX="$SRC_DIR/vmlinux"
|
||||
if [[ ! -f "$VMLINUX" ]]; then
|
||||
log "vmlinux not found after build; check build output above"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR/boot"
|
||||
DEST="$OUT_DIR/boot/vmlinux-${KERNEL_VERSION}"
|
||||
cp "$VMLINUX" "$DEST"
|
||||
|
||||
log "verifying: $(file -b "$DEST" | head -c 80)"
|
||||
|
||||
cat > "$OUT_DIR/metadata.json" <<EOF
|
||||
{
|
||||
"kernel_path": "$DEST",
|
||||
"kernel_version": "$KERNEL_VERSION",
|
||||
"config": "firecracker-x86_64-6.1"
|
||||
}
|
||||
EOF
|
||||
|
||||
log "done: $DEST ($(du -h "$DEST" | cut -f1))"
|
||||
log "no initrd or modules needed — all drivers are built-in"
|
||||
log ""
|
||||
log "next steps:"
|
||||
log " banger kernel import generic-${KERNEL_VERSION%%.*}.${KERNEL_VERSION#*.} --from $OUT_DIR --distro generic --arch x86_64"
|
||||
Loading…
Add table
Add a link
Reference in a new issue