This guide shows how to setup a virtualized qemu environment on a Debian-PC for running a RaspberryPi OS.

Prerequisits

Install on Debian host:

debian@pc:~/$ sudo apt-get update && sudo apt-get install qemu-system-arm virt-manager libvirt-daemon-system libnss-libvirt lvm2 bridge-utils

Get latest iso image of the Raspberry Pi OS from the Raspberry Pi Foundation:

debian@pc:~/$ wget https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2022-01-28/2022-01-28-raspios-bullseye-armhf-lite.zip

Get the latest qemu RaspberrPi kernel image and the dtb's from github:

debian@pc:~/$ git clone https://github.com/dhruvvyas90/qemu-rpi-kernel

Copy the qemu-rpi-kernel directory into /var/lib/libvirt/images/:

debian@pc:~/$ sudo cp -a qemu-rpi-kernel /var/lib/libvirt/images/

Add the user which should run the virtual machine to the groups libvirt and libvirt-qemu:

debian@pc:~/$ sudo usermod -a -G libvirt debian
debian@pc:~/$ sudo usermod -a -G libvirt-qemu debian

Create Logical volume containing Raspberry Pi OS

First lets occupy a dedicated partition on the physical disk for the virtual machine. This can be done by using gnome-disk-utility or gparted. The size should be around 17GB which is enough for the most development effort. For our example we are using /dev/sdc3. Create a volume group on top of our new partition /dev/sdc3:

debian@pc:~/$ sudo vgcreate raspi_vg /dev/sdc3
  Physical volume "/dev/sdc3" successfully created.
  Volume group "raspi_vg" successfully created

Create a logical volume with size 4G, name raspi_dev on top of the volume group raspi_vg which shall contain the kernel and the OS-image:

debian@pc:~/$ sudo lvcreate -L 4G -n raspi_dev raspi_vg
  Logical volume "raspi_dev" created.

Check if device of logical volume is created successfully:

debian@pc:~/$ ls -la /dev/raspi_vg/raspi_dev
lrwxrwxrwx     7 root root  10 Mär 16:35  /dev/raspi_vg/raspi_dev -> ../dm-0

debian@pc:~/$ sudo lvscan
 ACTIVE            '/dev/raspi_vg/raspi_dev' [4,00 GiB] inherit

Set group of logical volume to libvirt:

debian@pc:~/$ sudo chgrp libvirt /dev/raspi_vg/raspi_dev

Now transfer Raspberry Pi OS to logical volume:

debian@pc:~/$ unzip 2022-01-28-raspios-bullseye-armhf-lite.zip
debian@pc:~/$ sudo dd if=2022-01-28-raspios-bullseye-armhf-lite.img of=/dev/raspi_vg/raspi_dev bs=16M

libvirt is now able to boot from the logical volume instead of using the image file.

Create new virtual machine domain

First configure that users of group libvirt are allowed to call virt-install:

debian@pc:~/$sudo mkdir /etc/qemu; sudo touch /etc/qemu/bridge.conf 
debian@pc:~/$ sudo sh -c 'echo "allow all" > /etc/qemu/bridge.conf'

Set super user id for the qemu-bridge-helper:

debian@pc:~/$ sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper

Install a new domain:

debian@pc:~/$ sudo virt-install --name rpios --virt-type qemu --arch aarch64 --machine raspi3b --os-variant debian10 --vcpus 4 --memory 4096 --graphics spice --disk /dev/raspi_vg/raspi_dev,type=block,device=disk,target.bus=virtio --video vga --boot 'dtb=/home/stephan/workspace/qemu-rpi-kernel/native-emulation/dtbs/bcm2710-rpi-3-b.dtb,kernel=/home/stephan/workspace/qemu-rpi-kernel/kernel-qemu-5.10.63-bullseye,kernel_args=rw earlyprintk loglevel=8 console=ttyAMA0,115200 dwc_otg.lpm_enable=0 root=/dev/vda2 panic=1' --events on_reboot=destroy,on_poweroff=destroy  --network bridge,source=virbr0,model=virtio --console type=tcp,source.mode=bind,source.host=127.0.0.1,source.service=2445 --autostart

This creates a new domain file /etc/libvirt/qemu/rpios.xml:

