QEMU and U-Boot with a u-env
Emulating a Yocto-based system with QEMU is straightforward using the runqemu script from OpenEmbedded-Core. By default, QEMU loads and starts the kernel directly, which is often sufficient. However, for more realistic emulation—especially for tasks like system updates (e.g., SWUpdate)—a bootloader such as U-Boot is necessary. This guide explains how to configure U-Boot with a functional environment in QEMU.
Machine Configuration
To enable U-Boot in QEMU, add the following variables to your machine configuration:
MACHINE = "qemuarm64"
WKS_FILE = "qemuarm64.wks"
# File system type for QEMU
IMAGE_FSTYPES += "wic.qcow2"
# Use qcow2 file system
QB_DEFAULT_FSTYPE = "wic.qcow2"
# Bootloader binary
QB_DEFAULT_BIOS = "u-boot.bin"
# Ensure the bootloader is built with the WIC image
do_image_wic[depends] += "u-boot:do_deploy"After building, the runqemu command will start U-Boot.
Setting Up the U-Boot Environment
The U-Boot environment (u-env) can be stored in various locations. In this example, it will be stored as a file in the boot partition with the FAT format.
U-Boot Configuration
Add the following configuration to U-Boot:
# Disable MMC and Flash storage for environment
# CONFIG_ENV_IS_IN_MMC is not set
# CONFIG_ENV_IS_IN_FLASH is not set
# Enable FAT storage for environment
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
CONFIG_ENV_FAT_INTERFACE="virtio"Patch to Load Environment at Boot
For U-Boot versions before 2025, apply the following patch to initialize virtio before loading the environment from an ext4 or FAT partition:
From 147f2dfe6f67ab1534ac50b27a8545602c138ab0 Mon Sep 17 00:00:00 2001
From: Fiona Klute <fiona.klute@gmx.de>
Date: Wed, 1 May 2024 10:54:09 +0200
Subject: [PATCH] Init virtio before loading ENV from EXT4 or FAT
Specifying a file in an EXT4 or FAT partition on a virtio device as
environment location failed because virtio hadn't been initialized by
the time the environment was loaded. This patch mirrors commit
54ee5ae84191 ("Add SCSI scan for ENV in EXT4 or FAT") in issue and
fix, just for a different kind of block device.
The additional include in include/virtio.h is needed so all functions
called there are defined, the alternative would have been to include
dm/device.h separately in the env/ sources.
Upstream-Status: Backport
Checkpatch suggests using "if (IS_ENABLED(CONFIG...))" instead of
"#if defined(CONFIG_...)", I'm sticking to the style of the existing
code here.
Signed-off-by: Fiona Klute <fiona.klute@gmx.de>
CC: Joe Hershberger <joe.hershberger@ni.com>
CC: Bin Meng <bmeng.cn@gmail.com>
CC: Rogier Stam <rogier@unrailed.org>
---
env/ext4.c | 5 +++++
env/fat.c | 5 +++++
include/virtio.h | 1 +
3 files changed, 11 insertions(+)
diff --git a/env/ext4.c b/env/ext4.c
index 47e05a48919..99b61c9eeab 100644
--- a/env/ext4.c
+++ b/env/ext4.c
@@ -32,6 +32,7 @@
#include <ext4fs.h>
#include <mmc.h>
#include <scsi.h>
+#include <virtio.h>
#include <asm/global_data.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -151,6 +152,10 @@ static int env_ext4_load(void)
if (!strcmp(ifname, "scsi"))
scsi_scan(true);
#endif
+#if defined(CONFIG_VIRTIO)
+ if (!strcmp(ifname, "virtio"))
+ virtio_init();
+#endif
part = blk_get_device_part_str(ifname, dev_and_part,
&dev_desc, &info, 1);
diff --git a/env/fat.c b/env/fat.c
index 3172130d75d..d87a47b1001 100644
--- a/env/fat.c
+++ b/env/fat.c
@@ -18,6 +18,7 @@
#include <fat.h>
#include <mmc.h>
#include <scsi.h>
+#include <virtio.h>
#include <asm/cache.h>
#include <asm/global_data.h>
#include <linux/stddef.h>
@@ -134,6 +135,10 @@ static int env_fat_load(void)
if (!strcmp(CONFIG_ENV_FAT_INTERFACE, "scsi"))
scsi_scan(true);
#endif
+#if defined(CONFIG_VIRTIO)
+ if (!strcmp(ifname, "virtio"))
+ virtio_init();
+#endif
#endif
part = blk_get_device_part_str(ifname, dev_and_part,
&dev_desc, &info, 1);
diff --git a/include/virtio.h b/include/virtio.h
index 062a24630ce..8113a59d795 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -21,6 +21,7 @@
#define __VIRTIO_H__
#include <virtio_types.h>
+#include <dm/device.h>
#include <linux/bitops.h>
#include <linux/bug.h>
#define VIRTIO_ID_NET 1 /* virtio net */
User Space U-Env Configuration
For user space access, configure the /etc/fw_config.env file with the correct path and environment size:
# Block device name Device offset Env. size
/boot/uboot.env 0x0000 0x40000WKS File Example
Here is a simple WKS file to build the WIC image:
bootloader --ptable gpt
part /boot --source bootimg-partition --ondisk vda --fstype=vfat --label boot --active --align 1024
part --source rootfs --use-uuid --ondisk vda --fstype=ext4 --label rootfs --part-name rootfs --align 1024First Boot
Once configured, the environment is written to the file and persists across reboots:
U-Boot 2024.01 (Jan 08 2024 - 15:37:48 +0000)
DRAM: 1 GiB
Core: 51 devices, 14 uclasses, devicetree: board
Flash: 64 MiB
Loading Environment from FAT... Unable to read "uboot.env" from virtio0:1...
In: serial,usbkbd
Out: serial,vidconsole
Err: serial,vidconsole
Bus xhci_pci: Register 8001040 NbrPorts 8
Starting the controller
USB XHCI 1.00
scanning bus xhci_pci for devices... 3 USB Device(s) found
Net: eth0: virtio-net#32
Hit any key to stop autoboot: 0
=> saveenv
Saving Environment to FAT... OKCustom environment
The default bootcmd should be replaced to correctly boot on the root filesystem. For example:
bootcmd=
fatload virtio 0:1 ${kernel_addr_r};
setenv bootargs root=PARTLABEL=rootfs;
booti ${kernel_addr_r} - ${fdtcontroladdr}The default environment can be overridden using a text file in the U-Boot source at /board/emulation/qemu-arm/. The qemu-arm.env file is used by default, but you can specify a different file using the CONFIG_ENV_SOURCE_FILE configuration.