Virtualizing the Siemens IOT2040 on QEMU, VMware, and VirtualBox

David Krause18 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: Why Virtualize the SIMATIC IOT2040

The Siemens SIMATIC IOT2040 is an industrial IoT gateway based on the Intel Quark x1020 SoC (32-bit x86, single core @ 400 MHz) with 1 GB DDR3 RAM, two Gigabit Ethernet ports, two USB 2.0 ports, RS232/485, an Arduino Uno R3-compatible shield interface, and a mini-PCIe slot. The device ships with a custom Yocto-based Linux image (Example Image 2.4.x and later) that boots through a 32-bit UEFI firmware. The same image is distributed as a .wic file intended to be flashed to the on-board eMMC via dps or a host-side dd routine.

Virtualizing the gateway offers three practical benefits during development:

  • CI/CD smoke testing of Node-RED, PLCnext, MQTT, OPC UA, or custom C/C++ applications before deploying to physical hardware.
  • Application debugging with gdb-multiarch, strace, and breakpoints that survive a VM snapshot.
  • Training and demo on engineering workstations without consuming a physical IOT2040 unit.

The trade-off is that the IOT2040 is not a standard PC platform. The Intel Quark UEFI is 32-bit, the kernel is built for the pentium target, and the on-board peripherals (Arduino shield GPIOs, RS485 transceiver, mini-PCIe signals) have no native counterpart in VMware Workstation, VirtualBox, or QEMU. The virtual machine is therefore a logical sandbox — the network plane, the user-space runtime, and the systemd services work; the hardware I/O plane does not.

Architectural constraint: Treat the VM as a network and application behavior test bench, not as a hardware-in-the-loop substitute. The Arduino-shield and RS485 I/O cannot be exercised inside a hypervisor.

2. Hardware and Firmware Constraints You Must Respect

Parameter IOT2040 Value VM Implication
CPU architecture Intel Quark x1020, 32-bit, single core Use qemu-system-i386 (or x86_64 with -cpu pentium); create a 32-bit firmware VM
UEFI class 32-bit UEFI (Quark EDK II build) Requires IA32 OVMF, not the default X64 OVMF shipped with most hypervisors
Boot loader GRUB2-efi (IA32) loading a vmlinuz with pentium target Kernel must be i386/i586; 64-bit-only kernels will not boot
RAM 1 GB DDR3 (soldered) Allocate 1024 MB minimum in the VM; 1536 MB is comfortable for Node-RED
Storage 4 GB eMMC on-board Mount the .wic as a raw disk; expect two partitions (EFI + rootfs)
Network 2× Intel i210 / i211 GbE Map eth0/eth1 with virtio-net only; E1000 driver exists in IOT2000 kernel but is not always enabled
USB 2× USB 2.0 host USB passthrough works in QEMU and VMware; ignored in most workflows
Arduino shield I/O SPI, I2C, ADC, GPIO via Quark SoC No emulation possible in any hypervisor; this is the blocking constraint for hardware integration

The single most common reason an IOT2040 image refuses to boot inside a VM is the 32-bit UEFI mismatch. Modern VMware Workstation 15 and VirtualBox 6.x both default to 64-bit UEFI (OVMF X64) or to a 64-bit Hyper-V-compatible firmware, and the IOT2040's 32-bit bootloader cannot be loaded by them. A 32-bit IA32 OVMF firmware blob is required.

3. Understanding the .wic Image

The .wic file is produced by the Yocto wic tool. It is a raw disk image (not a compressed archive) that you can mount, partition, and inspect without writing it to a real device. For the IOT2000 SDK, the layout is:

image-iot2040.wic
├── Partition 1: EFI System Partition (FAT16/32, ~32 MB, type 0xEF)
│   └── EFI/BOOT/BOOTIA32.EFI  (the 32-bit GRUB2 EFI binary)
└── Partition 2: rootfs (ext4, type 0x83, fills remaining space)
    └── /boot/vmlinuz*, /boot/grub/grub.cfg, /lib/modules/...

