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.
raspi3bwith 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
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.
According to the qemu documentation, the following is impleted:
- ARM1176JZF-S, Cortex-A7 or Cortex-A53 CPU. I only tested the Cortex-A53 CPU
- 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.
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
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. The
dtbis 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 your
sdcardimage. The append is used for the kernel command line. If you want a serial console make sure you include:
sd $sd_image: Image for the
-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 that
Ctrl+Care 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
-serial stdio -device usb-mouse -device usb-kbd options and
This would automatically enable
-serial stdio and remove the framebuffer. In
Ctrl-C is handled properly.
I tested the following images, with these results:
|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.17.0-aarch64.tar.gz||Only works headless|
For convenicne, I wrote the
This can be used to prepare images and run emulation sessions.
Preparing base image
raspi-emu prep [options] src
Prepares the downloaded image so it can be used as a qemu thin-provisioned image.
--sz=size: Set the base image to the given
qcow2images, create a compressed image.
--qcow2: Create a
qcow2format image. This is the default.
--raw: Create a
--label=name: When creating AlpineLinux images, use
nameas the volume name.
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.
--reize=size: Set the SDCARD image to the given size.
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.
--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.
--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.