Embedded Linux · Buildroot · QEMU ARM

Buildroot Execution Manual

A comprehensive academic and professional guide for configuring, building, deploying, and validating a Buildroot-based embedded Linux system on the Bootlin QEMU ARM environment.

Single-file responsive HTML
Configure
Build
Deploy
Validate
Purpose

What this presentation covers

Execution focused
1

Correct Buildroot configuration

Target architecture, external toolchain, kernel, packages, filesystem images, and root password.

2

Complete command sequence

Commands are provided step by step from cloning Buildroot to booting from SD.

3

Debugging and validation

Includes verification commands, login recovery, shadow-file issues, and runtime checks.

The goal is not only to make the system boot, but to understand each generated component and validate it systematically.
Mental model

Buildroot is a system generator

Buildroot
.config
Toolchain
Kernel
Packages
RootFS
Images
Target
System

Manual rootfs

Useful for learning and experimentation. You manually copy files, libraries, scripts, and web pages.

Buildroot rootfs

Reproducible and automated. The system is generated from configuration and package selections.

Directory map

Working paths used in this lab

Use exact paths
PROJECT=~/Desktop/embedded-linux-qemu-labs
BOOT=$PROJECT/bootloader
BUILDROOT=$PROJECT/buildroot/buildroot
ROOTFS=$PROJECT/buildroot-rootfs
TINY=$PROJECT/tinysystem/nfsroot
SD=$BOOT/sd.img
When a command depends on the current directory, the slide explicitly shows the cd command first. Do not run deployment commands from an unknown directory.
SD card architecture

The deployment layout

Partition 1

FAT
Stores zImage, vexpress-v2p-ca9.dtb, and U-Boot environment.

Partition 2

SquashFS
Read-only root filesystem. Stable, compressed, and appropriate for embedded systems.

Partition 3

ext4
Writable data partition for persistent files if needed.

U-Boot → FAT p1 → zImage + DTB
Kernel → SquashFS p2 → root filesystem
Runtime data → ext4 p3 when needed
Toolchain inspection

Know the toolchain before configuring Buildroot

arm-linux-gcc --version

/home/ahmed/x-tools/arm-training-linux-musleabihf/bin/arm-linux-gcc \
  --version

Example result

If the version is 14.3.0, choose External toolchain gcc version = 14.x.

Why this matters

Buildroot validates the external toolchain. A wrong GCC version selection stops the build early.

C library and headers

Verify sysroot, C library, and kernel headers

arm-linux-gcc -print-sysroot

ls $(arm-linux-gcc -print-sysroot)/lib

find $(arm-linux-gcc -print-sysroot)/usr/include \
  -name version.h | grep linux
In this setup, the C library is musl, and the kernel headers series used by the external toolchain is 6.1.x.
Step 1

Create the Buildroot workspace

cd ~/Desktop/embedded-linux-qemu-labs
mkdir -p buildroot
cd buildroot
Keeping Buildroot inside the lab directory makes all later commands predictable and avoids path mistakes.
Step 2

Clone Buildroot

git clone https://gitlab.com/buildroot.org/buildroot.git
cd buildroot
If the clone already exists, do not clone again into the same directory. Enter the existing directory and run git status.
Step 3

Select the tested Buildroot release

git checkout 2026.02.1

Why not random master?

A release tag is stable and easier to reproduce during labs and grading.

Kernel note

With this release, the generated Linux kernel in your successful build was 6.19.12.

Step 4

Open Buildroot configuration

make menuconfig
Use arrow keys to navigate, Space to select features, Enter to open menus, and Save before exiting.
Target options

Configure the ARM Cortex-A9 target

Menu itemValue
Target ArchitectureARM (little endian)
Target Architecture Variantcortex-A9
Enable NEON SIMD extensionenabled
Enable VFP extensionenabled
Target ABIEABIhf
Floating point strategyVFPv3-D16
Toolchain

Use the external crosstool-NG toolchain

Menu itemValue
Toolchain typeExternal toolchain
ToolchainCustom toolchain
Toolchain path/home/ahmed/x-tools/arm-training-linux-musleabihf
External toolchain gcc version14.x
Kernel headers series6.1.x
C librarymusl
SSP supportenabled
C++ supportenabled
Kernel

Enable Linux kernel build