Inspect it on Linux before launching the VM:

$ fdisk -l image-iot2040.wic
Disk image-iot2040.wic: 1.4 GiB, 1500000000 bytes, 2930000 sectors
Units: sectors of 1 * 512 = 512 bytes

Device                  Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
image-iot2040.wic1      *    0,32,33     16,81,13         2048      67583      65536   32M ef EFI (FAT-12/16/32)
image-iot2040.wic2            16,81,14    1023,81,51      67584    2929663    2862080  1.4G 83 Linux

Loop-mount the rootfs for offline editing (handy for injecting SSH keys or a static network config):

$ sudo losetup -f --show -P image-iot2040.wic
/dev/loop0
$ sudo mount /dev/loop0p2 /mnt/iot
$ ls /mnt/iot/boot
vmlinuz-5.10.0-iot  grub  System.map-5.10.0-iot
$ sudo umount /mnt/iot
$ sudo losetup -d /dev/loop0
Important: the partition table is MBR (msdos), not GPT. This matters when attaching the .wic as a raw disk to a VM that defaults to GPT — VMware and VirtualBox will still boot from MBR, but Windows-side tools that assume GPT may misread the geometry.

4. Prerequisites

Before launching any of the methods below, gather the following:

  • The IOT2040 example image, e.g. image-iot2040.wic (image 2.4 or later), from the official Siemens Industry Online Support portal at support.industry.siemens.com.
  • The Siemens IOT2000 SDK sources if you intend to rebuild a custom image: github.com/siemens/iot2000.
  • An IA32 (32-bit) UEFI firmware blob. The edk2-stable202211 or later tag contains OvmfPkg/Ia32/Ia32X64.fd; for the Quark target, the binary OVMF-IA32.fd from the iot2000-setup project is a known-good choice. The 32-bit OVMF_CODE_IA32.fd distributed by the tianocore/edk2 repository is the canonical source.
  • QEMU 6.x or later with qemu-system-i386 and the edk2-ovmf-ia32 package on Linux.
  • VMware Workstation 15.5.7 or later (15.x) on Windows, or Workstation Pro 16.x/17.x.
  • VirtualBox 6.1.x or 7.0.x with the Extension Pack installed.

Verify the firmware blob is IA32 by checking the PE/COFF header:

$ file OVMF-IA32.fd
OVMF-IA32.fd: MS-DOS executable PE32 executable (EFI) Intel 80386 (DLL), for MS Windows

The Intel 80386 (not x86-64) string is the confirmation you need.

5. Method 1 — QEMU on Linux (Recommended)

QEMU gives the cleanest path because it can load an arbitrary 32-bit OVMF firmware, emulates a generic virtio disk, and exposes a virtio-net-pci device that the IOT2000 kernel recognizes. The Yocto build system also bundles a wrapper script, runqemu, that handles most of the arguments for you.

5.1 Quick path with runqemu

From the Yocto build directory of a built IOT2000 image:

$ source oe-init-build-env
$ runqemu nographic wic
runqemu - INFO - Running /usr/bin/qemu-system-i386...
...

runqemu automatically selects qemu-system-i386, attaches the .wic as a virtio-blk device, and brings up a usermode network. If you want KVM acceleration, your host kernel must expose kvm-intel; the Quark CPU itself is 32-bit but QEMU presents it to a 64-bit KVM process, so KVM works on modern hosts.

5.2 Manual qemu-system-i386 invocation

Use the manual form when you need explicit control of the firmware and network:

qemu-system-i386 \
  -machine q35 \
  -cpu pentium \
  -smp 1 \
  -m 1024 \
  -drive if=pflash,format=raw,readonly=on,file=OVMF-IA32.fd \
  -drive file=image-iot2040.wic,format=raw,if=virtio \
  -netdev user,id=net0,hostfwd=tcp::2222-:22 \
  -device virtio-net-pci,netdev=net0 \
  -serial mon:stdio \
  -nographic

