This is a little guide to emulate the Raspbian operating system for ARM on QEMU with network connectivity.

I found most resources on this outdated, relying on broken links, and lacking the steps for network access, so I reviewed them and streamlined the process.

Long story short

If you haven’t yet, install QEMU on your system. On my Arch machine this means

sudo pacman -S qemu qemu-arch-extra bridge-utils

on Debian based

sudo apt-get install kvm qemu bridge-utils

Then, clone my qemu-raspbian-network repository, download a raspbian image and launch qemu-pi.sh

git clone https://github.com/nachoparker/qemu-raspbian-network.git cd qemu-raspbian-network wget https://downloads.raspberrypi.org/raspbian_lite_latest -O raspbian_lite_latest.zip unzip rasbian_lite_latest.zip sudo ./qemu-pi.sh 2017-01-11-raspbian-jessie-lite.img # correct to real name

If you want network access, edit qemu-pi.sh line 30 and set

NO_NETWORK=0

If you do this, the script will setup a bridge called br0 on your enp3s0 interface, and restore your routes after QEMU exits. If you want to modify this behaviour, change the relevant lines in the script.

NO_NETWORK=1 # set to 1 to skip network configuration IFACE=enp3s0 # interface that we currently use for internet BRIDGE=br0 # name for the bridge we will create to share network with the raspbian img

Now your Raspbian image will have network connectivity, so you can SSH to it, and apt-get from it.

For QEMU to have access to the network bridge configuration, this needs to be in /etc/sudoers . Access with sudo visudo.

Cmnd_Alias QEMU=/usr/bin/ip,/usr/bin/modprobe,/usr/bin/brctl %kvm ALL=NOPASSWD: QEMU

If you ever need more disk space, you can

qemu-img resize 2017-01-11-raspbian-jessie-lite.img +2G

When you are done modifying the image, you can dd it to an SD card and run it directly on a Raspberry Pi.

Remember to use <CTRL><ALT>g in order to regain mouse control on QEMU.

Long story long

The Raspbian operating system is a Debian based GNU/Linux distribution that targets the Raspberry Pi board.

It can be convenient to play around with the OS without the need for a Rasberry Pi or an SD card. For this, we want to run it on the QEMU virtual machine.

The original Raspberry Pi has an ARM11 (ARMv6) processor, RPi2 has an ARM Cortex-A7, and RPi3 has an ARM Cortex-A53. I recommend going easy for the arm1136 or arm1176 . To see what your qemu-system-arm can emulate run

$ qemu-arm -cpu help Available CPUs: arm1026 arm1136 arm1136-r2 arm1176 arm11mpcore arm926 arm946 cortex-a15 cortex-a7 cortex-a8 cortex-a9 cortex-m3 cortex-m4 cortex-r5 pxa250 pxa255 pxa260 pxa261 pxa262 pxa270-a0 pxa270-a1 pxa270 pxa270-b0 pxa270-b1 pxa270-c0 pxa270-c5 sa1100 sa1110 ti925t any

The problem is that the kernel that ships with Raspbian is taylored for the Raspberry Pi board, which is not supported by QEMU.

For this reason, the kernel needs to be patched and cross-compiled in order to be run on the ARM Versatile development board, which is supported by QEMU. I included a modified kernel 4.4.34 is in my repo.

After this, it is a matter of invoking qemu with the -kernel option to replace the one that comes with Raspbian, and the -M versatilepb option to specify the emulated hardware.

Then, we are only left with the task of interfacing the block devices that raspbian expects, which are of the form mmcblk0 to what it will receive from the QEMU virtual block device, which come on the form sda . That is what the following lines of the script do

cat > tmpmnt/etc/udev/rules.d/90-qemu.rules <<EOF KERNEL=="sda", SYMLINK+="mmcblk0" KERNEL=="sda?", SYMLINK+="mmcblk0p%n" KERNEL=="sda2", SYMLINK+="root" EOF

In order to achieve network connectivity, it is common to use TUNTAP with QEMU.

TUNTAP creates a virtual network interface that can be accesed by a hypervisor. All packets sent to this virtual interface will appear on the hypervisor’s network stack and viceversa, allowing communication with the virtual machine through the regular network interface.

Therefore, we will include both the real interface and the TUNTAP interface on a network bridge, which will cause any packet sent to the bridge from outside to appear on both interfaces, and any packet sent from inside will appear as if it originates on the bridge.

QEMU requires hooks on /etc/qemu-ifup and /etc/qemu-ifdown to manage the TUNTAP interface, which is dealt with by the following lines taken from the Arch wiki.

cat > /etc/qemu-ifup <<EOF #!/bin/sh echo "Executing /etc/qemu-ifup" echo "Bringing up \$1 for bridged mode..." sudo /usr/bin/ip link set \$1 up promisc on echo "Adding \$1 to $BRIDGE..." sudo /usr/bin/brctl addif $BRIDGE \$1 sleep 2 EOF cat > /etc/qemu-ifdown <<EOF #!/bin/sh echo "Executing /etc/qemu-ifdown" sudo /usr/bin/ip link set \$1 down sudo /usr/bin/brctl delif $BRIDGE \$1 sudo /usr/bin/ip link delete dev \$1 EOF

Cross-compile or get a newer kernel

The first ones to share this work as far as I know were Xecdesign, for the 3.10.25 kernel. At some point, their website went down, so Dhruv Vyas rescued the kernel patches and has been providing newer kernels as well as cross-compiling instructions on his github.

Code

