Raspberry Pi emulation with Qemu
The idea here is that we use a Desktop PC for developing/debugging Raspberry Pi set-ups using qemu for emulating Rasperrby Pi.
qemu currently supports the following configurations:
- Raspberry Pi Zero and 1A+ (armhf)
- Raspberry Pi 2B (armv7)
- Raspberry Pi 3A+ (aarch64)
- Raspberry Pi 3B (aarch64)
- This is the version I am targetting in this article. I already recycled all my older boards.
- Actually I tried emulating the other configurations but they did not work. Either they failed to boot, or the graphic display wouldn't work.
- So
raspi3b
with 64-bit run-time is the only configuration I was able to succesfully boot.
- NOTE that Raspberry Pi 4 is not supported at the moment.
So, unfortunately the state of things is far from perfect.
Missing display bug
During my tests on the raspi3b
configuration, I was not able to get a working
console. This has to do with this
commit,
which disables the Frame Buffer driver because qemu doesn't seem to
report the display properly. This causes the error to show up on the kernel log:
bcm2708_fb soc:fb: Unable to determine number of FBs. Disabling driver.
Before the commit, the kernel would assume that there was always one display. On the other hand, this only affects use-cases that require display. For headless development, using the serial port works just fine.
For Alpine Linux, the last working Frame Buffer version seems to be
3.16.3-aarch64
. The display did not work for 3.17.0-aarch64
. The 32 bit
3.16.3
would display the Disabling driver.
message but I wasn't able to
boot further than that.
Emulated hardware
According to the qemu documentation, the following is impleted:
- ARM1176JZF-S, Cortex-A7 or Cortex-A53 CPU. I only tested the Cortex-A53 CPU
for the
raspi3b
configuration. - Interrupt controller
- DMA controller
- Clock and reset controller (CPRMAN)
- System Timer
- GPIO controller
- Serial ports (BCM2835 AUX - 16550 based - and PL011)
- Random Number Generator (RNG)
- Frame Buffer : However the Linux kernel does not seem to find it.
- USB host (USBH)
- GPIO controller
- SD/MMC host controller
- SoC thermal sensor
- USB2 host controller (DWC2 and MPHI)
- MailBox controller (MBOX)
- VideoCore firmware (property)
As you can see, no network interface is implemented, so you must use a USB network.
Getting started
The basic command line I am using is:
qemu-system-aarch64 \
-machine raspi3b -cpu cortex-a53 -m 1G -smp 4 -dtb bcm2710-rpi-3-b-plus.dtb \
-kernel $linux_kernel -initrd $linux_initrd -append "$cmdline" \
-sd $sd_image \
-serial stdio \
-usb \
-device usb-mouse -device usb-kbd \
-device usb-net,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22
Command explanation:
qemu-system-aarch64
: emulate a 64-bit ARM system-machine raspi3b -cpu cortex-a53 -m 1G -smp 4 -dtb bcm2710-rpi-3-b-plus.dtb
: Matches the Raspberry Pi model 3B configuration. Thedtb
is a file from the Raspberry Pi boot partition that is normally loaded by the Firmware.-kernel $linux_kernel -initrd $linux_initrd -append "$cmdline"
: Linux related boot configuration. You must provide a kernel and optional initrd files. Usually you would extract them from yoursdcard
image. The append is used for the kernel command line. If you want a serial console make sure you include:console=ttyAMA0,115200
sd $sd_image
: Image for thesdcard
storage-usb
: Enable USB bus. Needed for the emulated console mouse/keyboard and usb network.-serial stdio
: enable a serial console (if you are using the emulated framebuffer. Note thatCtrl+C
are not caught and would kill the emulation.-device usb-mouse -device usb-kbd
: these are used with the virtual framebuffer for providing keyboard and mouse.-device usb-net,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22
: Enable virtual networking using slirp.
If you wish to run a headless (only serial console) configuration, you should
remove the -serial stdio -device usb-mouse -device usb-kbd
options and
just use:
-nographic
This would automatically enable -serial stdio
and remove the framebuffer. In
this configuration Ctrl-C
is handled properly.
Tested OS
I tested the following images, with these results:
Operating System | Status |
---|---|
2020-08-20-raspios-buster-arm64-lite.zip | fully working |
2022-09-22-raspios-bullseye-arm64-lite.img.xz | Only works headless. Default user is not set properly, so the image needs to be modified to inject login credentials |
alpine-rpi-3.16.3-aarch64.tar.gz | fully working |
alpine-rpi-3.17.0-aarch64.tar.gz | Only works headless |
raspi-emu
For convenicne, I wrote the raspi-emu
script:
This can be used to prepare images and run emulation sessions.
Usage:
Preparing base image
raspi-emu prep [options] src
Prepares the downloaded image so it can be used as a base for a qemu thin-provisioned image.
Options:
--sz=size
: Set the base image to the givensize
.-c
|--compress
: Forqcow2
images, create a compressed image.--qcow2
: Create aqcow2
format image. This is the default.--raw
: Create araw
image.--volume=name
: When creating AlpineLinux images, usename
as the volume name. Otherwise a random name is generated.
Formatting SDCARD image
raspi-emu format [options] base dest
Create an SDCARD image to be used for qemu emulation. It will create a thin-provisioned image when possible.
Options:
--resize=size
: Set the SDCARD image to the given size.
Running Emulation
raspi-emu run [options] sdimg
Will boot qemu emulation with the specified SDCARD image. Configuration when possible is read from the boot partition of the SDCARD.
Options:
--vfb-only
: Enable the virtual framebuffer and disables the serial console.--vfb
: Enables the virtual framebuffer. The serial console is kept enabled.--no-vfb
: Disables virtual framebuffer. This is the default.--ttycon
: Enables the serial console for Linux logins. (Default)--no-ttycon
: Disables the serial console for Linux logins.--vnet
: Enable virtual network. (Default)--no-vnet
: Disables hte virtual network.--portfwd
: Enables virtual network. Forwards port 5555 on host to port 22 on VM.--portfwd=rule
: Adds the given port forwarding rule.
Example rule:tcp::5555-:22
--no-portfwd
: Dsiables port forwarding. (Default)--raspi3b
: Emulate a Raspberry Pi Model 3B. (Default)
The default is running headless (only serial console) with networking enabled.