Argument breakdown:

Argument Purpose
-machine q35 Modern chipset with PCIe root; required for virtio-net-pci under a 32-bit firmware
-cpu pentium Matches the Quark x1020 feature set (no PAE required, no SSE3); use pentium2 if you need PAE
-m 1024 Mirrors the physical 1 GB; 1536 is acceptable for development
-drive if=pflash,... Mounts IA32 OVMF as the platform flash; this is what makes UEFI boot possible in a 32-bit guest
hostfwd=tcp::2222-:22 Forwards host port 2222 to guest SSH on 22 for remote access
-device virtio-net-pci Required — the e1000 driver is not always built into the IOT2000 kernel
-serial mon:stdio Routes the IOT2040 serial console to the terminal, useful for boot diagnostics

5.3 Bridged networking in QEMU

For a VM that needs to reach a real LAN (e.g. to talk to a PLC over Modbus/TCP), bridge tap0 to the host interface:

# On the host (root)
ip tuntap add dev tap0 mode tap
ip link set tap0 up
brctl addif br0 tap0    # br0 already bridges eth0

qemu-system-i386 ... \
  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
  -device virtio-net-pci,netdev=net0 \
  -netdev user,id=net1 \
  -device virtio-net-pci,netdev=net1

Use the second virtio-net-pci for an out-of-band management network that always works, even if the bridge loses link state.

6. Method 2 — VMware Workstation 15 on Windows

VMware Workstation supports UEFI firmware, but the bundled firmware is 64-bit. There is no built-in option to switch to IA32 firmware. The reliable workaround is to attach the .wic as a raw disk to a 32-bit firmware-aware virtual machine, and to accept that VMware will report a non-standard firmware version.

6.1 Create the VM

  1. Launch VMware Workstation 15 (or 16/17) and choose Create a New Virtual Machine > Custom (advanced).
  2. Set Hardware compatibility to Workstation 15.x.
  3. Choose I will install the operating system later.
  4. Set Guest OS to Linux > Other Linux 3.x or later kernel 32-bit. The 32-bit selection is critical — it forces VMware to load the IA32 firmware shim instead of the X64 firmware.
  5. Allocate 1 CPU, 1024 MB RAM.
  6. Set the network adapter to NAT initially; switch to Bridged once the VM is reachable.
  7. Skip the virtual disk creation — you will attach the .wic directly.

6.2 Attach the .wic as a raw disk

Create a small seed VMDK descriptor that points at the .wic:

"RW \n" >  rawdisk.vmdk

This two-line VMDK is the standard VMware raw-disk mapping trick documented in the VMware Workstation Pro documentation:

# disk.vmdk
# Extent description
RW 2930000 FLAT "image-iot2040.wic" 0

# The Disk Data Base
#DDB

ddb.virtualHWVersion = "14"
ddb.adapterType = "ide"
ddb.geometry.sectors = "63"
ddb.geometry.heads = "16"
ddb.geometry.cylinders = "2906"

Edit the VM's .vmx file and add:

firmware = "efi"
efi.filename = ""
firmwareBootTimeout = "3000"
ide0:0.present = "TRUE"
ide0:0.fileName = "disk.vmdk"
ide0:0.mode = "independent-nonpersistent"
monitor_control.restrict_backdoor = "TRUE"

firmware = "efi" enables UEFI; the empty efi.filename tells Workstation to fall back to its own built-in UEFI rather than an external firmware file. Combined with the 32-bit guest OS type from step 4, this is the closest VMware gets to IA32 UEFI.

6.3 Boot, network, and known issues

  • On first boot, the GRUB2 menu from the .wic appears; choose the default kernel. If you see UEFI Interactive Shell instead, the firmware did not load GRUB. Re-check that the guest OS type is 32-bit Linux.
  • VMware's vlance, e1000, and vmxnet3 paravirtual NICs may or may not have a driver in the IOT2000 kernel. The safest choice is the e1000 NIC: ethernet0.virtualDev = "e1000". If that does not work, switch to vlance (an AMD PCnet clone, 10 Mbit, present in all IOT2000 kernels).
  • VMware's proprietary vmxnet3 driver is not in the IOT2040 default kernel — do not select it.
  • Power off the VM with Suspend > Power Off rather than Reset; the IOT2040 rootfs uses ext4 without journal on small partitions and a hard reset occasionally leaves it inconsistent.

6.4 Practical recipe (Windows command line)

You can launch the VM from vmware.exe -x with the vmx path once it is configured. For scripting, here is a PowerShell snippet that converts a .wic to a vmdk descriptor and patches the vmx:

$wic = "C:\iot\image-iot2040.wic"
$vmx = "C:\iot\iot2040-vm\iot2040-vm.vmx"
$desc = @"
# Extent description
RW 2930000 FLAT "$wic" 0

# The Disk Data Base
#DDB

ddb.virtualHWVersion = "14"
ddb.adapterType = "ide"
ddb.geometry.sectors = "63"
ddb.geometry.heads = "16"
ddb.geometry.cylinders = "2906"
"@
$desc | Out-File "C:\iot\iot2040-vm\disk.vmdk" -Encoding ASCII
Add-Content $vmx "ide0:0.present = `"TRUE`""
Add-Content $vmx "ide0:0.fileName = `"disk.vmdk`""
Add-Content $vmx "ide0:0.mode = `"independent-nonpersistent`""
Add-Content $vmx "ethernet0.virtualDev = `"e1000`""

7. Method 3 — VirtualBox on Windows

VirtualBox 6.1.x and 7.0.x ship with a 64-bit OVMF firmware in the Extension Pack. There is no first-class IA32 OVMF build in the public VirtualBox distribution, which is why you may have observed the VM dropping to the UEFI Interactive Shell and never finding EFI/BOOT/BOOTIA32.EFI. The fix is to inject an IA32 firmware blob into the VM's NVRAM.

7.1 Create the VM

  1. In VirtualBox Manager, click New and configure:
    • Type: Linux
    • Version: Other Linux (32-bit)
    • Memory: 1024 MB
    • Hard disk: Do not add a virtual hard disk (you will attach a raw disk next)
  2. Open the VM settings, go to System > Motherboard, and tick Enable EFI (special OSes only). In VirtualBox 7.0 this is in System > EFI.
  3. Go to Storage > Controller: IDE and click the second hard-disk icon. Choose Choose existing disk and browse to a small VMDK that points at the .wic (the same descriptor used in the VMware recipe works).
  4. Under Network > Adapter 1, set Attached to: Bridged Adapter (or NAT during initial bring-up). Set Adapter Type: Intel PRO/1000 MT Desktop (82540EM). The virtio-net adapter type is exposed by VirtualBox 6.1+ but the IOT2000 image has no virtio-net driver compiled in, so use 82540EM.

7.2 Replace the firmware with an IA32 OVMF

VirtualBox stores NVRAM as a per-VM file with a variable-size VBoxVDI/VBoxNV format. To substitute IA32 OVMF:

  1. Download a 32-bit OVMF firmware blob (e.g. OVMF-IA32.fd from the tianocore github.com/tianocore/edk2 project).
  2. With the VM powered off, copy the blob into the VM folder: VirtualBox VMs\iot2040-vm\OVMF-IA32.fd.
  3. Edit the iot2040-vm.vbox XML and add to the <ExtraData> section:
    <ExtraDataItem name="VBoxInternal/Devices/pcbios/0/Config/BiosRom" value="OVMF-IA32.fd"/>

On next start, VirtualBox flashes the custom firmware, the GRUB2 BOOTIA32.EFI is found, and the IOT2040 kernel boots normally. If the boot still falls to the UEFI Interactive Shell, list the file system in the shell:

Shell> fs0:
FS0:\> ls EFI\BOOT
BOOTIA32.EFI