#!/bin/bash # Run a raspbian image in qemu with network access # Tested with 2017-01-11-raspbian-jessie.img (and lite) # # Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com> # GPL licensed (see end of file) * Use at your own risk! # # Usage: # qemu-pi.sh 2017-01-11-raspbian-jessie.img # or any other image # # Notes: # If NO_NETWORK=0 it will include your network interface on a bridge # with the same gateway and routes, and restore it when exiting qemu # # If NO_NETWORK=1 (default), that configuration will have to be done manually # in order to obtain network access inside raspbian # # It requires a modified kernel image for qemu. (variable $KERNEL) # # It enables SSH on the image (but have to login once on lite version) # # For the network bridge configuration, this needs to be in /etc/sudoers # Cmnd_Alias QEMU=/usr/bin/ip,/usr/bin/modprobe,/usr/bin/brctl # %kvm ALL=NOPASSWD: QEMU IMG=$1 KERNEL=kernel-qemu-4.4.34-jessie NO_NETWORK=1 # set to 1 to skip network configuration IFACE=enp3s0 # interface that we currently use for internet BRIDGE=br0 # name for the bridge to share network with the raspbian img MAC='52:54:be:36:42:a9' # comment this line for random MAC (annoying if on DHCP) # sanity checks test -f $IMG && test -f $KERNEL || { echo "$IMG or $KERNEL not found"; exit; } [[ "$IFACE" == "" ]] || [[ "$BRIDGE" == "" ]] && NO_NETWORK=1 # some more checks [[ "$NO_NETWORK" != "1" ]] && { IP=$( ip a | grep "global $IFACE" | grep -oP '\d{1,3}(.\d{1,3}){3}' | head -1 ) [[ "$IP" == "" ]] && { echo "no IP found for $IFACE"; NO_NETWORK=1; } type brctl &>/dev/null || { echo "brctl is not installed"; NO_NETWORK=1; } modprobe tun &>/dev/null grep -q tun <(lsmod) || { echo "need to tun module" ; NO_NETWORK=1; } } # network configuration [[ "$NO_NETWORK" != "1" ]] && { test -f /etc/qemu-ifup && cp -nav /etc/qemu-ifup /etc/qemu-ifup.bak test -f /etc/qemu-ifdown && cp -nav /etc/qemu-ifdown /etc/qemu-ifdown.bak cat > /etc/qemu-ifup <<EOF #!/bin/sh echo "Executing /etc/qemu-ifup" echo "Bringing up \$1 for bridged mode..." sudo /usr/bin/ip link set \$1 up promisc on echo "Adding \$1 to $BRIDGE..." sudo /usr/bin/brctl addif $BRIDGE \$1 sleep 2 EOF cat > /etc/qemu-ifdown <<EOF #!/bin/sh echo "Executing /etc/qemu-ifdown" sudo /usr/bin/ip link set \$1 down sudo /usr/bin/brctl delif $BRIDGE \$1 sudo /usr/bin/ip link delete dev \$1 EOF chmod 750 /etc/qemu-ifdown /etc/qemu-ifup chown root:kvm /etc/qemu-ifup /etc/qemu-ifdown IPFW=$( sysctl net.ipv4.ip_forward | cut -d= -f2 ) sysctl net.ipv4.ip_forward=1 ROUTES=$( ip r | grep $IFACE ) BRROUT=$( echo "$ROUTES" | sed "s=$IFACE=$BRIDGE=" ) brctl addbr $BRIDGE brctl addif $BRIDGE $IFACE ip l set up dev $BRIDGE ip r flush dev $IFACE ip a a $IP dev $BRIDGE echo "$BRROUT" | tac | while read l; do ip r a $l; done precreationg=$(ip tuntap list | cut -d: -f1 | sort) ip tuntap add user $USER mode tap postcreation=$(ip tuntap list | cut -d: -f1 | sort) TAPIF=$(comm -13 <(echo "$precreationg") <(echo "$postcreation")) [[ "$MAC" == "" ]] && printf -v MAC "52:54:%02x:%02x:%02x:%02x" \ $(( RANDOM & 0xff)) $(( RANDOM & 0xff )) $(( RANDOM & 0xff)) $(( RANDOM & 0xff )) NET_ARGS="-net nic,macaddr=$MAC -net tap,ifname=$TAPIF" } # prepare the image SECTOR1=$( fdisk -l $IMG | grep FAT32 | awk '{ print $2 }' ) SECTOR2=$( fdisk -l $IMG | grep Linux | awk '{ print $2 }' ) OFFSET1=$(( SECTOR1 * 512 )) OFFSET2=$(( SECTOR2 * 512 )) mkdir -p tmpmnt mount $IMG -o offset=$OFFSET1 tmpmnt touch tmpmnt/ssh # this enables ssh umount tmpmnt mount $IMG -o offset=$OFFSET2 tmpmnt cat > tmpmnt/etc/udev/rules.d/90-qemu.rules <<EOF KERNEL=="sda", SYMLINK+="mmcblk0" KERNEL=="sda?", SYMLINK+="mmcblk0p%n" KERNEL=="sda2", SYMLINK+="root" EOF umount -l tmpmnt rmdir tmpmnt &>/dev/null # do it qemu-system-arm -kernel $KERNEL -cpu arm1176 -m 256 -M versatilepb $NET_ARGS \ -no-reboot -append "root=/dev/sda2 panic=1" -drive format=raw,file=$IMG \ # restore network to what it was [[ "$NO_NETWORK" != "1" ]] && { ip l set down dev $TAPIF ip tuntap del $TAPIF mode tap sysctl net.ipv4.ip_forward="$IPFW" ip l set down dev $BRIDGE brctl delbr $BRIDGE echo "$ROUTES" | tac | while read l; do ip r a $l; done } # License # # This script is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This script is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this script; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330, # Boston, MA 02111-1307 USA

This was tested on Arch Linux (qemu-arm version 2.8.0), and only some parts on a Debian container.

In the next post we will do something useful with this.