diff --git a/board/aarch64/bananapi-bpi-r3/genimage.cfg.in b/board/aarch64/bananapi-bpi-r3/genimage.cfg.in index c3734ad4..67d5413a 100644 --- a/board/aarch64/bananapi-bpi-r3/genimage.cfg.in +++ b/board/aarch64/bananapi-bpi-r3/genimage.cfg.in @@ -4,16 +4,23 @@ image cfg.ext4 { size = 128M ext4 { label = "cfg" + use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } +# The /var partition will be expanded automatically at first boot +# to use the full size of the SD-card or eMMC media. image var.ext4 { empty = true temporary = true - size = 1G + size = 128M ext4 { label = "var" use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } diff --git a/board/aarch64/friendlyarm-nanopi-r2s/genimage.cfg.in b/board/aarch64/friendlyarm-nanopi-r2s/genimage.cfg.in index 6a069181..8c0031b4 100644 --- a/board/aarch64/friendlyarm-nanopi-r2s/genimage.cfg.in +++ b/board/aarch64/friendlyarm-nanopi-r2s/genimage.cfg.in @@ -5,17 +5,24 @@ image cfg.ext4 { ext4 { label = "cfg" + use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } +# The /var partition will be expanded automatically at first boot +# to use the full size of the SD-card or eMMC media. image var.ext4 { empty = true temporary = true - size = 512M + size = 128M ext4 { label = "var" - use-mke2fs = true + use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } diff --git a/board/aarch64/raspberrypi-rpi64/genimage.cfg.in b/board/aarch64/raspberrypi-rpi64/genimage.cfg.in index 51e45bd2..8e7b356e 100644 --- a/board/aarch64/raspberrypi-rpi64/genimage.cfg.in +++ b/board/aarch64/raspberrypi-rpi64/genimage.cfg.in @@ -15,17 +15,24 @@ image cfg.ext4 { ext4 { label = "cfg" + use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } +# The /var partition will be expanded automatically at first boot +# to use the full size of the SD-card or eMMC media. image var.ext4 { empty = true temporary = true - size = 512M + size = 128M ext4 { label = "var" - use-mke2fs = true + use-mke2fs = true + features = "uninit_bg" + extraargs = "-m 0 -i 4096" } } diff --git a/board/common/mkaux.sh b/board/common/mkaux.sh index 3f139452..0adf9f0f 100755 --- a/board/common/mkaux.sh +++ b/board/common/mkaux.sh @@ -11,7 +11,8 @@ image aux.ext4 { ext4 { label = "aux" use-mke2fs = true - features = "^metadata_csum,^metadata_csum_seed" + features = "^metadata_csum,^metadata_csum_seed,uninit_bg" + extraargs = "-m 0 -i 4096" } } diff --git a/board/common/rootfs/etc/fstab b/board/common/rootfs/etc/fstab index 6c56c7f7..fc5850c7 100644 --- a/board/common/rootfs/etc/fstab +++ b/board/common/rootfs/etc/fstab @@ -15,9 +15,9 @@ cfgfs /config configfs nofail,noauto 0 0 # The chosen backing storage for the overlays placed on /cfg, /etc, # /home, /root, and /var, are determined dynamically by /usr/libexec/infix/mnt # depending on the available devices. -mnttmp /mnt/tmp tmpfs defaults 0 0 -LABEL=aux /mnt/aux auto noatime,nodiratime,noauto 0 0 -LABEL=var /mnt/var auto noatime,nodiratime,noauto 0 0 -LABEL=cfg /mnt/cfg auto noatime,nodiratime,noauto 0 0 -hostfs /mnt/host 9p cache=none,msize=16384,noauto 0 0 -/usr/libexec/infix/mnt# /cfg helper none 0 0 +mnttmp /mnt/tmp tmpfs defaults 0 0 +LABEL=aux /mnt/aux auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 +LABEL=var /mnt/var auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 +LABEL=cfg /mnt/cfg auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 +hostfs /mnt/host 9p cache=none,msize=16384,noauto 0 0 +/usr/libexec/infix/mnt# /cfg helper none 0 0 diff --git a/board/common/rootfs/usr/libexec/infix/mnt b/board/common/rootfs/usr/libexec/infix/mnt index 44ff6dbe..d46af0c6 100755 --- a/board/common/rootfs/usr/libexec/infix/mnt +++ b/board/common/rootfs/usr/libexec/infix/mnt @@ -19,6 +19,7 @@ set -e nm=$(basename "$0") err=0 +mmc="" opt="-k" # External button or bootloader changed kernel command line @@ -53,17 +54,22 @@ factory_reset() is_mmc() { + [ -n "$mmc" ] && return $mmc + # Check if primary or secondary partition (our rootfs) is on MMC for label in primary secondary; do devname=$(find_partition_by_label "$label" 2>/dev/null) if [ -n "$devname" ]; then case "$devname" in mmcblk*) + mmc=0 return 0 ;; esac fi done + + mmc=1 return 1 } @@ -89,8 +95,15 @@ find_partition_by_label() for diskpath in /sys/class/block/*; do devname=$(basename "$diskpath") + + # Skip partitions, only check whole disks [ -f "$diskpath/partition" ] && continue + # Skip ram, loop, and other virtual devices + case "$devname" in + ram*|loop*|nullb*|dm-*) continue ;; + esac + disk="/dev/$devname" result=$(sgdisk -p "$disk" 2>/dev/null | awk -v label="$label" -v devname="$devname" ' /^ *[0-9]/ { @@ -133,7 +146,7 @@ status() print_start() { - printf '\r%s%s' "$(status 3)" "$*" > /dev/console + printf '\r\033[K%s%s' "$(status 3)" "$*" > /dev/console } print_end() @@ -146,6 +159,12 @@ print_end() fi } +# Restore Finit's original progress message so its [ OK ] appears correctly +print_restore() +{ + print_start "Mounting filesystems from /etc/fstab" +} + # Helper to log resize error and create failure marker (stage 1) resize_err() { @@ -245,9 +264,7 @@ resize_filesystem() mv /mnt/aux/resized.pending /mnt/aux/resized sync - # Restore Finit's original progress message so its [ OK ] appears correctly - print_start "Mounting filesystems from /etc/fstab" - + print_restore return 0 } @@ -259,15 +276,21 @@ mount_rw() # Check if /var has been resized to fill the sdcard/eMMC if [ "$1" = "var" ] && is_mmc; then if [ -f /mnt/aux/resized ] || [ -f /mnt/aux/resized.failed ]; then + # 3. Already done : elif [ -f /mnt/aux/resized.pending ]; then + # 2. After reboot we can resize ext4 resize_filesystem "$1" else + # 1. Start with resizing the var partition to the end of the media resize_partition "$1" fi fi # TODO: Also look for UBI partitions + + # + tune2fs -c 0 -i 0 LABEL="$1" 2>/dev/null mount LABEL="$1" 2>/dev/null && return 0 return 1 diff --git a/configs/aarch64_defconfig b/configs/aarch64_defconfig index 553c6157..94e14b14 100644 --- a/configs/aarch64_defconfig +++ b/configs/aarch64_defconfig @@ -124,6 +124,7 @@ BR2_PACKAGE_MG=y BR2_PACKAGE_NANO=y BR2_TARGET_ROOTFS_SQUASHFS=y # BR2_TARGET_ROOTFS_TAR is not set +BR2_PACKAGE_HOST_BMAP_TOOLS=y BR2_PACKAGE_HOST_E2FSPROGS=y BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y BR2_PACKAGE_HOST_GENEXT2FS=y diff --git a/configs/aarch64_minimal_defconfig b/configs/aarch64_minimal_defconfig index c3b7255f..664e9616 100644 --- a/configs/aarch64_minimal_defconfig +++ b/configs/aarch64_minimal_defconfig @@ -100,6 +100,7 @@ BR2_PACKAGE_MG=y BR2_PACKAGE_NANO=y BR2_TARGET_ROOTFS_SQUASHFS=y # BR2_TARGET_ROOTFS_TAR is not set +BR2_PACKAGE_HOST_BMAP_TOOLS=y BR2_PACKAGE_HOST_E2FSPROGS=y BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y BR2_PACKAGE_HOST_GENEXT2FS=y @@ -114,8 +115,11 @@ INFIX_HOME="https://github.com/kernelkit/infix/" INFIX_DOC="https://kernelkit.org/infix/" INFIX_SUPPORT="mailto:kernelkit@googlegroups.com" BR2_PACKAGE_ALDER_ALDER=y +BR2_PACKAGE_BANANAPI_BPI_R3=y +BR2_PACKAGE_FRIENDLYARM_NANOPI_R2S=y BR2_PACKAGE_MARVELL_CN9130_CRB=y BR2_PACKAGE_MARVELL_ESPRESSOBIN=y +BR2_PACKAGE_RASPBERRYPI_RPI64=y BR2_PACKAGE_STYX_DCP_SC_28P=y BR2_PACKAGE_CONFD=y BR2_PACKAGE_CONFD_TEST_MODE=y diff --git a/configs/riscv64_defconfig b/configs/riscv64_defconfig index b69eb726..198cd002 100644 --- a/configs/riscv64_defconfig +++ b/configs/riscv64_defconfig @@ -164,6 +164,7 @@ BR2_TARGET_UBOOT_FORMAT_CUSTOM_NAME="u-boot.itb" BR2_TARGET_UBOOT_SPL=y BR2_TARGET_UBOOT_SPL_NAME="spl/u-boot-spl.bin.normal.out" BR2_TARGET_UBOOT_CUSTOM_DTS_PATH="$(BR2_EXTERNAL_INFIX_PATH)/board/riscv64/visionfive2/uboot/visionfive2-env.dtsi" +BR2_PACKAGE_HOST_BMAP_TOOLS=y BR2_PACKAGE_HOST_GENIMAGE=y BR2_PACKAGE_HOST_RAUC=y BR2_PACKAGE_HOST_UBOOT_TOOLS=y diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index bfcd8206..49569077 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -3,6 +3,25 @@ Change Log All notable changes to the project are documented in this file. +[v25.11.0][UNRELEASED] +------------------------- + +### Changes + +- Improvements to `sdcard.img` generation, useful for developers mostly: + - The NanoPi R2S bootloader is now automatically built and uploaded to + the [`latest-boot` release][lastest-boot] tag + - The `utils/mkimage.sh` script now supports fetching the bootloader + - The raspberrypi-rpi64 board's bootloader is now aptly named rpi64 + +### Fixes + +- Fix #855: User admin sometimes fails to be added to `wheel` group +- Fix serious regression in boot time, introduced in v25.10, delays the + boot step "Mounting filesystems ..." with up to 30 seconds! + +[lastest-boot]: https://github.com/kernelkit/infix/releases/latest-boot + [v25.10.0][] - 2025-10-31 ------------------------- @@ -1725,7 +1744,8 @@ Supported YANG models in addition to those used by sysrepo and netopeer: - N/A [buildroot]: https://buildroot.org/ -[UNRELEASED]: https://github.com/kernelkit/infix/compare/v25.09.0...HEAD +[UNRELEASED]: https://github.com/kernelkit/infix/compare/v25.10.0...HEAD +[v25.11.0]: https://github.com/kernelkit/infix/compare/v25.10.0...v26.11.0 [v25.10.0]: https://github.com/kernelkit/infix/compare/v25.09.0...v26.10.0 [v25.09.0]: https://github.com/kernelkit/infix/compare/v25.08.0...v26.09.0 [v25.08.0]: https://github.com/kernelkit/infix/compare/v25.06.1...v26.08.0 diff --git a/utils/mkimage.sh b/utils/mkimage.sh index b2b25519..58f7ef0d 100755 --- a/utils/mkimage.sh +++ b/utils/mkimage.sh @@ -18,10 +18,12 @@ Usage: $0 [OPTIONS] Options: + -b boot-dir Path to bootloader build directory (default: O= or output/) + -d Download bootloader files from latest-boot release + -f Force re-download of bootloader even if cached -h This help text -l List available boards -o Override auto-detection of genimage.sh, use host installed version - -b boot-dir Path to bootloader build directory (default: O= or output/) -r root-dir Path to rootfs build directory or rootfs.squashfs file (default: O= or output/) Arguments: @@ -44,8 +46,11 @@ Examples: # Standalone with separate boot/rootfs builds: $0 -b x-boot -r output raspberrypi-rpi64 - # With downloaded rootfs: - $0 -b x-boot -r ~/Downloads/rootfs.squashfs friendlyarm-nanopi-r2s + # With downloaded rootfs and bootloader: + $0 -d -r ~/Downloads/rootfs.squashfs friendlyarm-nanopi-r2s + + # Download bootloader and compose with Linux image in output directory: + $0 -od bananapi-bpi-r3 EOF } @@ -153,6 +158,83 @@ find_build_dir() return 1 } +# Map board name to bootloader identifier +# Returns bootloader name used in artifact naming +get_bootloader_name() +{ + board="$1" + case "$board" in + raspberrypi-rpi64) + echo "rpi64_boot" + ;; + bananapi-bpi-r3) + echo "bpi_r3_boot" + ;; + friendlyarm-nanopi-r2s) + echo "nanopi_r2s_boot" + ;; + *) + err "Unknown bootloader for board: $board" + return 1 + ;; + esac +} + +# Download and extract bootloader from latest-boot release +# Downloads to dl/bootloader/ cache and extracts to temporary build directory +# Returns the temporary directory path in SDCARD_TEMP_DIR variable +download_bootloader() +{ + board="$1" + build_dir="$2" + + bootloader=$(get_bootloader_name "$board") || return 1 + + if ! command -v gh >/dev/null 2>&1; then + die "gh CLI not found. Install it or build bootloader locally." + fi + + # Set up download cache directory + dl_dir="${BR2_EXTERNAL_INFIX_PATH}/dl/bootloader" + mkdir -p "$dl_dir" + + # Convert underscores to dashes for filename pattern matching + bootloader_pattern=$(echo "$bootloader" | tr '_' '-') + + # Find or download bootloader tarball + tarball=$(ls "$dl_dir"/${bootloader_pattern}*.tar.gz 2>/dev/null | head -n1) + + if [ -z "$tarball" ] || [ -n "$FORCE_DOWNLOAD" ]; then + if [ -n "$FORCE_DOWNLOAD" ] && [ -n "$tarball" ]; then + log "Force re-downloading bootloader..." + rm -f "$tarball" "$tarball.sha256" + else + log "Downloading bootloader $bootloader from latest-boot release..." + fi + + if ! gh release download latest-boot \ + --repo kernelkit/infix \ + --pattern "*${bootloader_pattern}*.tar.gz" \ + --dir "$dl_dir"; then + die "Failed downloading bootloader from latest-boot release. Check gh authentication and network connectivity." + fi + + tarball=$(ls "$dl_dir"/${bootloader_pattern}*.tar.gz 2>/dev/null | head -n1) + [ -n "$tarball" ] || die "Downloaded tarball not found in $dl_dir" + else + log "Using cached bootloader: $(basename "$tarball")" + fi + + # Create temporary directory for SD card composition + SDCARD_TEMP_DIR="${build_dir}/sdcard-${board}-$$" + mkdir -p "$SDCARD_TEMP_DIR" + + log "Extracting bootloader to $SDCARD_TEMP_DIR..." + tar -xzf "$tarball" --strip-components=1 -C "$SDCARD_TEMP_DIR/" + + log "Bootloader ready in temporary directory" +} + # Discover boot files for Raspberry Pi boot partition # Scans rpi-firmware directory and builds file list for genimage discover_rpi_boot_files() @@ -189,8 +271,19 @@ discover_rpi_boot_files() echo "$files" } -while getopts "hlob:r:" opt; do +while getopts "hldfob:r:" opt; do case $opt in + b) + BOOT_DIR="$OPTARG" + STANDALONE=1 + ;; + d) + DOWNLOAD_BOOT=1 + STANDALONE=1 + ;; + f) + FORCE_DOWNLOAD=1 + ;; h) usage exit 0 @@ -199,10 +292,6 @@ while getopts "hlob:r:" opt; do list_boards exit 0 ;; - b) - BOOT_DIR="$OPTARG" - STANDALONE=1 - ;; o) OVERRIDE=1 ;; @@ -225,12 +314,19 @@ fi # Standalone mode: set up environment from build directories if [ -n "$STANDALONE" ]; then - if [ -z "$BOOT_DIR" ]; then - BOOT_DIR=$(find_build_dir) || die "Could not find boot directory. Use -b option" - fi + # In download mode without explicit dirs, default to same location for both + if [ -n "$DOWNLOAD_BOOT" ]; then + default_dir=$(find_build_dir) || die "Could not find build directory. Set O= or use -b/-r option" + : "${BOOT_DIR:=$default_dir}" + : "${ROOT_DIR:=$default_dir}" + else + if [ -z "$BOOT_DIR" ]; then + BOOT_DIR=$(find_build_dir) || die "Could not find boot directory. Use -b option" + fi - if [ -z "$ROOT_DIR" ]; then - ROOT_DIR=$(find_build_dir) || die "Could not find rootfs directory. Set O= or use -r option" + if [ -z "$ROOT_DIR" ]; then + ROOT_DIR=$(find_build_dir) || die "Could not find rootfs directory. Set O= or use -r option" + fi fi # Set up environment variables, some required by genimage.sh @@ -248,22 +344,33 @@ if [ -n "$STANDALONE" ]; then fi done - # Copy rootfs and partition images to BINARIES_DIR + # Copy rootfs and partition images to BINARIES_DIR (skip if same directory) mkdir -p "$BINARIES_DIR" + + # Normalize paths for comparison + boot_images=$(cd "$BOOT_DIR" && pwd)/images + root_images="" + if [ -f "$ROOT_DIR" ]; then # Direct path to rootfs.squashfs file log "Copying rootfs from $ROOT_DIR to $BINARIES_DIR/rootfs.squashfs" cp "$ROOT_DIR" "$BINARIES_DIR/rootfs.squashfs" elif [ -f "$ROOT_DIR/images/rootfs.squashfs" ]; then - # Build directory with images/ - copy rootfs and partition images - log "Copying artifacts from $ROOT_DIR/images/ to $BINARIES_DIR/" - cp "$ROOT_DIR/images/rootfs.squashfs" "$BINARIES_DIR/" - # Copy partition images if they exist - for img in aux.ext4 cfg.ext4 var.ext4; do - if [ -f "$ROOT_DIR/images/$img" ]; then - cp "$ROOT_DIR/images/$img" "$BINARIES_DIR/" - fi - done + root_images=$(cd "$ROOT_DIR" && pwd)/images + # Only copy if different directories + if [ "$boot_images" != "$root_images" ]; then + # Build directory with images/ - copy rootfs and partition images + log "Copying artifacts from $ROOT_DIR/images/ to $BINARIES_DIR/" + cp "$ROOT_DIR/images/rootfs.squashfs" "$BINARIES_DIR/" + # Copy partition images if they exist + for img in aux.ext4 cfg.ext4 var.ext4; do + if [ -f "$ROOT_DIR/images/$img" ]; then + cp "$ROOT_DIR/images/$img" "$BINARIES_DIR/" + fi + done + else + log "Rootfs already in place at $BINARIES_DIR/" + fi elif [ -f "$ROOT_DIR/rootfs.squashfs" ]; then # Directory directly containing rootfs.squashfs log "Copying rootfs from $ROOT_DIR/rootfs.squashfs" @@ -292,6 +399,42 @@ fi : "${RELEASE:=""}" : "${INFIX_ID:="infix"}" +# Download bootloader if requested +if [ -n "$DOWNLOAD_BOOT" ]; then + # Save original output location + ORIGINAL_BINARIES_DIR="$BINARIES_DIR" + + download_bootloader "$BOARD" "$BUILD_DIR" + + # Now use the temporary directory for composition + BINARIES_DIR="$SDCARD_TEMP_DIR" + + log "Linking rootfs files to $BINARIES_DIR..." + # Link rootfs and partition images to temp directory + if [ -f "$ROOT_DIR" ]; then + # Direct path to rootfs.squashfs file + ln -sf "$(realpath "$ROOT_DIR")" "$BINARIES_DIR/rootfs.squashfs" + elif [ -f "$ROOT_DIR/images/rootfs.squashfs" ]; then + ln -sf "$(realpath "$ROOT_DIR/images/rootfs.squashfs")" "$BINARIES_DIR/rootfs.squashfs" + # Link partition images if they exist + for img in aux.ext4 cfg.ext4 var.ext4; do + if [ -f "$ROOT_DIR/images/$img" ]; then + ln -sf "$(realpath "$ROOT_DIR/images/$img")" "$BINARIES_DIR/$img" + fi + done + elif [ -f "$ROOT_DIR/rootfs.squashfs" ]; then + ln -sf "$(realpath "$ROOT_DIR/rootfs.squashfs")" "$BINARIES_DIR/rootfs.squashfs" + # Link partition images if they exist + for img in aux.ext4 cfg.ext4 var.ext4; do + if [ -f "$ROOT_DIR/$img" ]; then + ln -sf "$(realpath "$ROOT_DIR/$img")" "$BINARIES_DIR/$img" + fi + done + else + die "Could not find rootfs.squashfs in $ROOT_DIR" + fi +fi + # Template expansion log "Generating genimage configuration for $BOARD..." @@ -343,6 +486,25 @@ else run_genimage "$GENIMAGE_CFG" fi +# Post-processing: move images and cleanup if using download mode +if [ -n "$DOWNLOAD_BOOT" ]; then + log "Moving SD card images to $ORIGINAL_BINARIES_DIR..." + mkdir -p "$ORIGINAL_BINARIES_DIR" + + for img in "${BINARIES_DIR}"/*-sdcard.img*; do + if [ -f "$img" ]; then + mv "$img" "$ORIGINAL_BINARIES_DIR/" + log " $(basename "$img")" + fi + done + + log "Cleaning up temporary directory..." + rm -rf "$SDCARD_TEMP_DIR" + + # Update BINARIES_DIR for final output message + BINARIES_DIR="$ORIGINAL_BINARIES_DIR" +fi + log "SD card image created successfully:" for img in "${BINARIES_DIR}"/*-sdcard.img*; do if [ -f "$img" ]; then