If the file is present, manually launch it: FS0:\EFI\BOOT\BOOTIA32.EFI. If the file is not present, the .wic EFI partition is not mounted — the firmware is X64 and cannot read the FAT partition with 32-bit cluster hints. Re-do step 7.2 with a true IA32 OVMF.

7.3 Known VirtualBox quirks

  • VirtualBox's virtio-net adapter presents a PCI vendor/device ID of 1af4:1000; the IOT2040 kernel includes the virtio_net module but the IOT2000 default image-iot2040.wic from Siemens may have it compiled as a module, not loaded automatically. If you need virtio-net, add virtio_net to /etc/modules-load.d/iot2000.conf on the loop-mounted rootfs before booting.
  • The IOT2040 expects eth0 and eth1 interface names. VirtualBox's 82540EM adapter enumerates as enp0s3 under modern systemd's predictable naming. Disable predictable naming by appending net.ifnames=0 biosdevname=0 to the kernel command line in /boot/grub/grub.cfg.
  • VirtualBox Guest Additions are not compatible with the IOT2040 kernel build and must not be installed. Sharing folders with the host is best done through a host-side smb or nfs share instead.

8. Networking in the Virtual IOT2040

Whether you use QEMU, VMware, or VirtualBox, the IOT2040's user-space network configuration expects DHCP on eth0 and a static fallback on eth1. The default /etc/systemd/network/10-eth.network in image 2.4 looks like:

[Match]
Name=eth0

[Network]
DHCP=yes
IPv6AcceptRA=yes

[DHCPv4]
UseHostname=no

If you want the VM to be reachable from the host without DHCP, set a static address in /etc/systemd/network/20-eth-static.network:

[Match]
Name=eth1

[Network]
Address=192.168.7.2/24
Gateway=192.168.7.1
DNS=1.1.1.1

On the host (Linux), set the bridge address to 192.168.7.1/24 with ip addr add 192.168.7.1/24 dev tap0 after starting the QEMU VM. On Windows, the equivalent is a static address on the VirtualBox Host-Only Ethernet Adapter or the VMware VMnet1 interface.

8.1 Verifying the network plane

# Inside the VM, after boot
$ ip -br addr
eth0      UP   192.168.7.2/24
eth1      DOWN

$ ping -c 3 192.168.7.1
PING 192.168.7.1 (192.168.7.1): 56 data bytes
64 bytes from 192.168.7.1: seq=0 ttl=64 time=0.4 ms
...
$ systemctl status systemd-networkd
● systemd-networkd.service - Network Service
     Active: active (running) since ...
MTU pitfall: some QEMU virtio-net configurations default to an MTU of 1500, while the IOT2000's real Intel i210 negotiates 9000 (jumbo frames) if the link partner supports it. If your application makes heavy use of OPC UA binary protocol or large Modbus frames, leave the VM MTU at 1500 to match what production code will see on a typical 1 Gbit industrial switch.

9. I/O Limitations: What Cannot Be Virtualized

This is the single most important fact for a project manager: an IOT2040 VM cannot exercise the Arduino shield, RS485, the user LEDs, or the mini-PCIe slot. The Intel Quark SoC multiplexes GPIO, SPI, I2C, and UART into the Arduino Uno R3 connector through a custom firmware; none of this is emulated by QEMU, VMware, or VirtualBox. The application code that calls mraa, libiot2000, or accesses /sys/class/gpio will see no devices.

Concretely:

IOT2040 Feature Available in VM? Alternative
Ethernet eth0/eth1 Yes (with virtio-net or e1000) Use as in production
USB 2.0 host Yes (QEMU/VMware passthrough) Use as in production
RS232 console Yes (QEMU -serial, VMware serial pipe) Use as in production
RS485 half-duplex No Test with a USB-to-RS485 dongle attached via passthrough
Arduino shield SPI/I2C/ADC/GPIO No Stub with a Python fake; unit-test the application logic separately
Mini-PCIe (cellular, Wi-Fi) No Use a USB cellular or Wi-Fi dongle
User LEDs (D5/D6 on the shield) No Visible in production; do not branch application logic on them
Real-time clock (PCF2127) No VM uses the host RTC; expect time skew during suspend
Watchdog (Quark internal) No Stops the kernel; ignore in CI

