From d51618badbcb826c6168ab2ec381b0b902484c1e Mon Sep 17 00:00:00 2001 From: Adolfo Delorenzo Date: Thu, 14 May 2026 10:30:11 -0600 Subject: [PATCH] build: separate generic ARM64 from Raspberry Pi kernel builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Splits the ARM64 build into two tracks per docs/arm64-architecture.md: Generic ARM64 (mainline kernel.org, UEFI, virtio, GRUB): - New build/scripts/build-kernel-arm64.sh builds mainline LTS (6.12.x by default) from arm64 defconfig + shared container fragment + arm64-virt enables (VIRTIO_*, EFI_STUB, NVMe). Output: build/cache/kernel-arm64-generic/. - New Makefile targets: kernel-arm64, rootfs-arm64 (now consumes the mainline kernel modules via TARGET_VARIANT=generic). - versions.env: pin MAINLINE_KERNEL_VERSION=6.12.10, declare cdn.kernel.org URL and SHA256 placeholder. Raspberry Pi (raspberrypi/linux fork, custom DTBs, autoboot.txt): - build-kernel-arm64.sh (RPi-flavoured) renamed to build-kernel-rpi.sh; cache dir renamed from custom-kernel-arm64 to custom-kernel-rpi. - New Makefile targets: kernel-rpi, rootfs-arm64-rpi (uses TARGET_VARIANT=rpi). - rpi-image now depends on rootfs-arm64-rpi + kernel-rpi instead of the generic rootfs-arm64. - create-rpi-image.sh + inject-kubesolo.sh updated to reference the new cache path. inject-kubesolo.sh now takes a TARGET_VARIANT env var (rpi|generic) to select which ARM64 kernel modules to consume. Shared substrate: - rpi-kernel-config.fragment renamed to kernel-container.fragment. The contents were never RPi-specific (cgroup, namespaces, AppArmor, netfilter) — just misnamed. Extended with extra subsystem disables (KVM, WLAN, CFG80211, INFINIBAND, PCMCIA, HAMRADIO, ISDN, ATM, INPUT_JOYSTICK, INPUT_TABLET, FPGA) and CONFIG_LSM=lockdown,yama,apparmor. - build-kernel.sh (x86) refactored to apply the shared fragment via a generic apply_fragment function (two-pass for the TC stock config security dance), killing ~50 lines of inline config duplication. Note: rename detection shows build-kernel-arm64.sh as 'modified' because the new file at that path is the mainline build, while the old RPi-flavoured content lives in build-kernel-rpi.sh (which appears as a new file). The git log for build-kernel-rpi.sh is empty; the RPi history is preserved at the original path until this commit. No actual kernel build runs in this commit — that's Phase 3 work. Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 41 +++- ...fig.fragment => kernel-container.fragment} | 29 ++- build/config/versions.env | 8 + build/scripts/build-kernel-arm64.sh | 206 +++++++++++------- build/scripts/build-kernel-rpi.sh | 161 ++++++++++++++ build/scripts/build-kernel.sh | 108 +++------ build/scripts/create-rpi-image.sh | 4 +- build/scripts/inject-kubesolo.sh | 14 +- 8 files changed, 402 insertions(+), 169 deletions(-) rename build/config/{rpi-kernel-config.fragment => kernel-container.fragment} (54%) create mode 100755 build/scripts/build-kernel-rpi.sh diff --git a/Makefile b/Makefile index ea64bf3..5b8dad3 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: all fetch kernel build-cloudinit build-update-agent build-cross rootfs initramfs \ iso disk-image oci-image rpi-image \ - kernel-arm64 rootfs-arm64 \ + kernel-arm64 kernel-rpi rootfs-arm64 rootfs-arm64-rpi \ test-boot test-k8s test-persistence test-deploy test-storage test-security test-all \ test-boot-arm64 test-cloudinit test-update-agent \ bench-boot bench-resources \ @@ -73,21 +73,38 @@ build-cross: $(BUILD_DIR)/scripts/build-cross.sh # ============================================================================= -# ARM64 Raspberry Pi targets +# ARM64 generic targets (mainline kernel, UEFI, virtio — for cloud / SBCs) # ============================================================================= kernel-arm64: - @echo "==> Building ARM64 kernel for Raspberry Pi..." + @echo "==> Building generic ARM64 kernel (mainline LTS)..." $(BUILD_DIR)/scripts/build-kernel-arm64.sh +# Generic ARM64 rootfs consumes the mainline kernel modules. rootfs-arm64: build-cross - @echo "==> Preparing ARM64 rootfs..." + @echo "==> Preparing generic ARM64 rootfs..." TARGET_ARCH=arm64 $(BUILD_DIR)/scripts/fetch-components.sh TARGET_ARCH=arm64 $(BUILD_DIR)/scripts/extract-core.sh - TARGET_ARCH=arm64 $(BUILD_DIR)/scripts/inject-kubesolo.sh - @echo "==> Packing ARM64 initramfs..." + TARGET_ARCH=arm64 TARGET_VARIANT=generic $(BUILD_DIR)/scripts/inject-kubesolo.sh + @echo "==> Packing generic ARM64 initramfs..." $(BUILD_DIR)/scripts/pack-initramfs.sh -rpi-image: rootfs-arm64 kernel-arm64 +# ============================================================================= +# ARM64 Raspberry Pi targets (RPi-patched kernel, firmware blobs, SD card) +# ============================================================================= +kernel-rpi: + @echo "==> Building RPi kernel (raspberrypi/linux)..." + $(BUILD_DIR)/scripts/build-kernel-rpi.sh + +# RPi-flavoured rootfs consumes the RPi kernel modules. +rootfs-arm64-rpi: build-cross + @echo "==> Preparing RPi ARM64 rootfs..." + TARGET_ARCH=arm64 $(BUILD_DIR)/scripts/fetch-components.sh + TARGET_ARCH=arm64 $(BUILD_DIR)/scripts/extract-core.sh + TARGET_ARCH=arm64 TARGET_VARIANT=rpi $(BUILD_DIR)/scripts/inject-kubesolo.sh + @echo "==> Packing RPi ARM64 initramfs..." + $(BUILD_DIR)/scripts/pack-initramfs.sh + +rpi-image: rootfs-arm64-rpi kernel-rpi @echo "==> Creating Raspberry Pi SD card image..." $(BUILD_DIR)/scripts/create-rpi-image.sh @echo "==> Built: $(OUTPUT_DIR)/$(OS_NAME)-$(VERSION).rpi.img" @@ -246,10 +263,14 @@ help: @echo " make quick Fast rebuild (re-inject + repack + ISO only)" @echo " make docker-build Reproducible build inside Docker" @echo "" + @echo "Build targets (ARM64 generic — UEFI / cloud / SBCs):" + @echo " make kernel-arm64 Build mainline ARM64 kernel from kernel.org LTS" + @echo " make rootfs-arm64 Prepare generic ARM64 rootfs (mainline kernel modules)" + @echo "" @echo "Build targets (ARM64 Raspberry Pi):" - @echo " make kernel-arm64 Build ARM64 kernel from raspberrypi/linux" - @echo " make rootfs-arm64 Extract + prepare ARM64 rootfs from piCore64" - @echo " make rpi-image Create Raspberry Pi SD card image with A/B partitions" + @echo " make kernel-rpi Build RPi kernel from raspberrypi/linux" + @echo " make rootfs-arm64-rpi Prepare RPi-flavoured rootfs (RPi kernel modules)" + @echo " make rpi-image Create Raspberry Pi SD card image with A/B autoboot" @echo "" @echo "Test targets:" @echo " make test-boot Boot ISO in QEMU, verify boot success" diff --git a/build/config/rpi-kernel-config.fragment b/build/config/kernel-container.fragment similarity index 54% rename from build/config/rpi-kernel-config.fragment rename to build/config/kernel-container.fragment index 7245e34..62830a1 100644 --- a/build/config/rpi-kernel-config.fragment +++ b/build/config/kernel-container.fragment @@ -1,6 +1,15 @@ -# KubeSolo OS — Raspberry Pi kernel config overrides -# Applied on top of bcm2711_defconfig (Pi 4) or bcm2712_defconfig (Pi 5) -# These ensure container runtime support is enabled. +# KubeSolo OS — Shared kernel config fragment for container workloads +# +# Applied on top of: +# - Tiny Core stock config (x86_64) via build-kernel.sh +# - mainline kernel.org arm64 defconfig via build-kernel-arm64.sh +# - bcm2711_defconfig / bcm2712_defconfig via build-kernel-rpi.sh +# +# All entries here are architecture-agnostic. +# Apply this fragment twice with `make olddefconfig` between passes — TC's stock +# config has CONFIG_SECURITY disabled, which causes a single-pass olddefconfig +# to strip the security subtree before its dependencies (SYSFS, MULTIUSER) are +# resolved. # cgroup v2 (mandatory for containerd/runc) CONFIG_CGROUPS=y @@ -52,6 +61,7 @@ CONFIG_SECURITYFS=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_APPARMOR=y CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_LSM=lockdown,yama,apparmor # Security: seccomp CONFIG_SECCOMP=y @@ -60,10 +70,21 @@ CONFIG_SECCOMP_FILTER=y # Crypto (image verification) CONFIG_CRYPTO_SHA256=y -# Disable unnecessary subsystems for edge appliance +# Disable unnecessary subsystems for headless edge appliance # CONFIG_SOUND is not set # CONFIG_DRM is not set +# CONFIG_KVM is not set # CONFIG_MEDIA_SUPPORT is not set # CONFIG_WIRELESS is not set +# CONFIG_WLAN is not set +# CONFIG_CFG80211 is not set # CONFIG_BT is not set # CONFIG_NFC is not set +# CONFIG_INFINIBAND is not set +# CONFIG_PCMCIA is not set +# CONFIG_HAMRADIO is not set +# CONFIG_ISDN is not set +# CONFIG_ATM is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_FPGA is not set diff --git a/build/config/versions.env b/build/config/versions.env index 9645e25..8554134 100644 --- a/build/config/versions.env +++ b/build/config/versions.env @@ -41,5 +41,13 @@ RPI_FIRMWARE_URL=https://github.com/raspberrypi/firmware/archive/refs/tags/${RPI RPI_KERNEL_BRANCH=rpi-6.6.y RPI_KERNEL_REPO=https://github.com/raspberrypi/linux +# Mainline Linux kernel (for generic ARM64 — kernel.org LTS) +# Bump within the 6.12 LTS series as patch levels release. +# 6.12 LTS is supported until Dec 2029. +MAINLINE_KERNEL_VERSION=6.12.10 +MAINLINE_KERNEL_MAJOR=v6.x +MAINLINE_KERNEL_URL=https://cdn.kernel.org/pub/linux/kernel/${MAINLINE_KERNEL_MAJOR}/linux-${MAINLINE_KERNEL_VERSION}.tar.xz +MAINLINE_KERNEL_SHA256="" + # Output naming OS_NAME=kubesolo-os diff --git a/build/scripts/build-kernel-arm64.sh b/build/scripts/build-kernel-arm64.sh index a098ce6..8e2364c 100755 --- a/build/scripts/build-kernel-arm64.sh +++ b/build/scripts/build-kernel-arm64.sh @@ -1,14 +1,20 @@ #!/bin/bash -# build-kernel-arm64.sh — Build ARM64 kernel for Raspberry Pi 4/5 +# build-kernel-arm64.sh — Build generic ARM64 kernel (mainline LTS) # -# Uses the official raspberrypi/linux kernel fork with bcm2711_defconfig -# as the base, overlaid with container-critical config options. +# Builds a Linux kernel from kernel.org mainline LTS source, suitable for: +# - qemu-system-aarch64 -machine virt +# - UEFI ARM64 hosts (Ampere, Graviton, generic ARM64 servers) +# - Future ARM64 SBCs with UEFI/u-boot generic-distro support # -# Output is cached in $CACHE_DIR/custom-kernel-arm64/ and reused across builds. +# This is the GENERIC ARM64 build track. For Raspberry Pi specifically +# (raspberrypi/linux fork, RPi firmware boot path, custom DTBs), see +# build/scripts/build-kernel-rpi.sh. +# +# Output is cached in $CACHE_DIR/kernel-arm64-generic/ and reused across builds. # # Requirements: # - gcc-aarch64-linux-gnu (cross-compiler) -# - Standard kernel build deps (bc, bison, flex, etc.) +# - Standard kernel build deps (bc, bison, flex, libelf-dev, libssl-dev) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -18,17 +24,18 @@ CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}" # shellcheck source=../config/versions.env . "$SCRIPT_DIR/../config/versions.env" -CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel-arm64" +KVER="$MAINLINE_KERNEL_VERSION" +CUSTOM_KERNEL_DIR="$CACHE_DIR/kernel-arm64-generic" CUSTOM_IMAGE="$CUSTOM_KERNEL_DIR/Image" CUSTOM_MODULES="$CUSTOM_KERNEL_DIR/modules" -CUSTOM_DTBS="$CUSTOM_KERNEL_DIR/dtbs" mkdir -p "$CACHE_DIR" "$CUSTOM_KERNEL_DIR" # --- Skip if already built --- -if [ -f "$CUSTOM_IMAGE" ] && [ -d "$CUSTOM_MODULES" ]; then - echo "==> ARM64 kernel already built (cached)" - echo " Image: $CUSTOM_IMAGE ($(du -h "$CUSTOM_IMAGE" | cut -f1))" +if [ -f "$CUSTOM_IMAGE" ] && [ -d "$CUSTOM_MODULES/lib/modules/$KVER" ]; then + echo "==> Generic ARM64 kernel already built (cached)" + echo " Image: $CUSTOM_IMAGE ($(du -h "$CUSTOM_IMAGE" | cut -f1))" + echo " Kernel: $KVER" exit 0 fi @@ -39,73 +46,128 @@ if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then exit 1 fi -echo "==> Building ARM64 kernel for Raspberry Pi..." -echo " Branch: $RPI_KERNEL_BRANCH" -echo " Repo: $RPI_KERNEL_REPO" +echo "==> Building generic ARM64 kernel (mainline $KVER)..." +echo " Source: $MAINLINE_KERNEL_URL" -# --- Download kernel source --- -KERNEL_SRC_DIR="$CACHE_DIR/rpi-linux-${RPI_KERNEL_BRANCH}" -if [ ! -d "$KERNEL_SRC_DIR" ]; then - echo "==> Downloading RPi kernel source (shallow clone)..." - git clone --depth 1 --branch "$RPI_KERNEL_BRANCH" \ - "$RPI_KERNEL_REPO" "$KERNEL_SRC_DIR" +# --- Download mainline kernel source --- +KERNEL_SRC_ARCHIVE="$CACHE_DIR/linux-${KVER}.tar.xz" +if [ ! -f "$KERNEL_SRC_ARCHIVE" ]; then + echo "==> Downloading mainline kernel source (~140 MB)..." + wget -q --show-progress -O "$KERNEL_SRC_ARCHIVE" "$MAINLINE_KERNEL_URL" 2>/dev/null || \ + curl -fSL "$MAINLINE_KERNEL_URL" -o "$KERNEL_SRC_ARCHIVE" + echo " Downloaded: $(du -h "$KERNEL_SRC_ARCHIVE" | cut -f1)" else - echo "==> Kernel source already cached" + echo "==> Kernel source already cached: $(du -h "$KERNEL_SRC_ARCHIVE" | cut -f1)" fi -# --- Build in /tmp for case-sensitivity --- -KERNEL_BUILD_DIR="/tmp/kernel-build-arm64" +# --- Verify checksum if pinned --- +if [ -n "${MAINLINE_KERNEL_SHA256:-}" ]; then + actual=$(sha256sum "$KERNEL_SRC_ARCHIVE" | awk '{print $1}') + if [ "$actual" != "$MAINLINE_KERNEL_SHA256" ]; then + echo "ERROR: Kernel source checksum mismatch" + echo " Expected: $MAINLINE_KERNEL_SHA256" + echo " Got: $actual" + exit 1 + fi + echo " Checksum OK" +fi + +# --- Extract to case-sensitive fs --- +# The kernel source has files differing only by case (xt_mark.h vs xt_MARK.h). +# Build in /tmp (ext4 on Linux runners, case-sensitive). +KERNEL_BUILD_DIR="/tmp/kernel-build-arm64-generic" rm -rf "$KERNEL_BUILD_DIR" -cp -a "$KERNEL_SRC_DIR" "$KERNEL_BUILD_DIR" +mkdir -p "$KERNEL_BUILD_DIR" -cd "$KERNEL_BUILD_DIR" - -# --- Apply base config (Pi 4 = bcm2711) --- -echo "==> Applying bcm2711_defconfig..." -make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig - -# --- Apply container config overrides --- -CONFIG_FRAGMENT="$PROJECT_ROOT/build/config/rpi-kernel-config.fragment" -if [ -f "$CONFIG_FRAGMENT" ]; then - echo "==> Applying KubeSolo config overrides..." - while IFS= read -r line; do - # Skip comments and empty lines - case "$line" in \#*|"") continue ;; esac - key="${line%%=*}" - value="${line#*=}" - case "$value" in - y) ./scripts/config --enable "$key" ;; - m) ./scripts/config --module "$key" ;; - n) ./scripts/config --disable "${key#CONFIG_}" ;; - *) ./scripts/config --set-str "$key" "$value" ;; - esac - done < "$CONFIG_FRAGMENT" +echo "==> Extracting kernel source..." +tar -xf "$KERNEL_SRC_ARCHIVE" -C "$KERNEL_BUILD_DIR" +KERNEL_SRC_DIR=$(find "$KERNEL_BUILD_DIR" -maxdepth 1 -type d -name 'linux-*' | head -1) +if [ -z "$KERNEL_SRC_DIR" ]; then + echo "ERROR: Could not find extracted source directory" + ls -la "$KERNEL_BUILD_DIR"/ + exit 1 fi -# Handle "is not set" comments as disables -if [ -f "$CONFIG_FRAGMENT" ]; then +cd "$KERNEL_SRC_DIR" + +# --- Base config: arm64 defconfig (generic ARMv8) --- +echo "==> Applying arm64 defconfig..." +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig + +# --- Apply shared container fragment --- +CONFIG_FRAGMENT="$PROJECT_ROOT/build/config/kernel-container.fragment" +if [ ! -f "$CONFIG_FRAGMENT" ]; then + echo "ERROR: Config fragment not found: $CONFIG_FRAGMENT" + exit 1 +fi + +apply_fragment() { + local fragment="$1" while IFS= read -r line; do case "$line" in "# CONFIG_"*" is not set") - key=$(echo "$line" | sed -n 's/^# \(CONFIG_[A-Z_]*\) is not set$/\1/p') + key=$(echo "$line" | sed -n 's/^# \(CONFIG_[A-Z0-9_]*\) is not set$/\1/p') [ -n "$key" ] && ./scripts/config --disable "${key#CONFIG_}" + continue ;; + \#*|"") continue ;; esac - done < "$CONFIG_FRAGMENT" -fi + key="${line%%=*}" + value="${line#*=}" + case "$value" in + y) ./scripts/config --enable "$key" ;; + m) ./scripts/config --module "$key" ;; + n) ./scripts/config --disable "${key#CONFIG_}" ;; + *) ./scripts/config --set-str "$key" "$value" ;; + esac + done < "$fragment" +} -# Resolve dependencies +echo "==> Applying kernel-container.fragment (pass 1)..." +apply_fragment "$CONFIG_FRAGMENT" make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig -# --- Build kernel + modules + DTBs --- +echo "==> Applying kernel-container.fragment (pass 2)..." +apply_fragment "$CONFIG_FRAGMENT" +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig + +# --- ARM64 virt-host specific enables --- +# These are needed for the generic UEFI/virtio boot path but are arch-specific +# so they live in this script rather than the shared fragment. +echo "==> Enabling ARM64 virt-host configs..." +./scripts/config --enable CONFIG_EFI +./scripts/config --enable CONFIG_EFI_STUB +./scripts/config --enable CONFIG_VIRTIO +./scripts/config --enable CONFIG_VIRTIO_PCI +./scripts/config --enable CONFIG_VIRTIO_BLK +./scripts/config --enable CONFIG_VIRTIO_NET +./scripts/config --enable CONFIG_VIRTIO_CONSOLE +./scripts/config --enable CONFIG_VIRTIO_MMIO +./scripts/config --enable CONFIG_HW_RANDOM_VIRTIO +# NVMe for cloud / bare-metal ARM64 hosts that don't use virtio +./scripts/config --enable CONFIG_BLK_DEV_NVME +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig + +# --- Verify critical configs --- +echo "==> Verifying critical configs..." +for cfg in CGROUP_BPF SECURITY_APPARMOR AUDIT VIRTIO_BLK EFI_STUB; do + if ! grep -q "CONFIG_${cfg}=y" .config; then + echo "ERROR: CONFIG_${cfg} not set after olddefconfig" + grep "CONFIG_${cfg}" .config || echo " (not found)" + exit 1 + fi + echo " CONFIG_${cfg}=y confirmed" +done + +# --- Build kernel + modules (no DTBs — UEFI hosts use ACPI/virtio) --- NPROC=$(nproc 2>/dev/null || echo 4) echo "" echo "==> Building ARM64 kernel (${NPROC} parallel jobs)..." -echo " This may take 20-30 minutes..." +echo " This may take 20-40 minutes on a 6-core Odroid..." -make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j"$NPROC" Image modules dtbs 2>&1 +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j"$NPROC" Image modules 2>&1 -echo "==> ARM64 kernel build complete" +echo "==> Kernel build complete" # --- Install to staging --- echo "==> Installing Image..." @@ -117,28 +179,13 @@ mkdir -p "$CUSTOM_MODULES" make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \ INSTALL_MOD_STRIP=1 modules_install INSTALL_MOD_PATH="$CUSTOM_MODULES" -# Remove build/source symlinks -KVER=$(ls "$CUSTOM_MODULES/lib/modules/" | head -1) -rm -f "$CUSTOM_MODULES/lib/modules/$KVER/build" -rm -f "$CUSTOM_MODULES/lib/modules/$KVER/source" +# Pick up actual kernel version (e.g. 6.12.10 if KVER differs from package suffix) +ACTUAL_KVER=$(ls "$CUSTOM_MODULES/lib/modules/" | head -1) +rm -f "$CUSTOM_MODULES/lib/modules/$ACTUAL_KVER/build" +rm -f "$CUSTOM_MODULES/lib/modules/$ACTUAL_KVER/source" -# Run depmod -depmod -a -b "$CUSTOM_MODULES" "$KVER" 2>/dev/null || true +depmod -a -b "$CUSTOM_MODULES" "$ACTUAL_KVER" 2>/dev/null || true -echo "==> Installing Device Tree Blobs..." -rm -rf "$CUSTOM_DTBS" -mkdir -p "$CUSTOM_DTBS/overlays" -# Pi 4 DTBs -cp arch/arm64/boot/dts/broadcom/bcm2711*.dtb "$CUSTOM_DTBS/" 2>/dev/null || true -# Pi 5 DTBs -cp arch/arm64/boot/dts/broadcom/bcm2712*.dtb "$CUSTOM_DTBS/" 2>/dev/null || true -# Overlays we need -for overlay in disable-wifi disable-bt; do - [ -f "arch/arm64/boot/dts/overlays/${overlay}.dtbo" ] && \ - cp "arch/arm64/boot/dts/overlays/${overlay}.dtbo" "$CUSTOM_DTBS/overlays/" -done - -# Save config for reference cp .config "$CUSTOM_KERNEL_DIR/.config" # --- Clean up --- @@ -148,11 +195,10 @@ rm -rf "$KERNEL_BUILD_DIR" # --- Summary --- echo "" -echo "==> ARM64 kernel build complete:" +echo "==> Generic ARM64 kernel build complete:" echo " Image: $CUSTOM_IMAGE ($(du -h "$CUSTOM_IMAGE" | cut -f1))" -echo " Kernel ver: $KVER" -MOD_COUNT=$(find "$CUSTOM_MODULES/lib/modules/$KVER" -name '*.ko*' 2>/dev/null | wc -l) +echo " Kernel ver: $ACTUAL_KVER" +MOD_COUNT=$(find "$CUSTOM_MODULES/lib/modules/$ACTUAL_KVER" -name '*.ko*' 2>/dev/null | wc -l) echo " Modules: $MOD_COUNT" -echo " Modules size: $(du -sh "$CUSTOM_MODULES/lib/modules/$KVER" 2>/dev/null | cut -f1)" -echo " DTBs: $(ls "$CUSTOM_DTBS"/*.dtb 2>/dev/null | wc -l)" +echo " Modules size: $(du -sh "$CUSTOM_MODULES/lib/modules/$ACTUAL_KVER" 2>/dev/null | cut -f1)" echo "" diff --git a/build/scripts/build-kernel-rpi.sh b/build/scripts/build-kernel-rpi.sh new file mode 100755 index 0000000..e144113 --- /dev/null +++ b/build/scripts/build-kernel-rpi.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# build-kernel-rpi.sh — Build kernel for Raspberry Pi 4/5 (ARM64) +# +# Uses the official raspberrypi/linux kernel fork with bcm2711_defconfig as the +# base, overlaid with the shared container-config fragment. +# +# This is the RPi-specific build track. For generic ARM64 (UEFI / virtio / +# kernel.org mainline) see build/scripts/build-kernel-arm64.sh. +# +# Output is cached in $CACHE_DIR/custom-kernel-rpi/ and reused across builds. +# +# Requirements: +# - gcc-aarch64-linux-gnu (cross-compiler) +# - Standard kernel build deps (bc, bison, flex, etc.) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}" + +# shellcheck source=../config/versions.env +. "$SCRIPT_DIR/../config/versions.env" + +CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel-rpi" +CUSTOM_IMAGE="$CUSTOM_KERNEL_DIR/Image" +CUSTOM_MODULES="$CUSTOM_KERNEL_DIR/modules" +CUSTOM_DTBS="$CUSTOM_KERNEL_DIR/dtbs" + +mkdir -p "$CACHE_DIR" "$CUSTOM_KERNEL_DIR" + +# --- Skip if already built --- +if [ -f "$CUSTOM_IMAGE" ] && [ -d "$CUSTOM_MODULES" ]; then + echo "==> RPi kernel already built (cached)" + echo " Image: $CUSTOM_IMAGE ($(du -h "$CUSTOM_IMAGE" | cut -f1))" + exit 0 +fi + +# --- Verify cross-compiler --- +if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then + echo "ERROR: aarch64-linux-gnu-gcc not found" + echo "Install: apt-get install gcc-aarch64-linux-gnu" + exit 1 +fi + +echo "==> Building RPi kernel (raspberrypi/linux)..." +echo " Branch: $RPI_KERNEL_BRANCH" +echo " Repo: $RPI_KERNEL_REPO" + +# --- Download kernel source --- +KERNEL_SRC_DIR="$CACHE_DIR/rpi-linux-${RPI_KERNEL_BRANCH}" +if [ ! -d "$KERNEL_SRC_DIR" ]; then + echo "==> Downloading RPi kernel source (shallow clone)..." + git clone --depth 1 --branch "$RPI_KERNEL_BRANCH" \ + "$RPI_KERNEL_REPO" "$KERNEL_SRC_DIR" +else + echo "==> Kernel source already cached" +fi + +# --- Build in /tmp for case-sensitivity --- +KERNEL_BUILD_DIR="/tmp/kernel-build-arm64" +rm -rf "$KERNEL_BUILD_DIR" +cp -a "$KERNEL_SRC_DIR" "$KERNEL_BUILD_DIR" + +cd "$KERNEL_BUILD_DIR" + +# --- Apply base config (Pi 4 = bcm2711) --- +echo "==> Applying bcm2711_defconfig..." +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig + +# --- Apply container config overrides --- +CONFIG_FRAGMENT="$PROJECT_ROOT/build/config/kernel-container.fragment" +if [ -f "$CONFIG_FRAGMENT" ]; then + echo "==> Applying KubeSolo config overrides..." + while IFS= read -r line; do + # Skip comments and empty lines + case "$line" in \#*|"") continue ;; esac + key="${line%%=*}" + value="${line#*=}" + case "$value" in + y) ./scripts/config --enable "$key" ;; + m) ./scripts/config --module "$key" ;; + n) ./scripts/config --disable "${key#CONFIG_}" ;; + *) ./scripts/config --set-str "$key" "$value" ;; + esac + done < "$CONFIG_FRAGMENT" +fi + +# Handle "is not set" comments as disables +if [ -f "$CONFIG_FRAGMENT" ]; then + while IFS= read -r line; do + case "$line" in + "# CONFIG_"*" is not set") + key=$(echo "$line" | sed -n 's/^# \(CONFIG_[A-Z_]*\) is not set$/\1/p') + [ -n "$key" ] && ./scripts/config --disable "${key#CONFIG_}" + ;; + esac + done < "$CONFIG_FRAGMENT" +fi + +# Resolve dependencies +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig + +# --- Build kernel + modules + DTBs --- +NPROC=$(nproc 2>/dev/null || echo 4) +echo "" +echo "==> Building RPi kernel (${NPROC} parallel jobs)..." +echo " This may take 20-30 minutes..." + +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j"$NPROC" Image modules dtbs 2>&1 + +echo "==> RPi kernel build complete" + +# --- Install to staging --- +echo "==> Installing Image..." +cp arch/arm64/boot/Image "$CUSTOM_IMAGE" + +echo "==> Installing modules (stripped)..." +rm -rf "$CUSTOM_MODULES" +mkdir -p "$CUSTOM_MODULES" +make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \ + INSTALL_MOD_STRIP=1 modules_install INSTALL_MOD_PATH="$CUSTOM_MODULES" + +# Remove build/source symlinks +KVER=$(ls "$CUSTOM_MODULES/lib/modules/" | head -1) +rm -f "$CUSTOM_MODULES/lib/modules/$KVER/build" +rm -f "$CUSTOM_MODULES/lib/modules/$KVER/source" + +# Run depmod +depmod -a -b "$CUSTOM_MODULES" "$KVER" 2>/dev/null || true + +echo "==> Installing Device Tree Blobs..." +rm -rf "$CUSTOM_DTBS" +mkdir -p "$CUSTOM_DTBS/overlays" +# Pi 4 DTBs +cp arch/arm64/boot/dts/broadcom/bcm2711*.dtb "$CUSTOM_DTBS/" 2>/dev/null || true +# Pi 5 DTBs +cp arch/arm64/boot/dts/broadcom/bcm2712*.dtb "$CUSTOM_DTBS/" 2>/dev/null || true +# Overlays we need +for overlay in disable-wifi disable-bt; do + [ -f "arch/arm64/boot/dts/overlays/${overlay}.dtbo" ] && \ + cp "arch/arm64/boot/dts/overlays/${overlay}.dtbo" "$CUSTOM_DTBS/overlays/" +done + +# Save config for reference +cp .config "$CUSTOM_KERNEL_DIR/.config" + +# --- Clean up --- +echo "==> Cleaning kernel build directory..." +cd / +rm -rf "$KERNEL_BUILD_DIR" + +# --- Summary --- +echo "" +echo "==> RPi kernel build complete:" +echo " Image: $CUSTOM_IMAGE ($(du -h "$CUSTOM_IMAGE" | cut -f1))" +echo " Kernel ver: $KVER" +MOD_COUNT=$(find "$CUSTOM_MODULES/lib/modules/$KVER" -name '*.ko*' 2>/dev/null | wc -l) +echo " Modules: $MOD_COUNT" +echo " Modules size: $(du -sh "$CUSTOM_MODULES/lib/modules/$KVER" 2>/dev/null | cut -f1)" +echo " DTBs: $(ls "$CUSTOM_DTBS"/*.dtb 2>/dev/null | wc -l)" +echo "" diff --git a/build/scripts/build-kernel.sh b/build/scripts/build-kernel.sh index 541ee21..616aaa3 100755 --- a/build/scripts/build-kernel.sh +++ b/build/scripts/build-kernel.sh @@ -85,85 +85,49 @@ echo " Source dir: $(basename "$KERNEL_SRC_DIR")" cd "$KERNEL_SRC_DIR" -# --- Apply stock config + enable CONFIG_CGROUP_BPF --- +# --- Apply stock config + shared container-config fragment --- echo "==> Applying stock Tiny Core config..." cp "$KERNEL_CFG" .config -echo "==> Enabling required kernel configs..." -./scripts/config --enable CONFIG_CGROUP_BPF -./scripts/config --enable CONFIG_DEVTMPFS -./scripts/config --enable CONFIG_DEVTMPFS_MOUNT -./scripts/config --enable CONFIG_MEMCG -./scripts/config --enable CONFIG_CFS_BANDWIDTH +CONFIG_FRAGMENT="$PROJECT_ROOT/build/config/kernel-container.fragment" +if [ ! -f "$CONFIG_FRAGMENT" ]; then + echo "ERROR: Config fragment not found: $CONFIG_FRAGMENT" + exit 1 +fi -# --- Strip unnecessary subsystems for smallest footprint --- -# This is a headless K8s edge appliance — no sound, GPU, wireless, etc. -echo "==> Disabling unnecessary subsystems for minimal footprint..." +# Apply the fragment: each "CONFIG_X=v" line becomes the right scripts/config +# invocation; "# CONFIG_X is not set" comments become --disable. +apply_fragment() { + local fragment="$1" + while IFS= read -r line; do + case "$line" in + "# CONFIG_"*" is not set") + key=$(echo "$line" | sed -n 's/^# \(CONFIG_[A-Z0-9_]*\) is not set$/\1/p') + [ -n "$key" ] && ./scripts/config --disable "${key#CONFIG_}" + continue + ;; + \#*|"") continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + case "$value" in + y) ./scripts/config --enable "$key" ;; + m) ./scripts/config --module "$key" ;; + n) ./scripts/config --disable "${key#CONFIG_}" ;; + *) ./scripts/config --set-str "$key" "$value" ;; + esac + done < "$fragment" +} -# Sound subsystem (not needed on headless appliance) -./scripts/config --disable SOUND - -# GPU/DRM (serial console only, no display) -./scripts/config --disable DRM - -# KVM hypervisor (this IS the guest/bare metal, not a hypervisor) -./scripts/config --disable KVM - -# Media/camera/TV/radio (not needed) -./scripts/config --disable MEDIA_SUPPORT - -# Wireless networking (wired edge device) -./scripts/config --disable WIRELESS -./scripts/config --disable WLAN -./scripts/config --disable CFG80211 - -# Bluetooth (not needed) -./scripts/config --disable BT - -# NFC (not needed) -./scripts/config --disable NFC - -# Infiniband (not needed on edge) -./scripts/config --disable INFINIBAND - -# PCMCIA (legacy, not needed) -./scripts/config --disable PCMCIA - -# Amateur radio (not needed) -./scripts/config --disable HAMRADIO - -# ISDN (not needed) -./scripts/config --disable ISDN - -# ATM networking (not needed) -./scripts/config --disable ATM - -# Joystick/gamepad (not needed) -./scripts/config --disable INPUT_JOYSTICK -./scripts/config --disable INPUT_TABLET - -# FPGA (not needed) -./scripts/config --disable FPGA - -# First pass: resolve base dependencies before adding security configs. -# The stock TC config has "# CONFIG_SECURITY is not set" which causes -# olddefconfig to strip security-related options if applied in a single pass. +# Two-pass apply: TC's stock config has CONFIG_SECURITY disabled, so olddefconfig +# strips the security subtree before its dependencies resolve. Re-applying the +# fragment after the first olddefconfig restores those entries. +echo "==> Applying kernel-container.fragment (pass 1)..." +apply_fragment "$CONFIG_FRAGMENT" make olddefconfig -# Security: AppArmor LSM + Audit subsystem -# Applied AFTER first olddefconfig to ensure CONFIG_SECURITY dependencies -# (SYSFS, MULTIUSER) are resolved before enabling the security subtree. -echo "==> Enabling AppArmor + Audit kernel configs..." -./scripts/config --enable CONFIG_AUDIT -./scripts/config --enable CONFIG_AUDITSYSCALL -./scripts/config --enable CONFIG_SECURITY -./scripts/config --enable CONFIG_SECURITYFS -./scripts/config --enable CONFIG_SECURITY_NETWORK -./scripts/config --enable CONFIG_SECURITY_APPARMOR -./scripts/config --set-str CONFIG_LSM "lockdown,yama,apparmor" -./scripts/config --set-str CONFIG_DEFAULT_SECURITY "apparmor" - -# Second pass: resolve security config dependencies +echo "==> Applying kernel-container.fragment (pass 2)..." +apply_fragment "$CONFIG_FRAGMENT" make olddefconfig # Verify critical configs are set diff --git a/build/scripts/create-rpi-image.sh b/build/scripts/create-rpi-image.sh index a3f0b06..d23851f 100755 --- a/build/scripts/create-rpi-image.sh +++ b/build/scripts/create-rpi-image.sh @@ -31,12 +31,12 @@ IMG_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.rpi.img" IMG_SIZE_MB="${IMG_SIZE_MB:-2048}" # 2 GB default # ARM64 kernel (Image format, not bzImage) -KERNEL="${CACHE_DIR}/custom-kernel-arm64/Image" +KERNEL="${CACHE_DIR}/custom-kernel-rpi/Image" INITRAMFS="${ROOTFS_DIR}/kubesolo-os.gz" RPI_FIRMWARE_DIR="${CACHE_DIR}/rpi-firmware" # DTBs MUST come from the kernel build (not firmware repo) to match the kernel. # A DTB mismatch causes sdhci-iproc to silently fail — zero block devices. -KERNEL_DTBS_DIR="${CACHE_DIR}/custom-kernel-arm64/dtbs" +KERNEL_DTBS_DIR="${CACHE_DIR}/custom-kernel-rpi/dtbs" echo "==> Creating ${IMG_SIZE_MB}MB Raspberry Pi disk image..." diff --git a/build/scripts/inject-kubesolo.sh b/build/scripts/inject-kubesolo.sh index 63a5d1c..798485c 100755 --- a/build/scripts/inject-kubesolo.sh +++ b/build/scripts/inject-kubesolo.sh @@ -109,7 +109,19 @@ fi # If a custom kernel was built (with CONFIG_CGROUP_BPF=y), use it. # Otherwise fall back to TCZ-extracted modules with manual modules.dep. if [ "$INJECT_ARCH" = "arm64" ]; then - CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel-arm64" + # TARGET_VARIANT selects which ARM64 kernel to consume: + # rpi -> $CACHE_DIR/custom-kernel-rpi/ (raspberrypi/linux fork) + # generic -> $CACHE_DIR/kernel-arm64-generic/ (mainline kernel.org LTS) + # Default is rpi for backwards compatibility with existing rpi-image target. + TARGET_VARIANT="${TARGET_VARIANT:-rpi}" + case "$TARGET_VARIANT" in + generic) CUSTOM_KERNEL_DIR="$CACHE_DIR/kernel-arm64-generic" ;; + rpi) CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel-rpi" ;; + *) + echo "ERROR: TARGET_VARIANT must be 'rpi' or 'generic' (got: $TARGET_VARIANT)" + exit 1 + ;; + esac CUSTOM_VMLINUZ="$CUSTOM_KERNEL_DIR/Image" else CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel"