Emulating a Raspberry Pi with QEMU: the extras

Author's Note: This post has been sitting in draft state since my original post was published, so some details might be a little out of date. I'm publishing it anyway and hoping its helpful for someone!


I recently published a post outlining how to use QEMU to emulate a Raspberry Pi. In that post, we got things working, but not much more than that. Today, I'm covering a few post-install tweaks you might want to work on.

Display

When you run the commands from the previous post you'll see QEMU pop up a UI window with the Pi's display output. While this can be handy if you're constantly interacting with the UI, I find it quite irritating and would prefer an option to disconnect and reconnect to the UI without resetting the Pi. To that end, we can tell QEMU to serve the Pi's raw display output over a VNC connection instead!

By adding a simple -vnc :5 to the command, we tell QEMU to serve the vPi's display over a local-only VNC server on port 5905 (port 5900 + :5 = 5905). You can connect to the session with any VNC-compatible client (such as GNOME Remote Desktop Viewer). Note that since QEMU is the server here (not the vPi), the server is active during boot as well!

qemu-system-arm \
## trimmed for brevity
-net tap,ifname=vnet0,script=no,downscript=no \
-vnc :5 # <- this is the important part!

Scripting

The main thing is that every time you wanted to start the virtual Pi (vPi) you would need to run the same lengthy qemu-system-arm command, and it would block the prompt while doing so). Fortunately, there's a better way!

In my case, I went with a combination of a simple bash script and GNU Screen, a handy utility for running detachable terminal sessions in the background.

So, I created a small bash script (called vpi):

#!/usr/bin/env bash
cd /path/to/work-folder # where your kernel and disk image are!
qemu-system-arm \
## TRIMMED FOR BREVITY

and added it to my PATH. Combined with the trick above, running this script (you may need chmod +x first) will give you a fully backgrounded running vPi.

While this is effective, it still blocks the current console and will kill the vPi if we close it. Instead, I'll preface the whole command with screen -dmS vpi. Thus, the full script will be:

#!/usr/bin/env bash
cd /path/to/work-folder
screen -dmS vpi qemu-system-arm \
-kernel ./kernel-qemu-4.4.34-jessie \
-cpu arm1176 -m 256 -M versatilepb \
-no-reboot -serial stdio \
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \
-hda raspbian.qcow \
-net nic -net user \
-net tap,ifname=vnet0,script=no,downscript=no \
-vnc :5

Now, run chmod +x vpi and run vpi. You'll get a screen session called vpi started in the background that spins up the machine. You can then attach (with screen -r vpi) and detach (with Ctrl-A D) at will to get the serial console, and connect via VNC (on port 5905) at any time.

If you followed the instructions in the last post, you can also run ssh pi@192.168.122.200 to quickly SSH into your new Pi.

Permissions

Note that you might run into some wonky permissions around the /dev/net/tun device if you try and run these scripts not as root. The details of fixing this are a bit complex to get into here, but essentially come down to:

  • Give your user access to or ownership of /dev/net/tun (I'd recommend a udev rules file)
  • Add your user/group to the device with ip using sudo ip tuntap add dev vnet0 mode tap group <group-here>
  • Run the machine, making sure to include -net user

This process can be a little glitchy but does take out the need to run as root.