This guide shows how to setup a virtualized qemu environment on a Debian-PC for running a RaspberryPi OS.
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
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.
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
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
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
Start the Virtual Machine Manager from the desktop and start/stop/inspect the running guest-OS:
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.