<domain type='qemu'>
  <name>rpios</name>
  <uuid>b6c0990b-dcde-45f2-813e-de92508bedc0</uuid>
  <memory unit='KiB'>262144</memory>
  <currentMemory unit='KiB'>262144</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <os>
    <type arch='armv6l' machine='versatilepb'>hvm</type>
    <kernel>/home/debian/qemu-rpi-kernel/kernel-qemu-5.10.63-bullseye</kernel>
    <cmdline>root=/dev/vda2 panic=1</cmdline>
    <dtb>/home/debian/qemu-rpi-kernel/versatile-pb-bullseye-5.10.63.dtb</dtb>
    <boot dev='hd'/>
  </os>
  <cpu mode='custom' match='exact' check='none'>
    <model fallback='allow'>arm1176</model>
  </cpu>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>destroy</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-system-arm</emulator>
    <disk type='block' device='disk'>
      <driver name='qemu' type='raw' cache='none' io='native'/>
      <source dev='/dev/raspi_vg/raspi_dev'/>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
    </disk>
    <controller type='pci' index='0' model='pci-root'/>
    <controller type='virtio-serial' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </controller>
    <interface type='bridge'>
      <mac address='52:54:00:c9:11:fb'/>
      <source bridge='virbr0'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
    </interface>
    <serial type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445'/>
      <protocol type='raw'/>
      <target port='0'/>
    </serial>
    <console type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445'/>
      <protocol type='raw'/>
      <target type='serial' port='0'/>
    </console>
    <channel type='spicevmc'>
      <target type='virtio' name='com.redhat.spice.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <graphics type='spice' autoport='yes'>
      <listen type='address'/>
      <image compression='off'/>
    </graphics>
    <sound model='ich6'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </sound>
    <audio id='1' type='spice'/>
    <video>
      <model type='vga' vram='16384' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </video>
  </devices>
</domain>

As alternative the domain could be defined by the xml and the call of virsh define:

debian@pc:~/$ virsh -c qemu:///system define ./rpios.xml

Start virtual machine

debian@pc:~/$ virsh -c qemu:///system start rpios

Enable ssh instead using telnet and change the hostname of the guest-OS to a name, which should not interfere with other local network names like rpios:

debian@pc:~/$ telnet localhost 2445
raspberrypi login: pi
Password: raspberry

pi@raspberrypi:~$ sudo systemctl enable ssh.service
pi@raspberrypi:~$ sudo systemctl start ssh.service
pi@raspberrypi:~$ sudo vi /etc/hostname
rpios
pi@raspberrypi:~$ sudo vi /etc/hosts
127.0.0.1        rpios

pi@raspberrypi:~$ sudo systemctl restart networking.service 

Network configuration

An already installed dnsmasq on the host interferes the dnsmasq instance startet by libvirt. If possible, uninstall or disable any dnsmasq instance.

By the help of the package libnss-libvirt the guests-OS (Raspi) desired DNS-name via DHCP/dnsmasq configured in the /etc/libvirt/qemu/networks/default.xml could be resolved by changing the hosts /etc/nsswitch.conf by adding libvirt into the order of name-resolvers:

debian@pc:~/$ sudo nano /etc/nsswitch.conf
hosts:       files libvirt dns .....

After that, name resolution on the host should work immediately on a running guest-OS:

debian@pc:~/$ ping rpios
PING rpios (192.168.122.10) 56(84) bytes of data.
64 bytes from rpios.local (192.168.122.10): icmp_seq=1 ttl=64 time=1.21 ms

Or setup password-less login via ssh:

debian@pc:~/$ ssh-copy-id pi@rpios
pi@rpios's password: 
Number of key(s) added: 2
Now try logging into the machine, with "ssh 'pi@rpios'" and check to make sure that only the key(s) you wanted were added.
debian@pc:~/$ ssh pi@rpios
Linux rpios 5.10.63 #1 Thu Dec 16 11:31:22 GMT 2021 armv6l
pi@rpios:~ $ ping venus
PING venus (192.168.1.205) 56(84) bytes of data.
64 bytes from venus.lan (192.168.1.205): icmp_seq=1 ttl=64 time=0.724 ms

Using Virtmanager

Start the Virtual Machine Manager from the desktop and start/stop/inspect the running guest-OS:

Bildschirmfoto%20vom%202022-03-16%2019-56-31

Some settings can be established, e.g. the autostart behaviour. Further more the domain-XML could be edited by hand in the details.

Thanks to the authors of grimore.org and linuxconfig.org.

Previous Post Next Post