Skip to content

Running an OTA Update in QEMU

This guide covers building a SWUpdate package, publishing it through the device management platform, and triggering an OTA streaming update on a ToloMEO QEMU image using natscli from inside the guest.


Table of Contents


Prerequisites

  • QEMU installed with qemu-system-x86_64 available
  • QEMU boot artifacts already available (see meta-tolomeo-qemu/README.md)
  • An image built with update-ota in DISTRO_FEATURES (see Configure Update Modes)
  • The signed .swu package published through the device management platform and marked as a new release for this device

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

Upload the package to the device management platform and mark it as a new release for the target device before proceeding.


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

Create the shared directory if it does not exist:

mkdir -p shared

Run QEMU

The QEMU command for OTA updates is identical to the standalone mode guide — no USB-specific flags are required. Launch the VM:

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 \
  -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 \
  -serial mon:stdio -serial null -nographic

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


Trigger the OTA Update

Once the VM has booted, connect via SSH from a second terminal:

ssh -p 2222 admin@127.0.0.1

Use natscli inside the guest to interact with the swupdate management service. Run GetOTAStatus first to confirm the update is visible to the device before proceeding.

Check update availability:

timestamp=$(date +%s.%3N)
nats pub commands.swupdate.req \
  '[{"bn":"", "t":'"$timestamp"',"n":"GetOTAStatus","vs":"{\"id\":\"swupdate\"}"}]'

Start the download:

timestamp=$(date +%s.%3N)
nats pub commands.swupdate.req \
  '[{"bn":"", "t":'"$timestamp"',"n":"StartOTADownload","vs":"{\"id\":\"swupdate\"}"}]'

Install the downloaded update:

timestamp=$(date +%s.%3N)
nats pub commands.swupdate.req \
  '[{"bn":"", "t":'"$timestamp"',"n":"InstallOTAUpdate","vs":"{\"id\":\"swupdate\"}"}]'

Monitor the Update

In the same SSH session (or a parallel one), subscribe to the events topic to follow the status progression in real time:

nats sub "events.params"

A typical successful run produces the following sequence of events:

[#1] Received on "events.params"
[{"bn": "urn:cpt:device:sn:TVD_0000:", "n": "GetOTAStatus", "vs": "{\"id\": \"swupdate\", \"result\": \"success\", \"status\": \"update_available\"}"}]

[#2] Received on "events.params"
[{"bn": "urn:cpt:device:sn:TVD_0000:", "n": "StartOTADownload", "vs": "{\"id\": \"swupdate\", \"result\": \"success\", \"status\": \"downloading\"}"}]

[#3] Received on "events.params"
[{"bn": "urn:cpt:device:sn:TVD_0000:", "n": "GetOTAStatus", "vs": "{\"id\": \"swupdate\", \"result\": \"success\", \"status\": \"update_ready\"}"}]

[#4] Received on "events.params"
[{"bn": "urn:cpt:device:sn:TVD_0000:", "n": "InstallOTAUpdate", "vs": "{\"id\": \"swupdate\", \"result\": \"success\", \"status\": \"updating\"}"}]

After the updating event the VM reboots into the new image. Once it comes back up, 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.