Fix netboot: hardcode config values, simplify boot chain

- Hardcode ROOT_URL and OVERLAYROOT in netboot script
- Remove cmdline parsing that was failing silently
- Simplify boot.ipxe to chain to netboot.ipxe
- Add rebuild-initramfs.sh helper script

Resolves kernel panic caused by cmdline parsing issues.
This commit is contained in:
2026-02-05 01:16:41 +01:00
parent a927b69aad
commit 258d1ecc60
3 changed files with 117 additions and 200 deletions

View File

@@ -1,17 +1,5 @@
#!ipxe #!ipxe
echo Booting Ubuntu Noble K3s Node via iPXE echo Configuring network via DHCP...
dhcp
# Clear any previous images echo Chaining to dynamic boot script...
imgfree chain http://192.168.100.1:8800/netboot.ipxe
echo Loading kernel from http://192.168.100.1:8800/vmlinuz
kernel --name vmlinuz http://192.168.100.1:8800/vmlinuz
echo Loading initramfs from http://192.168.100.1:8800/initrd-netboot.img
initrd --name initrd http://192.168.100.1:8800/initrd-netboot.img
echo Setting kernel arguments for HTTP root mounting
imgargs vmlinuz initrd=initrd rdinit=/init boot=netboot root=http://192.168.100.1:8800/filesystem.squashfs rootfstype=squashfs overlayroot=tmpfs ip=dhcp console=tty0 console=ttyS0,115200 loglevel=7 panic=-1 break=mountroot
echo Booting system...
boot vmlinuz

View File

