Add netboot infrastructure: custom initramfs hooks, build scripts, iPXE configuration

This commit is contained in:
2026-01-30 23:09:43 +01:00
parent 4790e69113
commit 0c4a99605a
6 changed files with 294 additions and 0 deletions

38
initramfs/hooks/netboot Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh
#
# Add useful binaries to initrd for netboot HTTP root mounting
#
if [ "$1" = "prereqs" ]; then
echo "udev"
exit
fi
. /usr/share/initramfs-tools/hook-functions
# Essential utilities for netboot - copy_exec handles dependencies automatically
copy_exec "/usr/bin/wget"
copy_exec "/usr/bin/curl"
copy_exec "/usr/bin/unsquashfs"
copy_exec "/usr/sbin/switch_root"
# Useful tools for debugging
copy_exec "/usr/bin/awk"
copy_exec "/usr/bin/bash"
copy_exec "/usr/bin/chmod"
copy_exec "/usr/bin/file"
copy_exec "/usr/bin/free"
copy_exec "/usr/bin/grep"
copy_exec "/usr/bin/gunzip"
copy_exec "/usr/bin/gzip"
copy_exec "/usr/bin/less"
copy_exec "/usr/bin/lsblk"
copy_exec "/usr/bin/mount"
copy_exec "/usr/bin/readlink"
copy_exec "/usr/bin/sed"
copy_exec "/usr/bin/timeout"
copy_exec "/usr/bin/touch"
copy_exec "/usr/bin/vi"
# Note: copy_exec automatically handles all shared library dependencies via ldd()
# No need to manually copy libc or other libraries - copy_exec handles this

70
initramfs/initramfs.conf Normal file
View File

@@ -0,0 +1,70 @@
#
# initramfs.conf
# Configuration file for mkinitramfs(8). See initramfs.conf(5).
#
#
# MODULES: [ most | netboot | dep | list ]
#
# most - Add most filesystem and all harddrive drivers.
#
# dep - Try and guess which modules to load.
#
# netboot - Add the base modules, network modules, but skip block devices.
#
# list - Only include modules from the 'additional modules' list
#
MODULES=most
#
# BUSYBOX: [ y | n | auto ]
#
# Use busybox shell and utilities. If set to n, klibc utilities will be used.
# If set to auto (or unset), busybox will be used if installed and klibc will
# be used otherwise.
#
BUSYBOX=auto
#
# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz | zstd ]
#
COMPRESS=gzip
#
# DEVICE: ...
#
# Specify a specific network interface, like eth0
# Overridden by optional ip= or BOOTIF= bootarg
#
DEVICE=
#
# NFSROOT: [ auto | HOST:MOUNT ]
#
NFSROOT=auto
#
# RUNSIZE: ...
#
# The size of the /run tmpfs mount point, like 256M or 10%
# Overridden by optional initramfs.runsize= bootarg
#
RUNSIZE=10%
#
# FSTYPE: ...
#
# The filesystem type(s) to support, or "auto" to use the current root
# filesystem type
#
FSTYPE=auto
# Disable hibernation
RESUME=none

18
initramfs/modules Normal file
View File

@@ -0,0 +1,18 @@
# List of modules that you want to include in your initramfs.
# They will be loaded at boot time in the order below.
#
# Syntax: module_name [args ...]
#
# Filesystem support
nls_iso8859-1
isofs
ext4
squashfs
overlay
# Network modules
af_packet
# RTL8125 network driver for 2.5GbE
r8125

147
initramfs/scripts/netboot Executable file
View File

