Running meta-iot2000 in QEMU: Yocto Build and Setup Guide

David Krause10 min read
Other TopicSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

1. Overview

The Siemens meta-iot2000 Board Support Package (BSP) is a Yocto/OpenEmbedded meta-layer that targets the SIMATIC IOT2020, IOT2040, and IOT2050 industrial gateways. Although the primary deployment target is the physical IOT2000 hardware, the upstream machine configuration in meta-iot2000-bsp/conf/machine/iot2000.conf ships a complete QB_* variable block that allows the same image to be launched inside a QEMU virtual machine through the Yocto runqemu helper.

This is useful for CI pipelines, headless validation of userspace applications, and developer workstations that do not have a spare IOT2000 unit on the bench. The technique uses the standard Yocto Project QEMU workflow, but the meta-iot2000 layer has historically had rough edges (notably issue #118 in the GitHub tracker) that prevent the QEMU target from working out of the box. This guide consolidates the working configuration, the patches required, and the common failure modes encountered on CentOS 7 and inside the kas-docker wrapper.

Functional boundary: a QEMU-emulated IOT2000 image exposes virtio block, virtio net, and a serial console. It does not replicate the Arduino-shield GPIO, RS232/485 transceivers, the on-board mini-PCIe slot, or the X60/X100/X101 XHCI stack of the real IOT2050. Use QEMU only for userspace, networking, and storage validation.

2. Prerequisites

  1. Host OS. A modern 64-bit Linux distribution. CentOS 7 is supported but ships tar 1.26, which is too old for some Yocto runqemu archive steps; upgrade to tar 1.28+ or build inside the kas-docker container.
  2. QEMU binaries. Install qemu-system-i586 (and the qemu-kvm acceleration helper if the host has hardware virtualization). The host CPU instruction set must include VT-x or AMD-V for KVM acceleration to be effective.
  3. Yocto / kas build host. Install kas (the minimalistic tool from siemens/kas) and the standard Yocto bitbake dependencies (Python 3.8+, gcc, chrpath, diffstat, texinfo, etc.).
  4. meta-iot2000 checkout. Clone the Siemens meta-iot2000 repository and the matching meta-iot2000-example layer (or your own application layer).
  5. Free disk space. A full Poky + meta-iot2000 + IOT2000 SDK build requires 80–120 GB. Allocate at least 16 GB of RAM if you intend to enable KVM.

3. Understanding the meta-iot2000 QEMU Variables

The IOT2000 machine configuration defines the entire QEMU invocation through standard Yocto QB_* variables. These are evaluated by the runqemu script to assemble the final command line.

Variable Default in iot2000.conf Purpose
QB_MEM -m 1024 Allocates 1 GiB of guest RAM. Increase to -m 2048 for IOT2050 workloads.
QB_CPU -cpu pentium Targets a Pentium-class CPU model; matches the x86 baseline of the Quark X1000 (IOT2020/IOT2040) and the Apollo Lake (IOT2050) when software-rendered.
QB_CPU_KVM -cpu pentium CPU model exposed to the guest when KVM acceleration is active. The host CPU brand string is reported to the guest by default; explicit override keeps the IOT2000 ABI consistent.
QB_KERNEL_CMDLINE_APPEND console=ttyS1 Routes the kernel console to the second 16550A UART. This is the UART wired to the QEMU -serial pty backend so the login prompt appears on the host terminal.
QB_OPT_APPEND -serial pty -serial mon:stdio -device virtio-rng-pci Adds a PTY-backed serial port, a monitor-multiplexed stdio serial, and a virtio random number generator (required for entropy inside the guest).
QB_NETWORK_DEVICE -device virtio-net-pci -device virtio-net-pci,netdev=net0,mac=@MAC@ Provisions two virtio-net NICs. The second instance binds to the net0 slirp/tap network defined by the runqemu script, and @MAC@ is templated with a randomized MAC at launch time.
Patch point: do not edit QB_CPU to a model outside the Quark/Apollo Lake feature set unless your application layer can tolerate missing CPUID flags. pentium is the safe default.

4. Configuring the Machine and Example Layers

Two files must be edited to make the QEMU target buildable. The first enables the image-qemuboot class inside the application layer; the second fixes the QB_SYSTEM_NAME lookup that runqemu performs against DEPLOY_DIR.

4.1 Enable image-qemuboot in the example layer

Add the following line to meta-iot2000-example/conf/layer.conf:

IMAGE_CLASSES += "image-qemuboot"

This class is responsible for generating the iot2000.qemuboot.conf file in tmp/deploy/images/iot2000/. Without it, no bootable QEMU artifact is produced and runqemu aborts with "No qemuboot.conf file was generated".

4.2 Set QB_SYSTEM_NAME in the BSP machine configuration

Edit meta-iot2000-bsp/conf/machine/iot2000.conf and append:

QB_SYSTEM_NAME = "qemux86"

The Yocto QEMU infrastructure uses QB_SYSTEM_NAME to locate the matching qemu-system- binary. The default of qemux86 matches the qemu-system-i386/qemu-system-i586 build targets that the meta layer produces. If you build the qemux86-64 image instead, override the variable to qemux86-64 and install the corresponding qemu-system-x86_64 package on the host.

5. Building the QEMU-Boot Image

  1. Enter the kas shell. From the project root, run kas shell (or kas-docker shell on CentOS 7). This exports all required OE_* and BBPATH variables so that runqemu can find the SDK.
  2. Bitbake the example image. Inside the shell, run bitbake example-image. Poky will produce a rootfs and the iot2000.qemuboot.conf file under tmp/deploy/images/iot2000/.
  3. Confirm artifacts. The deploy directory must contain example-image-iot2000.qemux86.wic, example-image-iot2000.ext4, and the iot2000.qemuboot.conf metadata file. The absence of any of these indicates that IMAGE_CLASSES was not picked up by the build.
  4. Inspect qemuboot.conf. The file must begin with a [ section header such as [empty] or [wic]. runqemu reports "file does not contain section headers" if the file is a bare copy of iot2000.conf; this is the most common error when the file is generated by hand.

6. Launching runqemu from the kas Shell

The recommended invocation is:

runqemu kvm slirp nographic
Option Effect
kvm Enables KVM hardware acceleration. The QEMU binary uses /dev/kvm and runs guest code directly on the host CPU. Drop the flag on hosts without virtualization support to fall back to software emulation (significantly slower).
slirp Userspace networking provided by QEMU's built-in SLIRP stack. No tun/tap device is required, which means runqemu does not need sudo on the host.
nographic Disables the SDL/VNC display and routes the virtual framebuffer to the host terminal. Combine with QB_KERNEL_CMDLINE_APPEND = "console=ttyS1" to land at a root login prompt.

If KVM is unavailable, omit it:

runqemu slirp nographic

To attach a second shell for the QEMU monitor (useful for live debugging, register inspection, and device add/remove), launch runqemu kvm slirp nographic sdl with a graphical environment, or connect a telnet client to the monitor PTY printed at boot.

7. Resolving Common Build and Runtime Errors

7.1 "qemu-system-i586: command not found"

The meta layer assumes the existence of qemu-system-i586 on PATH even when KVM is in use; the wrapper script uses the command name to confirm support. On Debian/Ubuntu, install qemu-system-x86 (which provides the qemu-system-i386 and qemu-system-x86_64 symlinks). On RHEL/CentOS, install qemu-kvm and create a compatibility symlink:

sudo ln -s /usr/libexec/qemu-kvm /usr/local/bin/qemu-system-i586

7.2 "No qemuboot.conf file was generated"

Cause: IMAGE_CLASSES += "image-qemuboot" was added to the wrong layer, or the build was run from outside the kas shell. Re-run the full bitbake example-image cycle after verifying the line lives in meta-iot2000-example/conf/layer.conf. Clean the build with bitbake -c cleanall example-image if necessary.

7.3 "qemuboot.conf does not contain section headers"

Cause: the file was generated by manually copying iot2000.conf into the deploy directory. Delete the manual file and re-run bitbake example-image; the image-qemuboot class writes a proper INI-style configuration with [empty] or [wic] sections.

7.4 "tar: unknown option --transform" on CentOS 7

Cause: CentOS 7 ships tar 1.26, which lacks the --transform switch used by runqemu to rewrite archive paths. Either upgrade tar from the EPEL repository, or use the kas-docker wrapper to obtain a toolchain-newer build environment. Running the build natively on a modern host (Ubuntu 22.04+, Debian 12+, Fedora 38+) is the preferred path.

7.5 "runqemu: could not find deploy directory" inside kas-docker

Cause: the host working directory is mounted into the container, but the deploy path resolves inside the container filesystem. Confirm that the TMPDIR selected by local.conf lives under the same path that is bind-mounted into the container. Issue #118 in the meta-iot2000 tracker documents this regression introduced in commit 6dc0791.

8. Verifying the Emulated Environment

  1. Login prompt. After runqemu prints the boot sequence, the guest should reach a Poky (Yocto Project Reference Distro) ... ttyS1 login banner. Log in as root with no password (default for example-image).
  2. Filesystem check. Run df -h / to confirm the virtio block device is mounted. The size should match the value of IMAGE_ROOTFS_SIZE in the example layer.
  3. Network check. Run ip a and confirm that eth0 (or enp0s3 depending on the kernel) has a SLIRP-issued 10.0.2.0/24 address. ping -c 3 10.0.2.2 validates the gateway; ping -c 3 8.8.8.8 validates outbound connectivity through the host's NAT.
  4. Entropy check. Run cat /proc/sys/kernel/random/entropy_avail; values above 1000 confirm the virtio-rng-pci device is wired correctly. OpenSSL and SSH key generation rely on this.
  5. Serial console. From a second terminal, list /dev/pts/ on the host to find the PTY assigned to -serial pty. A separate screen /dev/pts/N 115200 session exercises the auxiliary UART.

9. QEMU Emulation Limitations vs Real IOT2000 Hardware

The QEMU target is a software approximation of the IOT2000 platform. Before relying on it for validation, audit the following gaps:

Subsystem Real IOT2000 behaviour QEMU behaviour
Arduino-shield GPIO (IOT2020/IOT2040) 24 GPIO lines via PCA9555 I2C expander Absent; no device tree node, no /dev/gpiochip
RS232/RS485 transceivers Accessed via /dev/ttySC0..SC3 Only /dev/ttyS0 and /dev/ttyS1 exposed
mini-PCIe / mSATA PCIe root port enumerated Not present
Real-time clock RTC + NTP from network virtio-rtc substitute; some chrony/ntpd configurations misbehave
Watchdog IOT2050 I210-style watchdog i6300esb QEMU watchdog only
Trusted Platform Module Discrete TPM 2.0 on IOT2050 Optional swtpm, off by default
Implication: any test that exercises the Arduino connector, RS485 termination, or the IOT2050's X60/X100/X101 PCIe lanes must run on physical hardware. QEMU is suitable for userspace application logic, network protocols, OPC UA, MQTT, and disk I/O validation only.

10. Network and Serial Configuration Reference

The default QB_NETWORK_DEVICE line provisions two virtio-net devices. The first is reserved for QEMU's internal management plane; the second binds to net0 for host-to-guest traffic. When slirp is selected, the guest receives the following by default:

guest IP:  10.0.2.15/24
gateway:   10.0.2.2
DNS:       10.0.2.3
host port: 2222 -> guest :22 (SSH forward)

To override, pass a tap backend by replacing slirp with net in the runqemu command line and pre-creating tap0 with the desired address range. To pin the guest MAC address for predictable DHCP reservations, replace @MAC@ in iot2000.conf with a literal MAC (must be locally administered, unicast):

QB_NETWORK_DEVICE = "-device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56"

The two serial devices can be re-targeted for scripted testing:

QB_OPT_APPEND = "-serial tcp:127.0.0.1:4444,server,nowait -serial mon:stdio -device virtio-rng-pci"

This forwards the kernel console to a TCP socket on port 4444, which is useful when integrating the QEMU boot with pytest-based CI runners.

11. Field-Proven Workflow Summary

  1. Clone meta-iot2000 and the example layer.
  2. Install qemu-system-x86, qemu-kvm, and a modern tar on the build host.
  3. Append IMAGE_CLASSES += "image-qemuboot" to the example layer's layer.conf.
  4. Append QB_SYSTEM_NAME = "qemux86" to the machine configuration.
  5. Run kas build (or kas shell followed by bitbake example-image).
  6. Run runqemu kvm slirp nographic from the kas shell.
  7. Validate userspace, network, and disk behaviour against the verification checklist in Section 8.
  8. Reserve final integration testing for physical IOT2000 hardware — the Arduino shield, RS485, and PCIe lanes are not emulated.

Which machine name should I set in QB_SYSTEM_NAME for the IOT2050?

Use qemux86-64 if your machine configuration in meta-iot2000-bsp is iot2050 with a 64-bit tune. The default qemux86 corresponds to the 32-bit IOT2020/IOT2040 image. Make sure the matching qemu-system-x86_64 binary is on the host PATH before invoking runqemu.

Why does my qemuboot.conf file not have section headers?

The file is produced by the image-qemuboot BitBake class. If you copied iot2000.conf into the deploy directory manually, the file has no INI section header and runqemu aborts. Delete the manual copy, ensure IMAGE_CLASSES += "image-qemuboot" is set in the example layer, and re-run bitbake example-image.

How do I fix the "tar: unrecognized option --transform" error on CentOS 7?

CentOS 7 ships tar 1.26, which does not implement the --transform option used by runqemu. Either install tar 1.28+ from EPEL or run the entire build inside kas-docker, which bundles a newer toolchain. The cleanest path is to migrate the build host to Ubuntu 22.04 LTS, Debian 12, or Fedora 38+.

Can I test Arduino-shield GPIO behaviour inside QEMU?

No. The Arduino-shield connector on the IOT2020/IOT2040 (and the X60 connector on the IOT2050) is wired to GPIO expanders, RS232/RS485 transceivers, and analog inputs that have no QEMU equivalent. Use QEMU only for userspace, networking, and storage validation; verify any code that touches the Arduino pins on physical hardware.

Why does runqemu need kvm and slirp together?

kvm enables hardware virtualization, which executes guest instructions directly on the host CPU and yields near-native boot times. slirp provides userspace NAT networking, which avoids the tun/tap device and the sudo requirement on the host. Combining them gives the fastest path that still works in a restricted CI container without privileged network operations.

Back to blog