Skip to content

Running a USB Update in QEMU

This guide covers building a SWUpdate package, placing it on a USB storage device, and triggering a USB update on a ToloMEO QEMU image using QEMU's USB hot-plug mechanism.


Table of Contents


Prerequisites

  • QEMU installed with qemu-system-x86_64 available
  • socat installed on the host:
  • QEMU boot artifacts already available (see meta-tolomeo-qemu/README.md)
  • An image built with update-local in DISTRO_FEATURES (see Configure Update Modes)

Build the Update Package

Build a full SWUpdate package from inside the devcontainer:

kas build kas/tolomeo-qemux86-64_imgen-update-full.yml

The recipe uses the stable,update software selection and targets /dev/vdb via GPT partition swap. After the build completes, the .swu package is collected under:

artifacts/tolomeo-qemux86-64/image-prod/<version>/updates/imgen-update-full/
└── imgen-update-full-tolomeo-qemux86-64.rootfs.swu

Prepare USB Storage

The .swu package must be placed at the root of a FAT filesystem on a USB storage device. Use either a real USB stick or a disk image file.

Option A — real USB stick:

Copy the package to the root of the USB stick:

cp artifacts/tolomeo-qemux86-64/image-prod/<version>/updates/imgen-update-full/imgen-update-full-tolomeo-qemux86-64.rootfs.swu /media/<mount>/
sync

Option B — disk image file:

Create a FAT image and copy the package onto it:

dd if=/dev/zero of=usb-stick.img bs=1M count=128
mkfs.vfat usb-stick.img
mcopy -i usb-stick.img \
  artifacts/tolomeo-qemux86-64/image-prod/<version>/updates/imgen-update-full/imgen-update-full-tolomeo-qemux86-64.rootfs.swu \
  ::imgen-update-full-tolomeo-qemux86-64.rootfs.swu

Prepare the Working Directory

Your working directory should contain the following before launching QEMU:

.
├── bzImage
├── image-prod-tolomeo-qemux86-64.rootfs.ext4
├── qemu-disk.img                               # provisioned disk with certificates
├── shared/                                     # local directory for shared mount
└── usb-stick.img  (or a real block device)

Create the shared directory if it does not exist:

mkdir -p shared

Run QEMU

Launch the VM with the QEMU monitor socket enabled and the USB storage backend declared. The -drive if=none option makes the storage available to QEMU without exposing it to the guest immediately — the hot-plug step in the next section attaches it as a USB device at runtime.

Replace usb-stick.img with your real block device path (e.g. /dev/sde) if using a physical USB stick.

qemu-system-x86_64 \
  -kernel bzImage \
  -append 'root=/dev/vda rw mem=1024M ip=dhcp console=ttyS0 console=ttyS1 oprofile.timer=1 tsc=reliable no_timer_check rcupdate.rcu_expedited=1 swiotlb=0' \
  -drive file=image-prod-tolomeo-qemux86-64.rootfs.ext4,if=virtio,format=raw \
  -drive file=qemu-disk.img,if=virtio,format=raw,id=qemu-disk \
  -drive file=usb-stick.img,if=none,id=usb-stick,format=raw \
  -virtfs local,path=shared,mount_tag=shared,security_model=mapped-xattr \
  -device virtio-net-pci,netdev=net0,mac=52:54:00:12:35:02 \
  -netdev user,id=net0,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23 \
  -object rng-random,filename=/dev/urandom,id=rng0 \
  -device virtio-rng-pci,rng=rng0 \
  -cpu IvyBridge -machine q35,i8042=off \
  -smp 4 -m 1024 \
  -usb -device usb-tablet -usb -device usb-kbd \
  -monitor unix:/tmp/qemu-monitor.sock,server,nowait \
  -serial mon:stdio -serial null -nographic

KVM acceleration: Add -enable-kvm for better performance. This may require sudo depending on your system configuration.

Real block device access: Passing a physical block device (e.g. /dev/sde) requires read access to that device, which typically requires sudo.


Simulate USB Insertion

Once the VM has fully booted, run the following script on the host to hot-plug the USB storage device into the guest. QEMU exposes it as a USB mass storage device and the guest udev rules fire swupdate-usb@<device>.service automatically.

#!/bin/bash

SOCK=/tmp/qemu-monitor.sock

# Wait for the QEMU monitor socket to become available
echo "Waiting for QEMU monitor socket..."
until [ -S "$SOCK" ]; do sleep 1; done
echo "Monitor ready."

# Wait for the guest to finish booting before hot-plugging.
# Increase this value if udev does not fire — the guest may still be initialising.
sleep 15

echo "Hot-plugging USB stick..."
echo 'device_add usb-storage,drive=usb-stick,id=stick0' | socat - "UNIX-CONNECT:$SOCK"
echo "Done. udev should fire swupdate-usb@<device>.service inside the guest."

Save the script (e.g. as hotplug-usb.sh), make it executable, and run it from a second terminal while QEMU is running:

chmod +x hotplug-usb.sh
./hotplug-usb.sh

Monitor the Update

Connect to the running VM via SSH to watch the update progress:

ssh -p 2222 admin@127.0.0.1

Inside the guest, follow the swupdate service log:

journalctl -fu 'swupdate-usb@*'

A successful update writes upgrade_available=1 to the boot environment and reboots the VM into the new image. After the VM restarts, confirm the rollback guard was cleared:

fw_printenv upgrade_available

The value should be unset once the new image has booted and confirmed itself healthy.