@@ -1,207 +1,85 @@
#!/bin/sh #!/bin/sh
# Netboot HTTP root mounting - sourced by initramfs init (functions already loaded) # Netboot HTTP root mounting - HARDCODED VALUES - no cmdline parsing
export PATH=/usr/bin:/usr/sbin:/bin:/sbin export PATH=/usr/bin:/usr/sbin:/bin:/sbin
# HARDCODED CONFIGURATION
ROOT_URL="http://192.168.100.1:8800/filesystem.squashfs"
OVERLAYROOT="tmpfs"
MOUNTPOINT=/root MOUNTPOINT=/root
SQUASHFS_MOUNT=/mnt/squashfs SQUASHFS_MOUNT=/mnt/squashfs
OVERLAY_TMPFS=/mnt/overlay OVERLAY_TMPFS=/mnt/overlay
# Hook functions for initramfs-tools boot script integration # Debug logging to console
netboot_top() log() {
{ echo "$@" > /dev/console 2>&1
if [ "${netboot_top_used}" != "yes" ]; then
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/netboot-top"
run_scripts /scripts/netboot-top
[ "$quiet" != "y" ] && log_end_msg
fi
netboot_top_used=yes
} }
netboot_premount() # Minimal hook functions
{ netboot_top() { :; }
if [ "${netboot_premount_used}" != "yes" ]; then netboot_premount() { :; }
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/netboot-premount" netboot_bottom() { :; }
run_scripts /scripts/netboot-premount mount_top() { :; }
[ "$quiet" != "y" ] && log_end_msg mount_premount() { :; }
fi mount_bottom() { :; }
netboot_premount_used=yes
}
netboot_bottom()
{
if [ "${netboot_premount_used}" = "yes" ] || [ "${netboot_top_used}" = "yes" ]; then
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/netboot-bottom"
run_scripts /scripts/netboot-bottom
[ "$quiet" != "y" ] && log_end_msg
fi
netboot_premount_used=no
netboot_top_used=no
}
# 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() { mountroot() {
rc=1 log "NETBOOT: ========================================"
log "NETBOOT: mountroot() HARDCODED VERSION"
log "NETBOOT: ROOT_URL=${ROOT_URL}"
log "NETBOOT: ========================================"
# Run hook scripts # Load network module
netboot_top /sbin/modprobe af_packet
netboot_premount
parse_cmdline # Wait for udev
wait_for_udev 10
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 via DHCP
log "NETBOOT: Calling configure_networking..."
configure_networking configure_networking
udevadm trigger
timeout 30 udevadm settle || log_warning_msg "udevadm settle timed out"
export DEVICE
log_end_msg
# Validate networking is up # Check we got an IP
INTERFACE_UP=0 log "NETBOOT: Checking for IP address..."
for iface in $(ip link show | grep "^[0-9]" | awk -F: '{print $2}' | tr -d ' '); do if ! ip addr show | grep -q "inet "; then
if ip addr show "$iface" | grep -q "inet "; then log "NETBOOT: FATAL - no IP address"
INTERFACE_UP=1 return 1
log_begin_msg "Interface $iface has IP address" fi
ip addr show "$iface" | grep "inet " | awk '{print $2}' log "NETBOOT: Network is up"
break
fi
done
if [ $INTERFACE_UP -eq 0 ]; then # Download squashfs
log_failure_msg "No network interface obtained an IP address" log "NETBOOT: Downloading ${ROOT_URL}..."
return ${rc} if ! wget -O /filesystem.squashfs "${ROOT_URL}"; then
log "NETBOOT: FATAL - wget failed"
return 1
fi
log "NETBOOT: Download complete"
# Create mount points
mkdir -p "${SQUASHFS_MOUNT}" "${OVERLAY_TMPFS}"
# Mount squashfs
log "NETBOOT: Mounting squashfs..."
if ! mount -t squashfs /filesystem.squashfs "${SQUASHFS_MOUNT}" -o ro; then
log "NETBOOT: FATAL - squashfs mount failed"
return 1
fi fi
# Extract filename from URL # Mount tmpfs for overlay
FILE_NAME=$(basename "${ROOT_URL}") log "NETBOOT: Mounting tmpfs..."
FILE_PATH="/${FILE_NAME}" if ! mount -t tmpfs -o size=2G tmpfs "${OVERLAY_TMPFS}"; then
log "NETBOOT: FATAL - tmpfs mount failed"
return 1
fi
mkdir -p "${OVERLAY_TMPFS}/upper" "${OVERLAY_TMPFS}/work"
# Download the root filesystem with retries and timeouts # Mount overlay
log_begin_msg "Downloading root filesystem from ${ROOT_URL}" log "NETBOOT: Mounting overlay..."
if wget --timeout=30 --tries=3 --waitretry=5 \ if ! mount -t overlay -o "lowerdir=${SQUASHFS_MOUNT},upperdir=${OVERLAY_TMPFS}/upper,workdir=${OVERLAY_TMPFS}/work" overlay "${MOUNTPOINT}"; then
--progress=dot:mega \ log "NETBOOT: FATAL - overlay mount failed"
"${ROOT_URL}" -O "${FILE_PATH}"; then return 1
log_end_msg
else
log_failure_msg "Failed to download from ${ROOT_URL} after retries"
rm -f "${FILE_PATH}"
return ${rc}
fi fi
# Verify the downloaded file is a valid SquashFS log "NETBOOT: SUCCESS - root mounted at ${MOUNTPOINT}"
if ! file "${FILE_PATH}" | grep -q "Squash"; then return 0
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"
# Setup overlay if requested - need separate mount points
if [ -n "${OVERLAYROOT}" ]; then
# Create mount points
mkdir -p "${SQUASHFS_MOUNT}" "${OVERLAY_TMPFS}"
# Mount read-only SquashFS to separate location
if ! mount -t squashfs "${FILE_PATH}" "${SQUASHFS_MOUNT}" -o ro; then
log_failure_msg "Failed to mount SquashFS at ${SQUASHFS_MOUNT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
log_begin_msg "SquashFS mounted at ${SQUASHFS_MOUNT}"
log_end_msg
log_begin_msg "Mounting tmpfs for overlay upper/work"
# Create tmpfs for upper and work directories
if ! mount -t tmpfs -o size=2G tmpfs_overlay "${OVERLAY_TMPFS}"; then
log_failure_msg "Failed to mount tmpfs for overlay"
umount "${SQUASHFS_MOUNT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
# Create overlay structure
mkdir -p "${OVERLAY_TMPFS}/upper" "${OVERLAY_TMPFS}/work"
# Mount overlay combining read-only lower + writable upper onto /root
if ! mount -t overlay \
-o "lowerdir=${SQUASHFS_MOUNT},upperdir=${OVERLAY_TMPFS}/upper,workdir=${OVERLAY_TMPFS}/work" \
overlay_root "${MOUNTPOINT}"; then
log_failure_msg "Failed to mount overlay filesystem"
umount "${OVERLAY_TMPFS}"
umount "${SQUASHFS_MOUNT}"
rm -f "${FILE_PATH}"
return ${rc}
fi
log_end_msg
log_begin_msg "Overlay mounted at ${MOUNTPOINT} (lower=${SQUASHFS_MOUNT})"
log_end_msg
# Clean up downloaded image as it's now mounted
rm -f "${FILE_PATH}"
rc=0
else
# Direct SquashFS mount without overlay - mount directly to /root
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 "Mounted SquashFS without overlay at ${MOUNTPOINT}"
log_end_msg
rc=0
fi
else
log_failure_msg "Unknown filesystem type: ${FILE_NAME}"
rm -f "${FILE_PATH}"
fi
return ${rc}
}
# Standard mount hook wrappers expected by initramfs init
mount_top()
{
netboot_top
}
mount_premount()
{
netboot_premount
}
mount_bottom()
{
netboot_bottom
} }