@@ -0,0 +1,147 @@
#!/bin/sh
# Import standard initramfs functions
. /scripts/functions
export PATH=/usr/bin:/usr/sbin:/bin:/sbin
MOUNTPOINT=/root
TMPFS_MOUNT=/mnt
# Parse kernel command line for HTTP root
parse_cmdline() {
for x in $(cat /proc/cmdline); do
case $x in
root=http://*)
export ROOT_URL="${x#root=}" ;;
rootfstype=*)
export ROOTFSTYPE="${x#rootfstype=}" ;;
overlayroot=*)
export OVERLAYROOT="${x#overlayroot=}" ;;
ip=*)
export BOOTIP="${x#ip=}" ;;
*)
: ;;
esac
done
}
mountroot() {
rc=1
parse_cmdline
if test -z "${ROOT_URL}"; then
log_failure_msg "No root URL defined (root=http://... not found)"
return ${rc}
fi
# Configure networking before attempting downloads
log_begin_msg "Configuring network"
modprobe af_packet || log_warning_msg "af_packet load failed"
# Load RTL8125 driver (already in module list but explicit load for debugging)
modprobe r8125 || log_warning_msg "r8125 driver load failed, may use generic driver"
configure_networking
udevadm trigger
timeout 30 udevadm settle || log_warning_msg "udevadm settle timed out"
export DEVICE
log_end_msg
# Validate networking is up
INTERFACE_UP=0
for iface in $(ip link show | grep "^[0-9]" | awk -F: '{print $2}' | tr -d ' '); do
if ip addr show "$iface" | grep -q "inet "; then
INTERFACE_UP=1
log_begin_msg "Interface $iface has IP address"
ip addr show "$iface" | grep "inet " | awk '{print $2}'
break
fi
done
if [ $INTERFACE_UP -eq 0 ]; then
log_failure_msg "No network interface obtained an IP address"
return ${rc}
fi
# Extract filename from URL
FILE_NAME=$(basename "${ROOT_URL}")
FILE_PATH="/${FILE_NAME}"
# Download the root filesystem with retries and timeouts
log_begin_msg "Downloading root filesystem from ${ROOT_URL}"
if wget --timeout=30 --tries=3 --waitretry=5 \
--progress=dot:mega \
"${ROOT_URL}" -O "${FILE_PATH}"; then
log_end_msg
else
log_failure_msg "Failed to download from ${ROOT_URL} after retries"
rm -f "${FILE_PATH}"
return ${rc}
fi
# Verify the downloaded file is a valid SquashFS
if ! file "${FILE_PATH}" | grep -q "Squash"; then
log_failure_msg "Downloaded file is not a valid SquashFS image"
rm -f "${FILE_PATH}"
return ${rc}
fi
# Handle SquashFS images with overlay
if echo "${FILE_NAME}" | grep -q squashfs; then
log_begin_msg "Setting up SquashFS with overlay"
# Mount read-only SquashFS
if ! mount -t squashfs "${FILE_PATH}" "${MOUNTPOINT}" -o ro; then
log_failure_msg "Failed to mount SquashFS at ${MOUNTPOINT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
log_begin_msg "SquashFS mounted at ${MOUNTPOINT}"
log_end_msg
# Setup overlay if requested
if [ -n "${OVERLAYROOT}" ]; then
log_begin_msg "Mounting ${OVERLAYROOT} for overlay"
# Create tmpfs for upper and work directories
if ! mount -o size=2G -t "${OVERLAYROOT}" tmpfs_overlay "${TMPFS_MOUNT}"; then
log_failure_msg "Failed to mount tmpfs for overlay"
umount "${MOUNTPOINT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
# Create overlay structure
mkdir -p "${TMPFS_MOUNT}/upper" "${TMPFS_MOUNT}/work"
# Mount overlay combining read-only lower + writable upper
if ! mount -t overlay \
-o "lowerdir=${MOUNTPOINT},upperdir=${TMPFS_MOUNT}/upper,workdir=${TMPFS_MOUNT}/work" \
overlay_root "${MOUNTPOINT}"; then
log_failure_msg "Failed to mount overlay filesystem"
umount "${TMPFS_MOUNT}"
umount "${MOUNTPOINT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
log_end_msg
log_begin_msg "Overlay mounted successfully"
log_end_msg
# Clean up downloaded image as it's now mounted
rm -f "${FILE_PATH}"
rc=0
else
# Direct SquashFS mount without overlay
log_begin_msg "Mounted SquashFS without overlay"
log_end_msg
rc=0
fi
else
log_failure_msg "Unknown filesystem type: ${FILE_NAME}"
rm -f "${FILE_PATH}"
fi
return ${rc}
}