Menu itemValue
Linux Kernelenabled
Kernel versionlatest / 6.19 with 2026.02.1
Kernel configurationUsing in-tree defconfig
Defconfig namevexpress
Build a Device Tree Blobenabled
In-tree DTS file namesarm/vexpress-v2p-ca9
The path arm/vexpress-v2p-ca9 is required with newer kernel trees because the DTS file is under arch/arm/boot/dts/arm/.
Packages

Select required target packages

Target packages  --->
  BusyBox
  System tools  --->
    [*] htop

  Audio and video applications  --->
    alsa-utils
      [*] alsamixer
      [*] speaker-test
    mpd
      [*] alsa
      [*] vorbis
      [*] tcp sockets
    mpd-mpc
htop is useful for comparison and system inspection. For the assignment, students should still implement their own Mini-HTOP logic from /proc.
BusyBox

Enable BusyBox httpd

make busybox-menuconfig
Networking Utilities  --->
  [*] httpd
After saving, Buildroot will generate BusyBox with the httpd applet enabled.
Login

Set a root password cleanly

make menuconfig
System configuration  --->
  [*] Enable root login with password
  Root password
Selecting “Enable root login with password” is not enough. The password field must contain a real password value, for example root.
Filesystem image

Enable tar root filesystem image

Filesystem images  --->
  [*] tar the root filesystem

The generated rootfs.tar will later be extracted, customized, converted to SquashFS, and written to SD partition 2.

Build

Build the complete system

make -j$(nproc)

Meaning

-j$(nproc) uses all available CPU cores to speed up compilation.

Your working variant

make ARCH=arm CROSS_COMPILE=arm-linux- -j$(nproc)
For Buildroot, make -j$(nproc) is normally enough because target architecture and toolchain are stored in .config.
Output

Inspect generated Buildroot directories

cd ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot/output
ls
build  host  images  staging  target

images

Final artifacts: kernel, DTB, and root filesystem images.

target

Runtime root filesystem tree before image generation.

host / staging

Build tools and development sysroot used during compilation.

Verify images

Check kernel, DTB, and rootfs

ls -lh images
file images/zImage
rootfs.tar
zImage
vexpress-v2p-ca9.dtb

images/zImage: Linux kernel ARM boot executable zImage (little-endian)
These files confirm that Buildroot generated the deployable system components.
Verify packages

Confirm httpd and htop were built

cd ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot

find output/target -name "htop"
find output/target -name "httpd"

grep -n "CONFIG_HTTPD=y" output/build/busybox-*/.config
output/target/usr/bin/htop
output/target/usr/sbin/httpd
890:CONFIG_HTTPD=y
Prepare deployment rootfs

Extract rootfs.tar safely

cd ~/Desktop/embedded-linux-qemu-labs

sudo rm -rf buildroot-rootfs rootfs.sqsh
mkdir buildroot-rootfs

sudo tar xvf buildroot/buildroot/output/images/rootfs.tar \
  -C buildroot-rootfs

sudo chown -R $USER:$USER buildroot-rootfs
chown -R $USER:$USER buildroot-rootfs
The ownership step is important. Without it, mksquashfs may fail to read files such as /etc/shadow.
Password validation

Verify /etc/shadow before SquashFS

cat buildroot-rootfs/etc/shadow

Good sign

The root line contains a password hash such as root:$5$....

Bad sign

If /etc/shadow is empty or unreadable, login will fail even if menuconfig had a password.

Manual password repair

Fix shadow only if required

cd ~/Desktop/embedded-linux-qemu-labs/buildroot-rootfs

mkpasswd -m sha-256 root

nano etc/shadow
Example root line format:
root:$5$xxxxxxxxxxxxxxxxxxxxxxxxxxxx:0:0:99999:7:::
Only use this step if the shadow file is empty or corrupted. If Buildroot already generated a valid hash, do not edit it manually.
Custom files

Add lab-specific web and assignment files

cd ~/Desktop/embedded-linux-qemu-labs

sudo cp -r tinysystem/nfsroot/www buildroot-rootfs/
sudo cp -r tinysystem/nfsroot/deadlock buildroot-rootfs/

sudo cp tinysystem/nfsroot/usr/bin/mini_htop_v2 \
  buildroot-rootfs/usr/bin/
This is a quick and practical workflow. In production, Buildroot overlays are preferred for reproducibility.
Web test page

Create a minimal index.html if needed