51
rebuild-initramfs.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
# Rebuild initramfs with updated netboot script
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="$SCRIPT_DIR/build"
ROOTFS="$BUILD_DIR/rootfs"
HTTP_DIR="$SCRIPT_DIR/http"
if [ ! -d "$ROOTFS" ]; then
echo "ERROR: Rootfs not found at $ROOTFS"
echo "Run build-image.sh first"
exit 1
fi
echo "=== Copying updated initramfs scripts ==="
cp "$SCRIPT_DIR/initramfs/scripts/netboot" "$ROOTFS/usr/share/initramfs-tools/scripts/netboot"
cp "$SCRIPT_DIR/initramfs/hooks/netboot" "$ROOTFS/usr/share/initramfs-tools/hooks/netboot"
cp "$SCRIPT_DIR/initramfs/initramfs.conf" "$ROOTFS/etc/initramfs-tools/initramfs.conf"
cp "$SCRIPT_DIR/initramfs/modules" "$ROOTFS/etc/initramfs-tools/modules"
echo "=== Getting kernel version ==="
KVER=$(ls "$ROOTFS/lib/modules/" | head -1)
echo "Kernel version: $KVER"
echo "=== Mounting filesystems for chroot ==="
mount --bind /proc "$ROOTFS/proc"
mount --bind /sys "$ROOTFS/sys"
mount --bind /dev "$ROOTFS/dev"
cleanup() {
echo "=== Cleaning up mounts ==="
umount "$ROOTFS/proc" 2>/dev/null || true
umount "$ROOTFS/sys" 2>/dev/null || true
umount "$ROOTFS/dev" 2>/dev/null || true
}
trap cleanup EXIT
echo "=== Rebuilding initramfs ==="
chroot "$ROOTFS" mkinitramfs -v -o /boot/initrd-netboot.img "$KVER"
echo "=== Copying to http directory ==="
cp "$ROOTFS/boot/initrd-netboot.img" "$HTTP_DIR/"
chmod 644 "$HTTP_DIR/initrd-netboot.img"
echo ""
echo "=== Done! ==="
echo "New initramfs: $HTTP_DIR/initrd-netboot.img"
ls -lh "$HTTP_DIR/initrd-netboot.img"
echo ""
echo "Run 'make deploy' to sync to NAS"