- https://superuser.com/questions/1087859/how-to-quit-the-qemu-monitor-when-not-using-a-gui
- https://raspberrypi.stackexchange.com/questions/119483/how-to-access-to-the-configuration-of-my-raspberry-pi-os-lite
- https://listman.redhat.com/archives/crash-utility/2014-November/005711.html#:~:text=If%20a%20kernel%20has%20been%20configured%20with%20CONFIG_DEBUG_INFO_REDUCED
The following setup assumes you already ran an existing RaspberryPi VM, and know which kernel you want to cross compile. If not, please follow this tutorial : https://interrupt.memfault.com/blog/emulating-raspberry-pi-in-qemu. This is not mandatory, but if you want to compile a custom kernel closely matching a VM one, you will need to run it first and inspect it a bit.
We will try to get as close as possible to the kernel used by the raspios image.
As we want to mimic the kernel from the raspios image, we will need to extract some informations :
$ cat /proc/version
Linux version 6.1.21-v8+ (dom@buildbot) (aarch64-linux-gnu-gcc-8 (Ubuntu/Linaro 8.4.0-3ubuntu1) 8.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023
$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2
We will be using a docker container to keep our host machine clean from apt packages.
Place yourself in a directory dedicated to this project (e.g. emu_raspberry/
).
Let's define a custom package name to isolate each build :
echo 'my_kernel' > /bind/kpack
Launch and access the container :
docker run -d -it --name rasp_compil -w /bind/ -v $(pwd):/bind gcc:10.2
docker exec -it rasp_compil bash
Install common dependencies :
apt-get update
apt-get install -y git bc rsync subversion libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf llvm cpio build-essential
apt-get install -y gcc-aarch64-linux-gnu
https://gist.github.com/azbesthu/3891645#file-get-rpi-kernel-source-sh-L4
Following assumes you are in the container terminal, at following path :
cd /bind/
Clone the kernel and navigate to its root tree :
# Use the technique at https://web.archive.org/web/20240124142754/https://gist.github.com/azbesthu/3891645#file-get-rpi-kernel-source-sh-L4 to determine the kernel source corresponding to the /proc/version informations
git clone --depth 1 -b '1.20230405' https://github.com/raspberrypi/linux.git
cd linux/
Export needed cross compilation variables :
export ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KERNEL=kernel8
Set configuration :
make clean
file .config || make bcm2711_defconfig # You can use an existing .config or make a default one
# Command to extract config from a live Raspberry VM : sudo modprobe configs; zcat /proc/config.gz > /boot/.config
Now, you need to run make menuconfig
and activate the following options, in order. This is not mandatory, but the setup will assume the VM has virtio support (it is much faster) :
- PCI support
- PCI controller drivers
- Generic PCI host controller
- PCI controller drivers
- Device Drivers
- Virtio drivers
- PCI driver for virtio devices
- Virtio balloon driver
- Virtio input driver
- Platform bus driver for memory mapped virtio devices
- Network device support
- Virtio network driver
- Block devices
- Virtio block driver
- Virtio drivers
Based on https://www.qemu.org/docs/master/system/arm/virt.html#linux-guest-kernel-configuration, https://www.kernelconfig.io/config_pci_host_generic and https://stackoverflow.com/a/26890069.
If you want vmlinux :
- Kernel hacking
- Compile-time checks and compiler options
- Debug information
- Rely on the toolchain's implicit default DWARF version
- Debug information
- Compile-time checks and compiler options
Cross compile the kernel :
CUSTOM_KPKG_NAME=$(cat /bind/kpack)
make -j$(nproc) LOCALVERSION="-$CUSTOM_KPKG_NAME" KDEB_PKGVERSION="$(make kernelversion)-1" Image modules dtbs
Export compiled objects :
KSAVE_DIR="/bind/kernel-$CUSTOM_KPKG_NAME"
mkdir -p $KSAVE_DIR/boot/overlays
cp arch/arm64/boot/Image $KSAVE_DIR/boot/kernel8.img
cp arch/arm64/boot/dts/overlays/*.dtbo $KSAVE_DIR/boot/overlays/
cp arch/arm64/boot/dts/broadcom/*.dtb $KSAVE_DIR/boot/
cp vmlinux $KSAVE_DIR/
- https://gist.github.com/G-UK/ee7edc4844f14fec12450b2211fc886e
- https://www.raspberrypi.com/documentation/computers/linux_kernel.html#cross-compiling-the-kernel
- https://medium.com/@wizofe/custom-raspberry-pi-debug-kernel-71661fd123f2
- https://sysprogs.com/VisualKernel/tutorials/raspberry/buildkernel/
- https://blog.matteyeux.com/posts/rpi-kernel-debugging/
- https://www.fdmobileinventions.com/blog/2019/01/18/cross-compiling-the-linux-kernel-for-raspberry-pi/
- https://gist.github.com/azbesthu/3891645
When cross compiling the kernel, many utilities needed to build external modules aren't cross compiled. Doing so, if we compile the kernel with :
make bindeb-pkg -j$(nproc)
Then transfer the .deb (linux-image, linux-headers etc.) to the VM, and install them, we will have x86_64 binaries instead of AArch64 ones. This is a known bug, and forces us to compile our modules outside of the VM (see following arm-cpusysregs example).
Potential patch :
Following assumes you are not in the Docker container, and at the root of the directory dedicated to this project (e.g. emu_raspberry/
).
Setup convenient aliases :
export RASPIOS_IMG_PATH='2023-05-03-raspios-bullseye-arm64.img'
export RASPIOS_IMG_MOUNT_POINT='/mnt/raspi_img/'
alias mount_img="sudo losetup -P /dev/loop0 $RASPIOS_IMG_PATH \
;sudo mkdir -p $RASPIOS_IMG_MOUNT_POINT \
;sudo mount /dev/loop0p2 $RASPIOS_IMG_MOUNT_POINT \
;sudo mount /dev/loop0p1 $RASPIOS_IMG_MOUNT_POINT/boot"
alias umount_img='sudo losetup -d /dev/loop0 ; sudo umount /dev/loop0*'
alias qemu_rasp="qemu-system-aarch64 -machine virt -cpu cortex-a72 -smp 8 -m 1G \
-kernel kernel8.img -append 'root=/dev/vda2 rootfstype=ext4 earlyprintk rw panic=0 console=ttyAMA0 fsck.repair=yes rootwait rootdelay=1 dwc_otg.lpm_enable=0' \
-drive format=raw,file=$RASPIOS_IMG_PATH,if=none,id=hd0,cache=writeback \
-device virtio-blk,drive=hd0,bootindex=0 \
-netdev user,id=mynet,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=mynet \
-nographic"
# For 52 bits VA support (https://github.com/hermit-os/loader/issues/108) : -cpu max
(re-)Download the raspios image, and mount it :
wget https://downloads.raspberrypi.org/raspios_arm64/images/raspios_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64.img.xz
xz -d 2023-05-03-raspios-bullseye-arm64.img.xz
mount_img
Override current kernel, and setup username + password :
CUSTOM_KPKG_NAME=$(cat kpack)
KSAVE_DIR="kernel-$CUSTOM_KPKG_NAME"
sudo cp -r $KSAVE_DIR/boot/* $RASPIOS_IMG_MOUNT_POINT/boot/
cp $KSAVE_DIR/boot/kernel8.img .
# CREDENTIALS : "pi:raspberry"
echo 'pi:$6$rBoByrWRKMY1EHFy$ho.LISnfm83CLBWBE/yqJ6Lq1TinRlxw/ImMTPcvvMuUfhQYcMmFnpFXUPowjy2br1NA0IACwF9JKugSNuHoe0' | sudo tee $RASPIOS_IMG_MOUNT_POINT/boot/userconf
Resize the image, and unmount it :
qemu-img resize ./2023-05-03-raspios-bullseye-arm64.img 16G
umount_img
Launch the emulation :
qemu_rasp
# Connect with "pi:raspberry"
# Recommended
sudo apt-get update
sudo apt-get install -y ntp
umount_img
qemu_rasp
# Connect with "pi:raspberry"
# https://muizidn.medium.com/increase-raspberry-pi-disk-size-in-qemu-d6d33666a930
sudo raspi-config
# advanced options -> expand filesystem
# CTRL + A then X to quit qemu
qemu_rasp
# launch the VM again and quit on login screen, disk has been expanded
This module allows to read AArch64 CPU registers conveniently. See https://github.com/lelegard/arm-cpusysregs.
Following assumes you are in the container terminal, at following path :
cd /bind/linux/ # The previously compiled kernel source
Cross compile the module :
INSTALL_MOD_PATH=../modules make -j$(nproc) modules_install
cd ../
git clone https://github.com/lelegard/arm-cpusysregs
cd arm-cpusysregs/kernel/linux/
make -C /bind/modules/lib/modules/*/build M=$PWD
cd /bind/linux/
Save the compiled module :
CUSTOM_KPKG_NAME=$(cat /bind/kpack)
KSAVE_DIR="/bind/kernel-$CUSTOM_KPKG_NAME"
cp /bind/arm-cpusysregs/kernel/linux/cpusysregs.ko $KSAVE_DIR/
Following assumes you are not in the Docker container, and at the root of the directory dedicated to this project (e.g. emu_raspberry/
).
mount_img
sudo cp $KSAVE_DIR/cpusysregs.ko $RASPIOS_IMG_MOUNT_POINT/root/
umount_img
qemu_rasp
# Following is in the VM
apt-get install -y python3 python-is-python3 build-essential
git clone https://github.com/lelegard/arm-cpusysregs
cd arm-cpusysregs/
make -j$(nproc)
mv apps/sysregs /usr/bin/sysregs
cd /root/
insmod cpusysregs.ko # Previously transferred
sysregs -a
Sample values :
TCR_EL1 00400034-F5567516
TPIDRRO_EL0 00000000-00000000
TPIDR_EL0 000003FF-B8826B80
TPIDR_EL1 FFFFFE00-36C00000
TTBR0_EL1 00000000-52990000
TTBR1_EL1 1C280000-411D0000
# VM is launchded
# "CTRL+A" then "C" to open qemu console
dump-guest-memory rasp_dump
$ sudo apt-get install -y raspberrypi-kernel-headers
$ gunzip -c /usr/share/doc/raspberrypi-bootloader/changelog.Debian.gz > changelog
$ head changelog
raspberrypi-firmware (1:1.20230405-1) bullseye; urgency=medium
* firmware as of 055e044d5359ded1aacc5a17a8e35365373d0b8b
* Linux version 6.1.21
Goal was to replicate an existing kernel identically. It is not worth the hassle :)
root@raspberrypi:~# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/aarch64-linux-gnu/10/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 10.2.1-6' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=aarch64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=aarch64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-mutex
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.1 20210110 (Debian 10.2.1-6)
- https://stackoverflow.com/a/75800188
- https://gcc.gnu.org/wiki/InstallingGCC
- https://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/
CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc-8 (Ubuntu/Linaro 8.4.0-3ubuntu1) 8.4.0" # from .config
# https://packages.debian.org/bullseye/amd64/gcc-10-aarch64-linux-gnu
mkdir libs_aarch64 && cd libs_aarch64
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/gcc-10-cross-base_10.2.1-6cross1_all.deb
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/libgcc-10-dev-arm64-cross_10.2.1-6cross1_all.deb
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/gcc-10-aarch64-linux-gnu-base_10.2.1-6cross1_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/cpp-10-aarch64-linux-gnu_10.2.1-6cross1_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/gcc-10-aarch64-linux-gnu_10.2.1-6cross1_amd64.deb
apt-get install -y binutils-aarch64-linux-gnu
apt-get install -y libgcc-12-dev-arm64-cross libasan6-arm64-cross libtsan0-arm64-cross
# dpkg -i --force-depends *.deb
ln -s /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
# https://launchpad.net/ubuntu/+source/gcc-8-cross/33ubuntu2
apt-get update
apt-get install -y binutils-aarch64-linux-gnu
apt-get install -y libgcc-12-dev-arm64-cross libasan6-arm64-cross libtsan0-arm64-cross
mkdir libs_aarch64_8 && cd libs_aarch64_8
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-8-cross/libgcc1-amd64-cross_8.3.0-2cross1_all.deb
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-8-cross/libasan5-arm64-cross_8.3.0-2cross1_all.deb
wget http://launchpadlibrarian.net/465590960/libisl22_0.22.1-1_amd64.deb
wget http://launchpadlibrarian.net/470696662/gcc-8-cross-base_8.4.0-3ubuntu1cross1_all.deb
wget http://launchpadlibrarian.net/470696794/libgcc-8-dev-arm64-cross_8.4.0-3ubuntu1cross1_all.deb
wget http://launchpadlibrarian.net/470696654/gcc-8-aarch64-linux-gnu-base_8.4.0-3ubuntu1cross1_amd64.deb
wget http://launchpadlibrarian.net/470696610/cpp-8-aarch64-linux-gnu_8.4.0-3ubuntu1cross1_amd64.deb
wget http://launchpadlibrarian.net/470696656/gcc-8-aarch64-linux-gnu_8.4.0-3ubuntu1cross1_amd64.deb