10. Verification — Proving the VM Is Functional

After the VM is running, walk through this checklist before declaring success:

  1. Boot phase: GRUB2 menu appears within 5 s of UEFI hand-off. If the firmware drops you into a UEFI shell, the firmware is not IA32.
  2. Kernel phase: the Quark-targeted kernel prints Linux version 5.10.0-iot ... (gcc version ...) and the Intel Quark SoC driver probes succeed.
  3. systemd phase: systemd --failed shows zero units. The most common failure is systemd-networkd-wait-online.service timing out before DHCP completes; if that happens, increase TimeoutStartSec=60s in the unit override.
  4. Network phase: ip -br addr shows an IPv4 address on eth0 within 30 s; ping 8.8.8.8 succeeds from the VM.
  5. User space: the IOT2000 SDK's iot2000setup tool reports Platform detected: SIMATIC IOT2040 — this is a strong signal that user-space sees a coherent gateway, even if the on-board peripherals are not present.
  6. Application phase: your Node-RED, PLCnext, or custom daemon starts and serves its protocol (MQTT, OPC UA, Modbus/TCP) on the expected port.

Capture each of these as a CI gate in Jenkins or GitLab CI. A complete QEMU-based smoke test for a hypothetical MQTT gateway looks like this:

#!/bin/bash
set -e
qemu-system-i386 -machine q35 -cpu pentium -m 1024 \
  -drive if=pflash,format=raw,readonly=on,file=OVMF-IA32.fd \
  -drive file=image-iot2040.wic,format=raw,if=virtio \
  -netdev user,id=net0 \
  -device virtio-net-pci,netdev=net0 \
  -serial mon:stdio \
  -nographic -daemonize -pidfile /tmp/iot.pid

# Wait for the guest to come up
for i in {1..30}; do
  sleep 1
  nc -z 127.0.0.1 1883 && break
done

# Smoke test
mosquitto_pub -h 127.0.0.1 -p 1883 -t test -m "ci-ok"
test "$(mosquitto_sub -h 127.0.0.1 -p 1883 -t test -C 1 -W 5)" = "ci-ok"

kill "$(cat /tmp/iot.pid)"
rm -f /tmp/iot.pid

11. Troubleshooting Matrix

Symptom Likely Cause Fix
UEFI Interactive Shell appears, fs0: not found Hypervisor loaded 64-bit OVMF; FAT partition unreadable Replace with IA32 OVMF (QEMU: if=pflash; VirtualBox: VBoxInternal/Devices/pcbios/0/Config/BiosRom; VMware: change guest OS to 32-bit Linux)
GRUB2 loads, kernel panic: kernel requires features not present on this CPU QEMU CPU model is too old or too new (e.g. qemu64 exposes x86_64 flags) Use -cpu pentium or -cpu pentium2
Boot stalls after Waiting for device /dev/disk/by-label/rootfs... VM disk not seen as virtio; kernel waits for the Intel Quark eMMC driver Re-attach the .wic with if=virtio (QEMU) or change controller to virtio-scsi-pci and add virtio_scsi to the initramfs
eth0 never appears NIC type is vmxnet3 or virtio-net with no driver Use e1000; check dmesg | grep -i eth
Network up, but DNS does not resolve systemd-resolved disabled or /etc/resolv.conf empty Enable systemd-resolved and symlink /etc/resolv.conf to /run/systemd/resolve/stub-resolv.conf
VM appears to boot but journalctl -xb shows "Watchdog detected" Real watchdog timer not emulated; kernel fires it on slow QEMU start-up Add nowatchdog to the kernel command line in grub.cfg
Snapshot of VM refuses to resume wic image mounted as independent-nonpersistent but rootfs journaled Switch to persistent mode in VMware, or in QEMU use drive.file.locking=off
VMware 15 BSODs on Windows 10 22H2 when attaching the raw disk Storage controller driver signature mismatch Update to Workstation 15.5.7+ or move to Workstation 17; convert .wic to .vmdk with qemu-img convert -O vmdk and use that instead of a raw-disk descriptor
VirtualBox "VERR_NEM_NOT_AVAILABLE" on Apple Silicon (M1/M2) VirtualBox 6.x is x86-only; the IOT2000 image is x86 Use VMware Fusion 13 (which x86-emulates) or Parallels 18 with the Other Linux 32-bit profile; ARM-native IOT2050 image is the only path on Apple Silicon with full performance

