On a machine running Ubuntu 22.04 (with gcc 11.4.0 and host kernel 6.5.0), install QEMU:
sudo apt-get install qemu qemu-system
Download Linux 6.6.16 from https://cdn.kernel.org and extract the archive. Use a separate build directory too keep the source clean:
cd linux-6.6.16
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
mkdir ../build
make O=../build vexpress_defconfig
make O=../build menuconfig # exit without changes
make -j$(nproc) O=../build bzImage dtbs
The kernel image and device tree appear at:
../build/arch/arm/boot/zImage../build/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb
Quick validatino (expect a panic due to missing rootfs):
qemu-system-arm \
-M vexpress-a9 -smp 4 -m 256M \
-kernel ../build/arch/arm/boot/zImage \
-dtb ../build/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb \
-append "console=ttyAMA0" \
-nographic
Press Ctrl+A then X to stop QEMU.
Root filesystem with BusyBox
Get BusyBox 1.36.1 from https://busybox.net/downloads and build:
tar xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make defconfig
make menuconfig # enable Settings -> Build static binary
mkdir ~/my_rootfs
make install CONFIG_PREFIX=~/my_rootfs
Supplement the directory structure:
cd ~/my_rootfs
mkdir -p etc dev mnt sys tmp proc root lib
cp -a /usr/arm-linux-gnueabi/lib/*.so* ./lib/
Create device nodes:
cd dev
sudo mknod -m 666 console c 5 1
sudo mknod -m 666 null c 1 3
for i in 1 2 3 4; do sudo mknod -m 666 tty$i c 4 $i; done
Write configuration files:
etc/init.d/rcS:
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export LD_LIBRARY_PATH=/lib:/usr/lib
/bin/mount -n -t ramfs ramfs /var
/bin/mount -n -t ramfs ramfs /tmp
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t ramfs none /dev
/bin/mkdir /var/tmp /var/modules /var/run /var/log
/bin/mkdir -p /dev/pts /dev/shm
/sbin/mdev -s
/bin/mount -a
echo "ARM emulation ready."
Mark it executable: chmod +x etc/init.d/rcS.
etc/fstab:
proc /proc proc defaults 0 0
none /dev/pts devpts mode=0622 0 0
mdev /dev ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
tmpfs /mnt tmpfs defaults 0 0
var /dev tmpfs defaults 0 0
ramfs /dev ramfs defaults 0 0
etc/inittab:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
SD card image and final launch
Create a raw disk image and populate it:
cd ~
qemu-img create -f raw disk.img 256M
mkfs.ext4 disk.img
mkdir tmp_mnt
sudo mount -o loop disk.img tmp_mnt
sudo cp -r ~/my_rootfs/* tmp_mnt/
sudo umount tmp_mnt
rmdir tmp_mnt
Start the full system:
qemu-system-arm \
-M vexpress-a9 -m 256M \
-kernel ../build/arch/arm/boot/zImage \
-dtb ../build/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb \
-sd disk.img \
-nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0"
When the prompt appears, standard Linux commands can be executed.