About

The Raspberry Pi can be virtualized using libvirt and qemu-system-arm just by running the RaspiOS image directly from libvirt. This guide installs the Raspberry Pi image to an LVM volume and boots directly from disk with the help of the Raspberry Pi kernel and the device tree blob with the help of libvirt. The guide assumes that there exists a bridge currently configured to be used by multiple virtual machines.

Obtaining the Kernel Image and the Device Tree Blob

Download the kernel image and the device tree blob from:

git clone https://github.com/dhruvvyas90/qemu-rpi-kernel

and place the resulting qemu-rpi-kernel directory in /var/lib/libvirt/images/.

Next, download the Raspberry Pi image corresponding to the kernel from the official Raspberry Pi repository:

wget https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-01-12/2021-01-11-raspios-buster-armhf-lite.zip

Now with the kernel image, the DTB and the RaspiOS image, RaspiOS system can be emulated under libvirt. Since this will be a permanently install, create a logical volume to hold the RaspiOS image:

lvcreate -L 32G -n rpi32.lan vms

where:

  • 32G is the size of the RaspiOS image to be installed to disk,
  • rpi32.lan is the name of the domain,
  • vms is the volume group that holds the logical volumes

Now the Raspberry Pi image downloaded previously can be transferred to the LVM logical volume:

unzip 2021-01-11-raspios-buster-armhf-lite.zip
dd if=2021-01-11-raspios-buster-armhf-lite.img of=/dev/vms/rpi32.lan bs=16M

such that libvirt will boot the image directly from hard-drive instead of booting the image file.

Aside the flexibility, LVM allows logical volumes to be cached such that a greater perfomance is to be expected compared to reading the image file off the drive.

Defining a Domain

Create a file named rpi32.lan with the following contents:

<domain type='qemu' id='24'>
  <name>rpi32.lan</name>
  <uuid>d5cd64d6-fdcf-46fd-958b-063af33d04cb</uuid>
  <memory unit='KiB'>262144</memory>
  <currentMemory unit='KiB'>262144</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch='armv6l' machine='versatilepb'>hvm</type>
    <kernel>/var/lib/libvirt/images/qemu-rpi-kernel/kernel-qemu-4.19.50-buster</kernel>
    <cmdline>root=/dev/vda2 panic=1 console=ttyAMA0 console=ttyS0</cmdline>
    <dtb>/var/lib/libvirt/images/qemu-rpi-kernel/versatile-pb-buster.dtb</dtb>
    <boot dev='hd'/>
  </os>
  <cpu mode='custom' match='exact' check='none'>
    <model fallback='forbid'>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/vms/rpi32.lan'/>
      <backingStore/>
      <target dev='hda' bus='virtio'/>
      <alias name='virtio-disk0'/>
    </disk>
    <controller type='pci' index='0' model='pci-root'>
      <alias name='pci'/>
    </controller>
    <interface type='bridge'>
      <mac address='a2:f3:32:99:d3:af'/>
      <source bridge='br0'/>
      <target dev='vnet5'/>
      <model type='virtio'/>
      <alias name='net0'/>
    </interface>
    <serial type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445' tls='no'/>
      <protocol type='telnet'/>
      <target port='0'/>
      <alias name='serial0'/>
    </serial>
    <console type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445' tls='no'/>
      <protocol type='telnet'/>
      <target type='serial' port='0'/>
      <alias name='serial0'/>
    </console>
  </devices>
  <seclabel type='dynamic' model='dac' relabel='yes'>
    <label>+0:+0</label>
    <imagelabel>+0:+0</imagelabel>
  </seclabel>
</domain>

and then import the domain using the libvirt command line:

virsh -c qemu:///system define rpi32.lan

The configuration defines the following for the RaspiOS install:

  • a logical block device created previously in the last section at /dev/vms/rpi32.lan to which RaspiOS will be installed,
  • the kernel image downloaded in the previous section and placed at /var/lib/libvirt/images/qemu-rpi-kernel/kernel-qemu-4.19.50-buster as well as the Raspberry Pi DTB at /var/lib/libvirt/images/qemu-rpi-kernel/versatile-pb-buster.dtb
  • creates a virtual console as a TCP server listening on port 2445 and redirects all console output to the TCP server via the kernel parameters console=ttyAMA0 console=ttyS0,
  • defines a PCI network device using the virtio driver and attaches the network device to the br0 bridge

First Start and Connecting to the Virtual Machine

The virtual machine can now be started:

virsh -c qemu:///system start rpi32.lan

and telnet can be used to connect over loopback to the virtual machine console:

telnet localhost 2445

If all goes well, the kernel should be booting and init should start setting up the system services.

Finishing Touches

All of these steps are optional but are mentioned here out of convenience.

Enabling OpenSSH inside the Raspberry Pi Virtual Image

Since the SSH daemon is not enabled by default, the daemon can be enabled by issuing:

systemctl enable ssh

and SSH can be started using:

systemctl start ssh

If necessary, the TCP port can now be closed by removing the following stanza from the rpi32.lan domain:

    <serial type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445' tls='no'/>
      <protocol type='telnet'/>
      <target port='0'/>
      <alias name='serial0'/>
    </serial>
    <console type='tcp'>
      <source mode='bind' host='127.0.0.1' service='2445' tls='no'/>
      <protocol type='telnet'/>
      <target type='serial' port='0'/>
      <alias name='serial0'/>
    </console>

Enable LVM Cache

The Raspberry Pi image can be cached via LVM caching which is documented separately on a different page.

Expand the Filesystem

Since LVM is used, more than likely the filesystem should be expanded to fill the logical volume defined. This can be done inside the virtualized Raspberry Pi OS by issuing:

raspi-config

and following the menus Advanced optionsExpand filesystem. RaspiOS will then expand the filesystem and grant the install more space to work with. A restart is required.

Limitations

Unfortunately using Versatile PB will limit the amount of RAM allocated to the VM to $256MiB$ and the CPU count to a single CPU. Alternatively, an ARM virtual machine can be created using the default virt machine type without the limitations of Versatile PB but without emulating the Raspberry Pi hardware.


libvirt/virtualizing_raspberry_pi.txt · Last modified: 2022/04/19 08:28 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.