12. Migrating from IOT2040 to IOT2050 — When to Stop Virtualizing

Siemens has replaced the IOT2040 with the SIMATIC IOT2050, which uses an ARM-based TI AM6528 SoC (ARMv8, 64-bit) and a 64-bit UEFI. The IOT2050 ships with a Yocto image based on the github.com/siemens/iot2050 meta-layer and a .wic file with a GPT partition table. Virtualizing the IOT2050 is significantly easier on QEMU with qemu-system-aarch64 and a 64-bit OVMF blob — there is no 32-bit UEFI constraint. If your project is greenfield and does not require the Arduino Uno R3 shield, plan the migration now: the IOT2050 has more RAM, more CPU cores, and a real-time capable PRU-ICSS for industrial protocols, none of which you can faithfully emulate in a 32-bit Quark VM.

For teams that must keep the IOT2040 in production, treat the VM as a fast iteration environment for the application layer — protocol stacks, web dashboards, MQTT brokers, and CI tests. Reserve final integration testing for physical hardware: at least one IOT2040 per developer plus one bench unit for regression.

Can the IOT2040 image be run as-is in VMware Workstation 15 on Windows?

Yes, but you must configure the guest OS as a 32-bit Linux so Workstation loads its IA32 firmware shim, attach the .wic as a raw disk through a VMDK descriptor, and select the e1000 virtual NIC. The default 64-bit UEFI will drop you into the UEFI Interactive Shell because the IOT2040 ships a 32-bit BOOTIA32.EFI GRUB2 binary.

Why does VirtualBox show a UEFI Interactive Shell and not boot the IOT2040 image?

VirtualBox 6.1/7.0 distribute an X64 OVMF firmware only. The IOT2040's 32-bit EFI binary cannot be loaded by 64-bit firmware, so no GRUB is executed. Replace the firmware with a 32-bit OVMF via the VBoxInternal/Devices/pcbios/0/Config/BiosRom extra-data entry, or fall back to a QEMU-based VM that lets you point at an explicit IA32 firmware file.

Is the .wic file a raw disk image, a compressed archive, or a VMDK?

It is a raw, uncompressed disk image produced by the Yocto wic tool. It contains an MBR partition table with a small FAT EFI System Partition and an ext4 rootfs partition. You can inspect it with fdisk -l image-iot2040.wic on Linux or convert it to VMDK with qemu-img convert -O vmdk image-iot2040.wic disk.vmdk.

Do the Arduino shield, RS485, and mini-PCIe ports work in the VM?

No. None of the Intel Quark on-board peripherals (Arduino shield GPIOs/SPI/I2C/ADC, RS485 transceiver, mini-PCIe signals) are emulated by QEMU, VMware, or VirtualBox. The virtual machine is a network and user-space test bench only. Use a physical IOT2040 for hardware integration testing or migrate to the IOT2050, which has a different shield ecosystem.

Which virtual NIC should I attach to the IOT2040 VM for best results?

Use the Intel e1000 (82540EM) in VMware and VirtualBox — the driver is built into every IOT2000 kernel. In QEMU, prefer virtio-net-pci for performance; the virtio_net module is included in the IOT2040 default kernel. Avoid vmxnet3 (no driver) and virtio-net on VirtualBox unless you have explicitly loaded the module on the loop-mounted rootfs.

Back to blog