cd ~/Desktop/embedded-linux-qemu-labs

mkdir -p buildroot-rootfs/www/cgi-bin

tee buildroot-rootfs/www/index.html > /dev/null <<'EOF'
<!doctype html>
<html>
<head>
  <title>Buildroot Web Test</title>
</head>
<body>
  <h1>Buildroot httpd is working</h1>
</body>
</html>
EOF
Startup script

Add S99custom to mount filesystems and run httpd

nano buildroot-rootfs/etc/init.d/S99custom
#!/bin/sh

mount -t proc none /proc 2>/dev/null
mount -t sysfs none /sys 2>/dev/null
mount -t devtmpfs devtmpfs /dev 2>/dev/null

ifconfig eth0 192.168.0.100 up

mkdir -p /www
/usr/sbin/httpd -h /www

exit 0
chmod +x buildroot-rootfs/etc/init.d/S99custom
SquashFS

Create the final root filesystem image

cd ~/Desktop/embedded-linux-qemu-labs

rm -f rootfs.sqsh
mksquashfs buildroot-rootfs rootfs.sqsh -noappend
Do not ignore this error: Failed to read file buildroot-rootfs/etc/shadow. If it appears, fix ownership and regenerate SquashFS.
Loop device

Attach the SD image safely

cd ~/Desktop/embedded-linux-qemu-labs/bootloader

LOOP=$(sudo losetup -fP --show sd.img)
echo $LOOP
Do not hard-code /dev/loop6. The loop number changes from machine to machine and from run to run.
Write rootfs

Write SquashFS to partition 2

sudo dd if=~/Desktop/embedded-linux-qemu-labs/rootfs.sqsh \
  of=${LOOP}p2 bs=1M status=progress

sync
Make sure ${LOOP}p2 exists. If not, detach the loop device and attach again with -fP.
Boot partition

Copy kernel and DTB to FAT partition 1

sudo mkdir -p /mnt/boot
sudo mount ${LOOP}p1 /mnt/boot

sudo cp ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot/output/images/zImage \
  /mnt/boot/

sudo cp ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot/output/images/vexpress-v2p-ca9.dtb \
  /mnt/boot/

sync
sudo umount /mnt/boot
sudo losetup -d $LOOP
QEMU script

Run QEMU with audio disabled

cd ~/Desktop/embedded-linux-qemu-labs/bootloader
./run6-qemu.sh
sudo qemu-system-arm \
  -M vexpress-a9 \
  -m 128M \
  -nographic \
  -kernel u-boot/u-boot \
  -sd sd.img \
  -net tap,script=./qemu-myifup \
  -net nic \
  -audio none
The -audio none option removes repeated ALSA host audio warnings and makes the console cleaner.
U-Boot

Set the normal SD boot command

setenv bootcmd 'fatload mmc 0:1 0x61000000 zImage; fatload mmc 0:1 0x62000000 vexpress-v2p-ca9.dtb; bootz 0x61000000 - 0x62000000'

setenv bootargs 'console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=squashfs rootwait rw'

saveenv
reset
Use this command for normal boot and login. Do not save init=/bin/sh unless you intentionally want debug mode.
Debug mode

Temporary login bypass

setenv bootargs 'console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=squashfs rootwait rw init=/bin/sh'
boot
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
ifconfig eth0 192.168.0.100 up
This is for debugging only. It bypasses normal init and login, so startup scripts may not run automatically.
Runtime validation

Test the generated system inside QEMU

login: root
password: root

which htop
which httpd

htop
ps | grep httpd

/usr/sbin/httpd -h /www
If htop opens and httpd is running, the selected Buildroot packages are present and executable.
Host validation

Verify the web server from Ubuntu

curl http://192.168.0.100/index.html

You may also open the same address in the host browser:

http://192.168.0.100/index.html
If this fails, verify that QEMU networking is configured and that eth0 is up inside the target.
Failure scenario

Login still fails after setting password

Most likely cause: /etc/shadow was not copied correctly into SquashFS because of file ownership or permissions.
cat buildroot-rootfs/etc/shadow

mksquashfs buildroot-rootfs rootfs.sqsh -noappend
SymptomFix
Failed to read file .../etc/shadowRun sudo chown -R $USER:$USER buildroot-rootfs then rebuild SquashFS.
root/root failsVerify root hash exists in buildroot-rootfs/etc/shadow.
Failure scenario

Kernel panic: cannot mount root filesystem

VFS: Cannot open root device
Please append a correct root= boot option

Cause

Missing or wrong root= in U-Boot bootargs.

Fix

setenv bootargs 'console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=squashfs rootwait rw'
saveenv
reset
Failure scenario

DTB build error in newer kernels

No rule to make target 'arch/arm/boot/dts/vexpress-v2p-ca9.dtb'
Fix the Buildroot DTS field to:
arm/vexpress-v2p-ca9

The older lab name was vexpress-v2p-ca9, but newer kernel trees store the file under the arm/ subdirectory.

Failure scenario

External toolchain mismatch

ErrorMeaningAction
Expected GCC 12.x, got 14.3.0Wrong GCC version selected.Choose 14.x.
Expected headers 6.12.x, got 6.1.xWrong kernel headers series.Choose 6.1.x.
C library mismatchWrong libc selected.Select musl.
arm-linux-gcc --version
arm-linux-gcc -print-sysroot
Failure scenario

Loop device mistakes

Do not assume the loop device number. Never blindly write to /dev/loop6p2 unless echo $LOOP actually printed /dev/loop6.
losetup -a | grep sd.img

sudo losetup -d /dev/loopX
LOOP=$(sudo losetup -fP --show sd.img)
echo $LOOP
Failure scenario

init=/bin/sh and missing /proc

fopen /proc/stat: No such file or directory
fopen /proc/meminfo: No such file or directory
This happens because init=/bin/sh bypasses normal init scripts.
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
Buildroot overlay note

The cleaner long-term method

Manual copying into buildroot-rootfs works for labs. For a reproducible engineering workflow, use a Buildroot root filesystem overlay.

System configuration  --->
  Root filesystem overlay directories
~/Desktop/embedded-linux-qemu-labs/my-overlay
overlay/etc
overlay/www
overlay/usr/bin
Generated rootfs
Cheat sheet

Full successful command sequence

cd ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot
make busybox-menuconfig
make menuconfig
make -j$(nproc)

cd ~/Desktop/embedded-linux-qemu-labs
sudo rm -rf buildroot-rootfs rootfs.sqsh
mkdir buildroot-rootfs
sudo tar xvf buildroot/buildroot/output/images/rootfs.tar \
  -C buildroot-rootfs
sudo chown -R $USER:$USER buildroot-rootfs
cat buildroot-rootfs/etc/shadow

sudo cp -r tinysystem/nfsroot/www buildroot-rootfs/
sudo cp -r tinysystem/nfsroot/deadlock buildroot-rootfs/
sudo cp tinysystem/nfsroot/usr/bin/mini_htop_v2 \
  buildroot-rootfs/usr/bin/

mksquashfs buildroot-rootfs rootfs.sqsh -noappend
Cheat sheet

Deploy to SD and boot

cd ~/Desktop/embedded-linux-qemu-labs/bootloader

LOOP=$(sudo losetup -fP --show sd.img)
echo $LOOP

sudo dd if=~/Desktop/embedded-linux-qemu-labs/rootfs.sqsh \
  of=${LOOP}p2 bs=1M status=progress
sync

sudo mkdir -p /mnt/boot
sudo mount ${LOOP}p1 /mnt/boot
sudo cp ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot/output/images/zImage \
  /mnt/boot/
sudo cp ~/Desktop/embedded-linux-qemu-labs/buildroot/buildroot/output/images/vexpress-v2p-ca9.dtb \
  /mnt/boot/
sync
sudo umount /mnt/boot
sudo losetup -d $LOOP

./run6-qemu.sh
Final validation

What success looks like

Boot

The kernel mounts /dev/mmcblk0p2 as SquashFS and reaches login.

Login

root/root works, unless debug mode init=/bin/sh is used.

Applications

htop runs, httpd serves /www, and the browser opens the target web page.

which htop
which httpd
curl http://192.168.0.100/index.html
Closing

Key engineering lessons

Buildroot gives reproducibility

Configuration drives the system. Avoid random manual changes when a reproducible overlay can be used.

Deployment requires discipline

Always verify paths, loop devices, /etc/shadow, kernel image, DTB, and U-Boot bootargs.

A correct embedded Linux workflow is not just “it boots”; it is: build, inspect, deploy, validate, and debug with evidence.