Skip to content

Instantly share code, notes, and snippets.

@misaalanshori
Last active January 8, 2026 07:18
Show Gist options
  • Select an option

  • Save misaalanshori/7b03321733827e364d5fa53e1e842fa9 to your computer and use it in GitHub Desktop.

Select an option

Save misaalanshori/7b03321733827e364d5fa53e1e842fa9 to your computer and use it in GitHub Desktop.

TODO:

  • Still missing kernel modules (from sysdrv/source/objs_kernel/drv_ko/lib/modules/, not copied in for some reason?)

Creating an Alpine Linux image for Luckfox Pico Mini B (probably works for A too)

Start by getting the Luckfox Pico SDK at https://github.com/LuckfoxTECH/luckfox-pico

Set it up by running:

# 1. Configure the Environment
cd luckfox-pico
./build.sh env
# SELECT: [2] Luckfox Pico Mini B -> [0] SDCard -> [0] Buildroot


# 2. Build U-Boot
./build.sh uboot


# 3. Build Kernel
cd luckfox-pico
./build.sh kernelconfig
# Keep default or change as needed, then we build kernel:
./build.sh kernel

4. Build alpine rootfs (skip ./build.sh rootfs) We build the rootfs ourself using https://github.com/alpinelinux/alpine-make-rootfs

sudo APK_OPTS="--arch armhf --allow-untrusted --no-progress" \
  ./alpine-make-rootfs \
  --keys-dir ./keys \
  --branch v3.21 \
  --timezone Asia/Jakarta \
  --packages "alpine-baselayout busybox openrc apk-tools shadow \
              bash bash-completion nano htop btop file \
              grep sed gawk \
              tar gzip bzip2 xz zstd \
              pigz zip unzip 7zip \
              wget curl openssh openssh-sftp-server \
              iw net-tools iproute2 wpa_supplicant wireless-tools \
              bind-tools ca-certificates \
              ethtool tcpdump socat \
              sudo parted mmc-utils \
              e2fsprogs e2fsprogs-extra \
              cloud-utils-growpart \
              usbutils util-linux util-linux-misc usb-modeswitch \
              lm-sensors i2c-tools libgpiod rng-tools \
              tmux fastfetch chrony \
              python3 git \
              linux-firmware-none \
              linux-firmware-brcm \
              linux-firmware-cypress \
              linux-firmware-ath10k \
              linux-firmware-ath9k_htc \
              linux-firmware-rtlwifi \
              linux-firmware-realtek \
              linux-firmware-mediatek
              linux-firmware-other \
              wireless-regdb"\
  ./alpine-armhf-ultimate.tar.gz --script-chroot - <<'SHELL'
  # ==========================================
  # CONFIGURATION
  # ==========================================
  TARGET_DISK="/dev/mmcblk1"
  ROOT_PART_NUM="7"
  ROOT_PARTITION="${TARGET_DISK}p${ROOT_PART_NUM}"
  
  # Export PATH for tools
  export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

  # --- 1. Filesystem Setup ---
  mkdir -p /dev/pts /proc /sys
  cat > /etc/fstab <<EOF
devpts      /dev/pts     devpts    gid=5,mode=620   0 0
proc        /proc        proc      defaults         0 0
sysfs       /sys         sysfs     defaults         0 0
tmpfs       /tmp         tmpfs     defaults         0 0
# $ROOT_PARTITION  /            ext4      noatime      0 1
EOF

  # --- 2. Service Setup ---
  rc-update add devfs sysinit
  rc-update add procfs sysinit
  rc-update add sysfs sysinit
  rc-update add networking boot
  rc-update add wpa_supplicant boot
  rc-update add sshd default
  rc-update add chronyd default
  rc-update add rngd boot

# --- 3. FIRST BOOT PROVISIONING SCRIPT ---
  # We use EOF (unquoted) so $ROOT_PARTITION expands NOW (during build),
  # but we escape \$KERNEL_VERSION so it expands LATER (during boot).
  cat > /etc/init.d/firstboot-provision <<EOF
#!/sbin/openrc-run

depend() {
    after localmount
}

start() {
    # --- STAGE 2: Post-Reboot Provisioning ---
    if [ -f /etc/rootfs-expanded ]; then
        
        # A. Resize Filesystem
        ebegin "Provisioning: Resizing Filesystem ($ROOT_PARTITION)"
        if [ -x /sbin/resize2fs ]; then
            /sbin/resize2fs $ROOT_PARTITION > /dev/null
        elif [ -x /usr/sbin/resize2fs ]; then
            /usr/sbin/resize2fs $ROOT_PARTITION > /dev/null
        fi
        eend \$?

        # B. Move Kernel Modules
        if [ -d "/oem/usr/ko" ]; then
            ebegin "Provisioning: Installing Kernel Modules"
            
            # ESCAPED VARIABLES HERE (So they run on the device, not the builder)
            KERNEL_VERSION=\$(uname -r)
            MODULES_DIR="/lib/modules/\$KERNEL_VERSION"
            
            mkdir -p "\$MODULES_DIR"
            
            # Move files to the variable path
            mv /oem/usr/ko/*.ko "\$MODULES_DIR/" 2>/dev/null
            
            # Generate dependency map
            depmod -a
            
            # Cleanup
            rm -rf /oem/usr/ko
            eend \$?
        else
            ewarn "Provisioning: No OEM modules found in /oem/usr/ko"
        fi

        # C. Create Swapfile
        if [ ! -f /swapfile ]; then
            ebegin "Provisioning: Creating 512MB Swapfile"
            fallocate -l 512M /swapfile
            chmod 600 /swapfile
            mkswap /swapfile > /dev/null
            swapon /swapfile > /dev/null
            echo '/swapfile none swap sw 0 0' >> /etc/fstab
            eend \$?
        fi

        # D. CLEANUP
        rm -f /etc/rootfs-expanded
        rc-update del firstboot-provision default
        rm -f /etc/init.d/firstboot-provision
        
    # --- STAGE 1: First Boot Partition Expand ---
    else
        ebegin "Provisioning: Expanding Partition Table"
        growpart $TARGET_DISK $ROOT_PART_NUM > /dev/null 2>&1
        touch /etc/rootfs-expanded
        eend \$?
        ebegin "Rebooting to apply partition changes..."
        reboot
    fi
}
EOF
  chmod +x /etc/init.d/firstboot-provision
  rc-update add firstboot-provision default

  # --- 4. SSH Configuration ---
  ssh-keygen -A
  sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
  sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
  sed -i 's|^#*Subsystem.*sftp.*|Subsystem sftp /usr/lib/ssh/sftp-server|' /etc/ssh/sshd_config

  # --- 5. Console ---
  sed -i '/^tty[1-6]::/d' /etc/inittab
  echo "ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100" >> /etc/inittab
  echo ttyFIQ0 >> /etc/securetty

  # --- 6. Network ---
  echo "auto eth0" >> /etc/network/interfaces
  echo "iface eth0 inet dhcp" >> /etc/network/interfaces

  # --- 7. User Configuration ---
  awk -F: 'FNR==NR {seen[$1]=1; next} !($1 in seen) {print $1":!:19700:0:99999:7:::"}' /etc/shadow /etc/passwd >> /etc/shadow
  echo "root:linux" | chpasswd
  adduser -D -s /bin/bash alpine
  awk -F: 'FNR==NR {seen[$1]=1; next} !($1 in seen) {print $1":!:19700:0:99999:7:::"}' /etc/shadow /etc/passwd >> /etc/shadow
  echo "alpine:linux" | chpasswd
  addgroup alpine wheel
  echo "%wheel ALL=(ALL:ALL) ALL" > /etc/sudoers.d/wheel
SHELL

5. Copy rootfs Copy the rootfs (alpine-armhf-ultimate.tar.gz) to luckfox-pico-alpinecopy/sysdrv/custom_rootfs/alpine-armhf-ultimate.tar.gz

6. Modify build.sh

Update __PACKAGE_ROOTFS to

function __PACKAGE_ROOTFS()
{
    local rootfs_tarball _target_dir _install_dir

    # --- Start Merged Instruction Logic ---
    # Determine the rootfs tarball path (Default vs Custom)
    if [ -z "$RK_CUSTOM_ROOTFS" ]; then
        rootfs_tarball="$RK_PROJECT_PATH_SYSDRV/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}.tar"
        
        # Verify default tarball exists
        if [ ! -f "$rootfs_tarball" ]; then
            msg_error "Not found rootfs tarball: $rootfs_tarball"
            exit 1
        fi
        
        # Extract default to Project Output
        tar xf "$rootfs_tarball" -C "$RK_PROJECT_OUTPUT"
    else
        rootfs_tarball="$RK_CUSTOM_ROOTFS"
        
        # Verify custom tarball exists
        if [ ! -f "$rootfs_tarball" ]; then
            msg_error "Not found rootfs tarball: $rootfs_tarball"
            exit 1
        fi

        # Ensure destination directory exists and extract
        if [ ! -d "$RK_PROJECT_PACKAGE_ROOTFS_DIR" ]; then
            mkdir -p "$RK_PROJECT_PACKAGE_ROOTFS_DIR"
        fi
		echo "USING CUSTOM ROOTFS: $rootfs_tarball extract to $RK_PROJECT_PACKAGE_ROOTFS_DIR"
        tar xf "$rootfs_tarball" -C "$RK_PROJECT_PACKAGE_ROOTFS_DIR"
    fi
    # --- End Merged Instruction Logic ---

    # --- Start Preserved Target Logic ---
    
    # Handle Ubuntu specific configurations (e.g., Wifi)
    if [ "$RK_BOOT_MEDIUM" == "emmc" ] && [ "$LF_TARGET_ROOTFS" == "ubuntu" ]; then
        if [ -f "$WIFI_CONF" ]; then
            cp "$WIFI_CONF" "$RK_PROJECT_PACKAGE_ROOTFS_DIR/etc"
        fi
    fi

    # Handle Buildroot/Busybox SDK info generation
    if [ "$LF_TARGET_ROOTFS" == "buildroot" ] || [ "$LF_TARGET_ROOTFS" == "busybox" ]; then
        build_get_sdk_version
        cat >"$RK_PROJECT_PACKAGE_ROOTFS_DIR/bin/sdkinfo" <<EOF
#!/bin/sh
echo Build Time:  $(date "+%Y-%m-%d-%T")
echo SDK Version: ${GLOBAL_SDK_VERSION}
EOF
        chmod a+x "$RK_PROJECT_PACKAGE_ROOTFS_DIR/bin/sdkinfo"
        __COPY_FILES "$RK_PROJECT_PATH_APP/root" "$RK_PROJECT_PACKAGE_ROOTFS_DIR"
    fi

    # Copy media and external files
    __COPY_FILES "$RK_PROJECT_PATH_MEDIA/root" "$RK_PROJECT_PACKAGE_ROOTFS_DIR"
    __COPY_FILES "$SDK_ROOT_DIR/external" "$RK_PROJECT_PACKAGE_ROOTFS_DIR"

    # Handle IQ Files symlink
    if [ -d "$RK_PROJECT_PACKAGE_ROOTFS_DIR/usr/share/iqfiles" ]; then
        (
            cd "$RK_PROJECT_PACKAGE_ROOTFS_DIR/etc"
            ln -sf ../usr/share/iqfiles ./
        )
    fi

    # Copy Rootfs initialization script
    if [ -f "$RK_PROJECT_FILE_ROOTFS_SCRIPT" ]; then
        chmod a+x "$RK_PROJECT_FILE_ROOTFS_SCRIPT"
        cp -f "$RK_PROJECT_FILE_ROOTFS_SCRIPT" "$RK_PROJECT_PACKAGE_ROOTFS_DIR/etc/init.d"
    fi
}

7. Update .BoardConfig.mk Set CMA to 1M, disable install app to oem partition, remove the shadow overlay, and add our custom rootfs

# Config CMA size in environment
export RK_BOOTARGS_CMA_SIZE="1M"

#...

# enable install app to oem partition
export RK_BUILD_APP_TO_OEM_PARTITION=n

#...

# declare overlay directory
export RK_POST_OVERLAY="overlay-luckfox-config overlay-luckfox-buildroot-init"

# Configure custom image directory
export RK_CUSTOM_ROOTFS=../sysdrv/custom_rootfs/alpine-armhf-ultimate.tar.gz
# 8. Build firmware
sudo ./build.sh firmware

# 9. Generate Partition Info & Flash Image
cd output/image
# Fix rootfs partition size in env config
sed -i 's/6G(rootfs)/100G(rootfs)/' .env.txt
# Generate env image
../../sysdrv/tools/pc/uboot_tools/mkenvimage -s 0x8000 -p 0x0 -o env.img .env.txt
# Pack the final image
./blkenvflash ../../alpine.img

Debian 12 (Bookworm) on LuckFox Pico Mini

This guide outlines how to build a custom OS image for the LuckFox Pico Mini by combining the official LuckFox Linux Kernel with a Debian 12 (Bookworm) root filesystem taken from Raspberry Pi OS.

Prerequisites

Install the necessary build tools on your host machine (tested on Ubuntu/Debian):

sudo apt update
sudo apt-get install -y git ssh make gcc gcc-multilib g++-multilib module-assistant expect g++ gawk texinfo libssl-dev bison flex fakeroot cmake unzip gperf autoconf device-tree-compiler libncurses5-dev pkg-config bc python-is-python3 passwd openssl openssh-server openssh-client vim file cpio rsync

Step 1: Build the LuckFox Kernel

We need to build the kernel provided by the manufacturer to ensure hardware support (GPIO, Ethernet, etc.).

  1. Clone the SDK:

    git clone https://github.com/LuckfoxTECH/luckfox-pico.git
    cd luckfox-pico
    git submodule update --init
  2. Configure the Board:

    # Select the board profile (choose options relative to Pico Mini)
    sudo ./build.sh lunch
    
    # Generate default kernel config
    sudo ./build.sh kernelconfig
  3. Enable Hardware Interfaces (DTS): Open the Device Tree Source file to enable Ethernet, USB, SPI, etc.

    sudo vim sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini.dts

    Changes required:

    • Set status = "okay" for: Ethernet (gmac), SPI, I2C, UART3, UART4.
    • Set status = "host" for USB.
    • Verify SPI speed is 2000000.
  4. Compile the Kernel:

    sudo ./build.sh
    
    # Also compile the kernel modules (drivers) which are needed for WiFi, networking, etc.
    sudo ./build.sh driver

Step 2: Prepare the Root Filesystem (Rootfs)

We will use the Raspberry Pi OS Lite (Bookworm) image as our base Debian system.

  1. Download and Extract:

    # Download the latest Raspberry Pi OS Lite image
    wget https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2023-12-11/2023-12-11-raspios-bookworm-armhf-lite.img.xz
    
    # Extract
    unxz 2023-12-11-raspios-bookworm-armhf-lite.img.xz
    mv 2023-12-11-raspios-bookworm-armhf-lite.img raspios.img
  2. Extract the Root Partition: The Raspberry Pi image has two partitions (boot and root). We only need the second one (root).

    Option A: Using losetup (Standard)

    # Setup loop device
    sudo losetup -fP --show raspios.img
    # (Assume it looped to /dev/loop0. The root partition is normally loop0p2)
    
    # Copy partition 2 to a new file
    sudo dd if=/dev/loop0p2 of=rootfs.img bs=4M status=progress
    
    # Clean up loop device
    sudo losetup -d /dev/loop0

    Option B: Using kpartx (Alternative) If losetup doesn't show partitions (often due to max_part=0 on some kernels), use kpartx.

    # Install kpartx if needed
    sudo apt install kpartx -y
    
    # Create mappings
    sudo kpartx -av raspios.img
    # (Output normally shows 'add map loopXp2 ...')
    
    # Copy partition 2 (check /dev/mapper/ for the correct name, e.g., loop0p2)
    sudo dd if=/dev/mapper/loop0p2 of=rootfs.img bs=4M status=progress
    
    # Clean up
    sudo kpartx -d raspios.img
  3. Repair and Resize the Filesystem: Before mounting, run a filesystem check to ensure integrity.

    # Check filesystem (Must be unmounted!)
    sudo e2fsck -f rootfs.img
    
    # (Optional) Resize if needed
    # resize2fs rootfs.img 2G
  4. Mount and Configure:

    sudo mkdir -p /mnt/rootfs
    sudo mount -o loop,rw rootfs.img /mnt/rootfs
  5. Install Kernel Modules: We must manually install the LuckFox kernel modules into the Debian rootfs.

    # Copy modules from the SDK build output
    sudo cp -r sysdrv/source/objs_kernel/drv_ko/lib/modules/* /mnt/rootfs/lib/modules/
  6. Edit /etc/fstab: The fstab needs to point to the correct devices for LuckFox, not Raspberry Pi.

    sudo vim /mnt/rootfs/etc/fstab
    • Identify the UUID using blkid rootfs.img.
    • Replace the old root entry with the new UUID or device path.
    • Add tmpfs for /run:
      proc                                       /proc           proc    defaults          0       0
      UUID=ee354f68-c22f-48b7-a81c-43ae87c280f8  /               ext4    defaults,noatime  0       1
      tmpfs                                      /dev/shm        tmpfs   defaults,size=2g  0       0
      
  7. Set Root Password: Since we don't have a GUI or the "pi" user wizard, we must manually set a root password.

    Generate a password hash:

    mkpasswd --method=SHA-512 --stdin
    # Type your desired password and press Ctrl+D. Copy the output string.

    Apply it:

    sudo vim /mnt/rootfs/etc/shadow

    Find the line starting with root:. Replace the * or ! between the first two colons with the hash you copied.

  8. Configure via Chroot (Optional but Recommended): To run commands like apt or apt-mark directly on the image before flashing, we can use chroot with QEMU.

    Setup Environment:

    # Install QEMU static (on host)
    sudo apt install qemu-user-static
    
    # Copy QEMU binary to rootfs
    sudo cp /usr/bin/qemu-arm-static /mnt/rootfs/usr/bin/
    
    # Mount system directories
    sudo mount --bind /proc /mnt/rootfs/proc
    sudo mount --bind /sys /mnt/rootfs/sys
    sudo mount --bind /dev /mnt/rootfs/dev
    sudo mount --bind /dev/pts /mnt/rootfs/dev/pts

    Enter Chroot:

    sudo chroot /mnt/rootfs /bin/bash

    Run Commands (Inside Chroot):

    You have two options: manually configure the basics, or run the automated setup script.

    Option A: Manual Configuration

    # Prevent apt from breaking the custom kernel
    apt-mark hold raspberrypi-kernel raspberrypi-bootloader
    
    # Install basic tools
    apt update && apt install -y vim git sudo
    
    exit

    Option B: Automated Foxbian Setup (Recommended) You can use the foxbian.chroot script found in femtofox/assets/ to automate user creation (fox), timezone setup, and package installation.

    # 1. (On Host) Copy the script into the mounted rootfs
    sudo cp femtofox/assets/foxbian.chroot /mnt/rootfs/
    sudo chmod +x /mnt/rootfs/foxbian.chroot
    
    # 2. (Inside Chroot) Run the script
    # If not already inside: sudo chroot /mnt/rootfs /bin/bash
    /foxbian.chroot
    
    exit

    Cleanup:

    sudo umount /mnt/rootfs/dev/pts
    sudo umount /mnt/rootfs/dev
    sudo umount /mnt/rootfs/sys
    sudo umount /mnt/rootfs/proc
  9. Unmount:

    sudo umount /mnt/rootfs

Step 3: Bundle and Flash

  1. Copy Rootfs to Output:

    sudo cp -f rootfs.img output/image/
  2. Create Final Image: Use the blkenvflash tool (ensure you have this tool available, usually in LuckFox tools) to repackage everything.

    cd output/image
    sudo ./blkenvflash ../../luckfox-debian.img
  3. Flash: Flash luckfox-debian.img to your SD card using BalenaEtcher or dd.

Foxbuntu Manual Build Guide

This document defines the complete manual process for building the Foxbuntu operating system for Femtofox (Luckfox Pico Mini A) hardware. It is intended for advanced users, developers, and those who need to understand exactly what is happening under the hood.

This guide replaces the automated foxbuntu-builder.sh script, giving you full control over every step of the process.

1. Prerequisites

Before you begin, ensure your build environment meets these requirements.

Operating System:

  • Ubuntu 22.04 LTS (Jammy Jellyfish) is required. Using other versions or distributions may lead to build failures due to toolchain incompatibilities.
  • Root Privileges: You will need sudo access for package installation and chroot operations.

Required Packages: Open your terminal and install the necessary dependencies:

sudo apt update
sudo apt install -y git ssh make gcc gcc-multilib g++-multilib module-assistant expect g++ gawk texinfo libssl-dev bison flex fakeroot cmake unzip gperf autoconf device-tree-compiler libncurses5-dev pkg-config bc python-is-python3 passwd openssl openssh-server openssh-client vim file cpio rsync qemu-user-static binfmt-support dialog

2. Workspace Setup

Unlike the automated script which forces you to use your home directory, you can set up your workspace anywhere. We will refer to your chosen directory as PROJECT_ROOT.

  1. Create and enter your workspace:

    # Example: Creating a workspace in your home folder
    mkdir -p ~/foxbuntu-workspace
    cd ~/foxbuntu-workspace
    export PROJECT_ROOT=$(pwd)
  2. Clone the repositories: You need two key repositories:

    • Luckfox Pico SDK: The base SDK provided by the manufacturer.
    • Femtofox: The repository containing Foxbuntu-specific customizations.
    # Clone Luckfox Pico SDK
    git clone https://github.com/Ruledo/luckfox-pico.git
    
    # Clone Femtofox (Foxbuntu) repo
    git clone https://github.com/femtofox/femtofox.git

3. Quick Start: Build Your First Image

If you want to get a standard Foxbuntu image built immediately without reading the deep-dive details, follow these commands in order.

Assumption: You are in your PROJECT_ROOT (e.g., ~/foxbuntu-workspace), and have already installed the prerequisites and cloned the repos as shown above.

# 1. Configure the Environment
cd luckfox-pico
./build.sh env
# SELECT: [1] Luckfox Pico Mini A -> [0] SDCard -> [1] Ubuntu

# 2. Build U-Boot
./build.sh uboot

# 3. Apply Foxbuntu Customizations (The "Sync" Step)
# This merges Foxbuntu files over the standard SDK.
cd "$PROJECT_ROOT"
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/sysdrv/ luckfox-pico/sysdrv/
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/project/ luckfox-pico/project/
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/output/image/ luckfox-pico/output/image/

# 4. Build Kernel
cd luckfox-pico
./build.sh kernelconfig
# (Exit and save the default configuration when the menu appears)
./build.sh kernel

# 4. Build RootFS (Base)
./build.sh rootfs

# 5. Run Foxbuntu Setup (Crucial!)
#    (We must perform chroot setup before packaging the firmware)
export ROOTFS_DIR="sysdrv/out/rootfs_uclibc_rv1106"
cp /usr/bin/qemu-arm-static "$ROOTFS_DIR/usr/bin/"
cp ../femtofox/environment-setup/femtofox.chroot "$ROOTFS_DIR/"
sudo chroot "$ROOTFS_DIR" /bin/bash /femtofox.chroot
#    (The script will exit automatically when done)

# 6. Package Firmware
#    (This bundles your customized rootfs into the final image)
./build.sh firmware

# 7. Generate Partition Info & Flash Image
cd output/image
# Fix rootfs partition size in env config
sed -i 's/6G(rootfs)/100G(rootfs)/' .env.txt
# Generate env image
../../sysdrv/tools/pc/uboot_tools/mkenvimage -s 0x8000 -p 0x0 -o env.img .env.txt
# Pack the final image
./blkenvflash ../../foxbuntu.img

You now have a flashable image at luckfox-pico/foxbuntu.img.


4. Detailed Build Process

This section explains each step of the build in detail, allowing you to understand the architecture and make customizations.

Step 4.1: Initial Configuration (build.sh env)

This step initializes the build system.

  • Board Selection: If .BoardConfig.mk does not exist, it launches the interactive menu to select the board, storage, and OS.
  • Environment Image: It generates env.img, which stores the U-Boot environment variables and partition table layout.
cd luckfox-pico
./build.sh env

Step 4.2: Build U-Boot

Compiles the U-Boot bootloader based on the board configuration.

  • Compilation: Builds the uboot binary using the toolchain.
  • Binaries: Generates the bootloader image (uboot.img) and the ID block used for hardware initialization.
./build.sh uboot

Step 4.3: Sync Foxbuntu Customizations (Crucial)

This is the step that turns "Luckfox Ubuntu" into "Foxbuntu". We manually overwrite files in the luckfox-pico directory with files from femtofox/foxbuntu. This process syncs two key directories: sysdrv, which contains the core build engine and source code for the kernel and U-Boot, and project, which holds the board configuration and overlay customizations.

Warning

This is a destructive action for the luckfox-pico directory. It overwrites SDK defaults with Foxbuntu specifics.

cd "$PROJECT_ROOT" # Go back to your workspace root

# Sync System Drivers & Tools
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/sysdrv/ luckfox-pico/sysdrv/

# Sync Project Configs
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/project/ luckfox-pico/project/

# Sync Image Assets
rsync -aHAXv --progress --keep-dirlinks --itemize-changes \
    femtofox/foxbuntu/output/image/ luckfox-pico/output/image/

Step 4.4: Build Kernel

Compiles the Linux Kernel (version 5.10.x).

  • Configuration: Applies the default configuration (defconfig) and Device Tree (.dts) for the Luckfox Pico Mini A.
  • Output: Produces the kernel image (boot.img) and compiles necessary kernel modules (.ko files) that will later be installed into the filesystem.
cd luckfox-pico

# Load Kernel Configuration (Optional: Only if you need to customize)
./build.sh kernelconfig

A menu (menuconfig) will appear.

  • For a standard build: Simply Exit and verify you choose Yes to save the configuration.
  • For customization: This is where you enable/disable kernel drivers and features.

After saving the config:

# Compile the Kernel
./build.sh kernel

Step 4.5: Build RootFS

Prepares the base Ubuntu root filesystem.

  • Extraction: It extracts the base Ubuntu filesystem tarball into the build output directory (output/rootfs...).
  • Preparation: Sets up the initial directory structure before Foxbuntu-specific packages and configuration changes are applied.
./build.sh rootfs

Step 4.6: Build Firmware

This is the final packaging and image generation step.

  1. Module Installation: Copies compiled kernel modules (.ko) and firmware blobs into the rootfs.
  2. Resource Packaging: Installs media libraries and other SDK tools.
  3. Image Creation: Converts the file directories (rootfs, oem, userdata) into filesystem images (rootfs.img, oem.img) using tools like e2fs or mkenvimage.
  4. Final Bundle: Generates the final update packages.
./build.sh firmware

5. RootFS Customization (Chroot)

If you need to install packages, add users, or change system configs inside the Foxbuntu image before flashing it, you use chroot.

5.1 Prepare the Environment

We need to make sure the target filesystem has access to the host's resources (network, dev, etc.) and can execute ARM binaries (via QEMU).

Note

How this works: Linux uses a kernel feature called binfmt_misc. When you install qemu-user-static on your host, it registers a "handler" with the kernel (visible in /proc/sys/fs/binfmt_misc in the host). It effectively tells the kernel: "If you try to execute a file starting with these specific ARM header bytes, use /usr/bin/qemu-arm-static as the interpreter."

When you enter chroot, the filesystem root / changes. The kernel still obeys the registered handler and tries to load the interpreter at /usr/bin/qemu-arm-static, but now that path resolves inside the chroot. That is why we must physically copy the static binary into the target filesystem—so the kernel can find the interpreter to run your ARM commands.

# Define the rootfs path for convenience
export ROOTFS_DIR="sysdrv/out/rootfs_uclibc_rv1106"

# 1. Install QEMU static binary
# This allows your x86_64 PC to run ARM commands inside the chroot.
cp /usr/bin/qemu-arm-static "$ROOTFS_DIR/usr/bin/"

# 2. Mount System Directories
# WARNING: Be careful with these. If you delete files in /dev inside chroot, 
# you might delete them on your host!
sudo mount --bind /proc "$ROOTFS_DIR/proc"
sudo mount --bind /sys "$ROOTFS_DIR/sys"
sudo mount --bind /dev "$ROOTFS_DIR/dev"
sudo mount --bind /dev/pts "$ROOTFS_DIR/dev/pts"

5.2 Enter Chroot

sudo chroot "$ROOTFS_DIR" /bin/bash

You are now "inside" the Foxbuntu system.

  • You can run apt update and apt install <package>.

  • You can edit /etc/hostname, /etc/fstab, etc.

  • (Recommended) Run the official Foxbuntu setup script to install packages and configure the environment:

    # 1. Copy the script from the femtofox repo into the chroot
    cp ../femtofox/environment-setup/femtofox.chroot "$ROOTFS_DIR/"
    
    # 2. Execute it inside the chroot
    /femtofox.chroot

    [!NOTE] What femtofox.chroot does:

    • Installs Packages: Adds apt repositories and installs huge number of dependencies (Python, system utils).
    • User Setup: Creates the femto user, groups, and sudo privileges.
    • Meshtastic: Installs meshtasticd and related tools (unless you modified the script to stop it!).
    • Services: Enables system services like femto-runonce, meshtasticd, etc.

5.3 Exit and Cleanup

When you are done:

  1. Type exit to leave the chroot.
  2. Unmount everything immediately.
sudo umount "$ROOTFS_DIR/dev/pts"
sudo umount "$ROOTFS_DIR/proc"
sudo umount "$ROOTFS_DIR/sys"
sudo umount "$ROOTFS_DIR/dev"

Important

Always rebuild the rootfs and firmware after modifying the chroot to ensure changes are packed correctly.

./build.sh rootfs
./build.sh firmware

6. Creating the Final Image

The final step packages your kernel, bootloader, and filesystem into a .img file.

  1. Configure Partition Size: The default env file might specify a small rootfs partition (6G). We expand it to fill the card (conceptually 100G, the device handles the resize or limit).

    cd output/image
    sed -i 's/6G(rootfs)/100G(rootfs)/' .env.txt
  2. Generate Environment Image:

    ../../sysdrv/tools/pc/uboot_tools/mkenvimage -s 0x8000 -p 0x0 -o env.img .env.txt
  3. Pack the Image:

    ./blkenvflash ../../foxbuntu.img
  4. Verify: Check if the image exists:

    ls -lh ../../foxbuntu.img

8. Advanced Topics

8.1 Understanding the SDK Architecture

The Luckfox SDK is divided into two primary directories that handle different aspects of the build process. Understanding this separation is key to advanced customization.

sysdrv (System Drive/Driver)

This directory acts as the build engine. It contains the source code for the low-level system components and the logic to build them.

  • Role: It handles cross-compilation of the toolchain, kernel, U-Boot, and base filesystems.
  • Key Locations:
    • source/kernel: The Linux Kernel source tree.
    • source/uboot: The U-Boot bootloader source tree.
    • tools/board/: Contains compressed source archives (tarballs) for userland components like busybox and buildroot.

project

This directory handles configuration and customization. It defines what sysdrv builds and how the final image is assembled.

  • Role: It stores board specific configurations (.mk files) and overlay files that get applied on top of the base system.
  • Key Locations:
    • cfg/BoardConfig_IPC/: Contains the board configuration files (e.g., BoardConfig-SD_CARD-Ubuntu...mk).
    • cfg/BoardConfig_IPC/overlay/: Contains filesystem overlays (see Section 8.3).

8.2 Source Code & Tarballs (Busybox/Buildroot/Ubuntu)

Unlike the kernel, sources for busybox, buildroot, and the ubuntu base image are not populated in sysdrv/source by default. They are stored as compressed archives (or split archives) and extracted only during the build process to valid integrity.

  • Busybox Archive: sysdrv/tools/board/busybox/busybox-1.27.2.tar.bz2
  • Buildroot Archive: sysdrv/tools/board/buildroot/buildroot-2023.02.6.tar.gz
  • Ubuntu Base Image: sysdrv/tools/board/ubuntu/luckfox-ubuntu-22.04.3.tar.gz.split.*
    • Note: Taking a closer look, you will see files named .split.0 through .split.6. The build script concatenates these segments into a single tarball before extracting. This strategy bypasses GitHub file size limits.

How it works: The build system (sysdrv/Makefile) checks for the existence of the unpacked folders in sysdrv/source (or sysdrv/out for rootfs). If missing, it invokes helper scripts (like split_and_check_md5.sh for Ubuntu) to reconstruct and extract the archives on-the-fly.

Note: If you need to patch Busybox source code permanently, you must either modify the tarball itself or add a patch file to sysdrv/tools/board/busybox/ and update the sysdrv/Makefile to apply it.

8.3 Advanced Customization: The Overlay System

The most powerful feature for customization is the Overlay System. This allows you to inject files into the final root filesystem without modifying the complex upstream build scripts or the base Ubuntu image.

How Overlays Work

  1. Definition: Your active BoardConfig file (created during step 4.1) defines a variable named RK_POST_OVERLAY.
    • Example: export RK_POST_OVERLAY="overlay-luckfox-config overlay-luckfox-ubuntu"
  2. Execution: During the ./build.sh firmware step, the script executes the post_overlay function.
  3. Application: It recursively copies (rsyncs) every file from the directories listed in RK_POST_OVERLAY (located in project/cfg/BoardConfig_IPC/overlay/) directly into the target rootfs.

Customizing Your Build

To add your own custom files (e.g., a startup script rc.local or a custom configuration):

  1. Locate the Overlay Directory: Go to luckfox-pico/project/cfg/BoardConfig_IPC/overlay/.
  2. Choose/Create an Overlay Folder: You can use an existing folder (like overlay-luckfox-ubuntu) or create a new one (e.g., overlay-my-custom).
    • If you create a new one, remember to add it to RK_POST_OVERLAY in your .BoardConfig.mk file.
  3. Replicate Directory Structure: Create the full path inside the overlay folder.
    • Example: To replace /etc/rc.local, create the file at: project/cfg/BoardConfig_IPC/overlay/overlay-luckfox-ubuntu/etc/rc.local
  4. Rebuild: Run ./build.sh firmware. Your file will permanently overwrite the default version in the generated foxbuntu.img.

8.4 Modifying Kernel Options (Deep Dive)

To change low-level hardware support (e.g., enabling a specific USB Wi-Fi adapter or Docker support):

  1. Configure: Run ./build.sh kernelconfig inside the SDK root. This opens the visual ncurses menu (menuconfig).
  2. Select: Navigate to Device Drivers or Kernel Features and toggle options (Spacebar).
    • [*] Built-in: Compiled into the boot.img. Always available.
    • <M> Module: Compiled as a .ko file. Must be loaded via modprobe.
  3. Save: Exit and Save. This updates the .config file.
    • Tip: To make this change permanent for all future builds, copying this .config to sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig is often required if you run mrproper.
  4. Build:
    • Run ./build.sh kernel to compile the changes.
    • Run ./build.sh firmware to package the new modules into the image.

8.5 Updating Foxbuntu Sources

If the femtofox repo updates:

  1. git pull inside femtofox.
  2. Re-run the synchronous rsync commands (Step 4.3).
  3. Rebuild: Kernel -> RootFS -> Firmware -> Image.

8.6 Removing Meshtastic Customizations

Foxbuntu comes with Meshtastic pre-integrated. You can remove it either by modifying the build script (cleanest) or by cleaning up a built image.

Method 1: Prevent Installation (Recommended during Build)

The femtofox.chroot script (run in Step 5) conducts the installation. Editing this script before you run it is the best way to get a clean image.

  1. Open femtofox/environment-setup/femtofox.chroot.
  2. Comment out the Repository & Install:
    # echo "Adding Meshtastic repository..."
    # add-apt-repository -y ppa:meshtastic/beta
    # DEBIAN_FRONTEND=noninteractive apt install -y ... meshtasticd
  3. Comment out Google/Python Dependencies:
    # pip3 install requests ... meshtastic ...
  4. Comment out Control Repo:
    # echo "Cloning Control for Meshtastic..."
    # git clone https://github.com/pdxlocations/control.git /opt/control

Method 2: Manual Removal from Workspace (Pre-Image Build)

If you have already synced the Foxbuntu files (Step 4.3) but haven't built the firmware yet (Step 4.6), you can remove the files directly from the output directory to prevent them from being packaged.

  1. Delete Service and Config Files:

    # System Services & Configs
    rm sysdrv/out/rootfs_uclibc_rv1106/etc/avahi/services/meshtasticd.service
    rm -rf sysdrv/out/rootfs_uclibc_rv1106/etc/meshtasticd/
    rm -rf sysdrv/out/rootfs_uclibc_rv1106/etc/systemd/system/meshtasticd.service.d/
    
    # Helper Scripts
    rm sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-meshtasticd-*
    rm sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/packages/control_for_meshtastic.sh
    
    # Web Interface Components
    rm sysdrv/out/rootfs_uclibc_rv1106/var/www/html/cgi-bin/update-meshtastic.cgi
  2. Edit Start-up Scripts: Edit sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh and remove:

    systemctl enable meshtasticd

Method 3: Remove from Existing Image (Post-Flash/Chroot)

If you have already built the image (or are using a pre-built one), you must manually remove the components.

  1. Enter Chroot (see Section 5.2).
  2. Remove Files:
    rm -rf /usr/local/bin/femto-meshtasticd-config.sh
    rm -rf /usr/local/bin/packages/control_for_meshtastic.sh
    rm -rf /etc/systemd/system/meshtasticd.service.d
    rm -rf /etc/avahi/services/meshtasticd.service
    rm -rf /etc/meshtasticd/
    rm -rf /opt/control
  3. Updates Startup Script: Edit /usr/local/bin/femto-runonce.sh and remove the line:
    systemctl enable meshtasticd
  4. Uninstall Package:
    apt remove meshtasticd
    rm /etc/apt/sources.list.d/meshtastic-ubuntu-beta-jammy.list
    apt autoremove

Flashing: You can now flash foxbuntu.img to your SD card using dd, Balena Etcher, or Raspberry Pi Imager.

#!/bin/bash
echo "Inside chroot environment..."
echo "tmpfs /run tmpfs rw,nodev,nosuid,size=32M 0 0" | tee -a /etc/fstab
echo "Removing netdevice rules..."
ln -s /dev/null /etc/udev/rules.d/80-net-setup-link.rules
echo "Setting up kernel modules..."
# These specific versions might need updating depending on the kernel build
touch /lib/modules/5.10.160/modules.order
touch /lib/modules/5.10.160/modules.builtin
depmod -a 5.10.160
if [[ $? -eq 2 ]]; then echo "Error depmod failed..."; fi
echo "Setting localtime to UTC..."
rm /etc/localtime
ln -sf /usr/share/zoneinfo/UTC /etc/localtime
echo "Installing essential packages..."
apt update
DEBIAN_FRONTEND=noninteractive apt install -y --option Dpkg::Options::="--force-confold" \
linux-firmware wireless-tools git \
openssl libssl-dev evtest screen avahi-daemon \
telnet fonts-noto-color-emoji chrony software-properties-common \
lsof spi-tools samba vim mtd-utils jq rsync sudo
if [[ $? -eq 2 ]]; then echo "Error installing packages..."; fi
DEBIAN_FRONTEND=noninteractive apt upgrade -y --option Dpkg::Options::="--force-confold"
echo "Setting hostname to foxbian..."
echo "foxbian" | tee /etc/hostname > /dev/null
echo "Disabling unnecessary services..."
systemctl disable apt-daily.timer
systemctl disable apt-daily-upgrade.timer
systemctl mask apt-daily.service
systemctl mask apt-daily-upgrade.service
systemctl disable unattended-upgrades
systemctl disable ModemManager.service
systemctl disable getty@tty1.service
echo "Configuring user 'fox'..."
# Rename existing 'pico' user if it exists (standard in some images) or create new
if id "pico" &>/dev/null; then
groupmod -n fox pico
usermod -l fox pico
usermod -d /home/fox -m fox
else
useradd -m -s /bin/bash fox
fi
usermod -aG sudo,input,dialout,tty,video,audio fox
echo "fox ALL=(ALL:ALL) ALL" | tee /etc/sudoers.d/fox > /dev/null
chmod 440 /etc/sudoers.d/fox
# Set default password to 'fox'
echo 'fox:fox' | chpasswd
# Force password change on first login? Uncomment next line if desired
# sudo chage -d 0 fox
# Fix ownership of potential leftover files
find / -group pico -exec chgrp fox {} \; 2>/dev/null
find / -user pico -exec chown fox {} \; 2>/dev/null
# Library fixes for LuckFox compatibility
# Ensure libgcc_s.so and libc_nonshared.a exist where the SDK expects or apps need them
mkdir -p /usr/lib/gcc/arm-linux-gnueabihf/11/
ln -sf /usr/lib/arm-linux-gnueabihf/libgcc_s.so.1 /usr/lib/gcc/arm-linux-gnueabihf/11/libgcc_s.so
ln -sf /usr/lib/arm-linux-gnueabihf/libgcc_s.so.1 /lib/libgcc_s.so.1 || true
ln -sf /usr/lib/gcc/arm-linux-gnueabihf/11/libgcc_s.so /usr/lib/libgcc.so || true
echo "/usr/lib/gcc/arm-linux-gnueabihf/11" | tee -a /etc/ld.so.conf.d/gcc.conf
ldconfig
# Back up libc_nonshared.a because some SDK build scripts might remove it
cp /usr/lib/arm-linux-gnueabihf/libc_nonshared.a /usr/lib/arm-linux-gnueabihf/libc_nonshared.a.keep
echo "Cleaning up..."
apt autoremove -y
apt clean
rm -rf /tmp/*
rm -rf /var/tmp/*
find /var/log -type f -exec truncate -s 0 {} +
: > /root/.bash_history
history -c
echo "Foxbian setup complete."
exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment