Skip to content

Instantly share code, notes, and snippets.

@Ljohske
Forked from plembo/RPIwithQEMU.md
Last active November 5, 2022 20:32
Show Gist options
  • Save Ljohske/23f2b1fbe9fcd3788dbd97481d1d3c48 to your computer and use it in GitHub Desktop.
Save Ljohske/23f2b1fbe9fcd3788dbd97481d1d3c48 to your computer and use it in GitHub Desktop.
Emulating a Raspberry Pi with QEMU

NB

  • This is just my own notes for me trying the same thing in the future (and probably make the exact same mistake as I did today!), so please do not rely on this too much ...
  • This is a fork of a gist by plembo. Check out there for original instructions. Sorry, but I think not much changes have been made, meaning probably you might not have any particular reason to prefer this gist over the original one :(
  • I tried this on my Alter Linux platform, so I believe most of the notes here would apply to any Arch-based distros (not sure though).

Emulating a Raspberry Pi with QEMU

Goal: Emulate a Raspberry Pi with QEMU in order to run the Raspbian O/S (based on Debian Linux).

The current setup is not ideal. For one thing, the maximum RAM allowed using the "versatile-pb" firmware is 256 Mb. In addition, only the most basic peripherals, a keyboard and mouse, are supported.

A number of articles have been written on this topic. Most are outdated, and the few recent ones are missing key information.

Software Required

  1. QEMU system emulation binaries for ARM processors. On Ubuntu, qemu-system-arm.
  2. Raspbian Stretch with Desktop, disk image. I strongly recommend using the lite version since it is smaller than desktop versions and here we assume this edition to be installed when deciding the virtual disk size.
  3. Latest Debian Stretch kernel from the qemu-rpi-kernel project.

Procedure

  1. Install qemu-system-arm to allow the emulation of devices with arm processors like the Pi.
sudo pacman -S libvirt qemu qemu-arch-extra
  1. Create an emulation project directory, "~/Project/rpitest" to hold the emulation files.
  2. Clone the qemu-rpi-kernel repo to another directory using git.
  3. Follow the instructions in the repo for building a versatile-pb.dtb file for your kernel of choice (or simply use the kernel and matching .dtb file provided by default -- currently kernel-4.9.41-stretch).
  4. Copy the kernel image and matching versatile-pb.dtb file into the project directory (e.g. "rpitest"). Rename the kernel file to "kernel-qemu".
  5. Download the latest Raspbian disk image from raspberrypi.com and unzip into the project directory. Remember that we are using 32-bit emulation and that we will download armhf image.
  6. Find starting sector of the image's second partition using fdisk:
fdisk -l 2018-06-27-raspbian-stretch.img
Disk 2018-06-27-raspbian-stretch.img: 4.5 GiB, 4823449600 bytes, 9420800 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xbd98648d

Device                           Boot Start     End Sectors  Size Id Type
2018-06-27-raspbian-stretch.img1       8192   96663   88472 43.2M  c W95 FAT32 (LBA)
2018-06-27-raspbian-stretch.img2      98304 9420799 9322496  4.5G 83 Linux
  1. Multiply that sector number by 512 and use the result as the offset in mounting the image:
# Don't forget to add the 'rw' option as it is required for editing any file inside the iamge.
sudo mount 2018-06-27-raspbian-stretch.img -o offset=50331648,rw /mnt
  1. Edit the ld.so.preload file to comment out its one and only line:
sudo vi /mnt/etc/ld.so.preload

This will enable devices like keyboards and mice to work in the emulator.

  1. Unmount and rename the file to "rpitest.img".
  2. Convert the raw img file to qcow2 format:
qemu-img convert -f raw -O qcow2 rpitest.img rpitest.qcow2

Starting the virtual machine

Copy the following code into a startup script called something like "rpistart.sh":

#!/usr/bin/env bash
sudo qemu-system-arm -kernel kernel-qemu \
-cpu arm1176 -m 256 \
-M versatilepb -dtb versatile-pb.dtb \
-no-reboot \
-serial stdio \
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \
-hda rpitest.qcow2 \
-net nic -net user \
-net tap,ifname=vnet0,script=no,downscript=no

Login using username "pi" and password "raspberry".

Your machine should be fully functional at this point, and able to reach the Internet over the virtual network (vnet0) created along with it.

NB: Do NOT update the system at this point, as the shipping image is 97% full.

Expand the disk size

To update and do useful work with the system, the root partition (/dev/sda2) will need to be expanded in size. The following steps will accomplish that.

  1. Shut down the virtual machine and run qemu-img to increase the disk size:
# in VM
sudo shutdown now

# on host
qemu-img resize rpitest.qcow2 +5G
  1. Start up the machine again and log in. Run fdisk to delete and then re-create the second partition.
sudo fdisk /dev/sda

The commands to run within fdisk are:

print - to show the current disk layout

Do note the starting sector of the second partition !!! ; This is VERY important as you can break everything down if you just continue with default selection of starting sector (apparently it selects the empty slice of the partition BEFORE /dev/sda1, meaning that you can destroy the whole partition table and boot sector, which results in kernel panic)

d - to delete a partition
2 - choose partition 2
n - create a new partition
p - make it primary
2 - partition number
xxxxx - the starting sector of the original partition 2
Enter - to accept the last sector of the disk as the end of the partition
w - to write all changes to disk
  1. Complete the resizing operation by running resize2fs on the partition:
resize2fs /dev/sda2
  1. Run df -h to confirm the expanded disk size is recognized by the system.
@Ljohske
Copy link
Author

Ljohske commented Mar 22, 2022

By the way, this rpistart.sh is what I ended up with so far:

#!/usr/bin/env bash
sudo qemu-system-arm -kernel kernel-qemu-5.10.63-bullseye \
-cpu arm1176 -m 256 \
-M versatilepb -dtb versatile-pb-bullseye-5.10.63.dtb \
-no-reboot \
-serial stdio \
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \
-hda raspios-lite.qcow2 \
-net nic -net user \
-net tap,ifname=vnet0,script=no,downscript=no

Note that I used some different 'already built' kernels and .dtb file from qemu-rpi-kernel and that I named some directories differently as the original gist does -- yet everything works just fine for me. Maybe comparing this with the original example help you a little bit more in understanding what the shell script is doing.

@Ljohske
Copy link
Author

Ljohske commented Mar 22, 2022

When you are done with all of those things instructed above, you can finally update your emulated Raspberry Pi OS system; however, this can take a loooooooong time to finish (I'm not sure why this takes almost 4-5 mins just to finish apt update, but maybe due to the default mirror settings or my environment setups?)

@Ljohske
Copy link
Author

Ljohske commented Apr 6, 2022

Some other possible references:

  • [https://blog.lazym.io/2021/04/16/Run-ARM-MIPS-Debian-on-QEMU/]
  • [https://gist.github.com/luk6xff/9f8d2520530a823944355e59343eadc1]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment