> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs-staging.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enable virtualization

Qualcomm<sup>®</sup> Linux supports hardware-assisted virtualization through KVM (Kernel-based Virtual Machine), which integrates directly into the Linux kernel and runs guest operating systems at near-native performance. Virtual Host Extensions (VHE) are enabled by default on Qualcomm SoCs that implement Armv8.1 or later, allowing the host kernel to run at EL2 while guest kernels and user space run at EL1 and EL0.

## **Supported targets**

All Qualcomm<sup>®</sup> Linux development kits support KVM-based virtualization. Each target uses either KVM or Gunyah as its default boot mode.

**Table: Virtualization support by target**

| Hardware SoC | Development kit | Default boot mode |
| - | - | - |
| IQ-9075 (Lemans) | Qualcomm Dragonwing<sup>™</sup> IQ-9075 | KVM |
| IQ-8275 (Monaco) | Qualcomm Dragonwing<sup>™</sup> IQ-8275 | KVM |
| IQ-615 (Talos) | Qualcomm Dragonwing<sup>™</sup> IQ-615 | KVM |
| QCS6490 (Kodiak) | QCS6490 Qualcomm Dragonwing<sup>™</sup> RB3 Gen 2 Core Development Kit, QCS6490 Dragonwing<sup>™</sup> RB3 Gen 2 optional mezzanine boards | Gunyah |
| QCS5430 (Kodiak) | QCS5430 Dragonwing<sup>™</sup> RB3 Gen 2 Core Development Kit | Gunyah |
| IQ-X7181 (Hamoa) | Qualcomm Dragonwing<sup>™</sup> IQ-X7181 EVK | Gunyah |
| IQ-X5121 (Purwa) | Qualcomm Dragonwing<sup>™</sup> IQ-X5121 EVK | Gunyah |

<Note>
  Refer release notes to know about platform capability and support for hypervisor solution.
</Note>

## **Prerequisites and Kconfig**

Enable the following in the host kernel configuration:

```text theme={null}
CONFIG_KVM=y
CONFIG_VIRTUALIZATION=y
```

KVM requires the kernel to boot in exception level 2 (EL2). Verify KVM is available on the device after boot:

```bash theme={null}
ls -l /dev/kvm
```

## **Boot the kernel in EL2**

Configure KVM as the default boot mode on supported platforms. See the release notes to determine the virtualization capabilities of each platform and identify any exceptions.

The boot mode is selected by the platform-specific DTB or XBL configuration image flashed to the target. Refer to the following table to identify the image that contains the UEFI device tree nodes and the file that selects KVM or Gunyah boot.

| Platform | XBL config file | UEFI DTB file | KVM-based boot file | Gunyah-based boot file |
| - | - | - | - | - |
| CQ2390M/CQ2390S | `xbl_config.elf` (XBL nodes only) | `uefi_dtbs.elf` (UEFI nodes) | `uefi_dtbs_kvm.elf` | `uefi_dtbs.elf` |
| Hamoa/IQ-X7181 | `xbl_config.elf` (XBL nodes only) | `uefi_dtbs.xz` (UEFI nodes, compressed) | `uefi_dtbs_kvm.xz` | `uefi_dtbs.xz` |
| Purwa/IQ-X5121 | `xbl_config.elf` (XBL nodes only) | `uefi_dtbs.xz` (UEFI nodes, compressed) | `uefi_dtbs_kvm.xz` | `uefi_dtbs.xz` |
| IQ10/Nord | `xbl_config.elf` (XBL nodes only) | `uefi_dtbs.elf` (UEFI nodes) | `uefi_dtbs_kvm.elf` | `uefi_dtbs.elf` |
| IQ9/IQ-9075/Lemans | `xbl_config.elf` (XBL and UEFI nodes) | Not applicable | `xbl_config_kvm.elf` | `xbl_config.elf` |
| IQ8/IQ-8275/Monaco | `xbl_config.elf` (XBL and UEFI nodes) | Not applicable | `xbl_config_kvm.elf` | `xbl_config.elf` |
| IQ6/IQ615/Talos | `xbl_config.elf` (XBL and UEFI nodes) | Not applicable | `xbl_config_kvm.elf` | `xbl_config.elf` |
| QCS6490/Kodiak/RB3Gen2 | `xbl_config.elf` (XBL and UEFI nodes) | Not applicable | `xbl_config_kvm.elf` | `xbl_config.elf` |
| QCS5430/Kodiak/RB3Gen2Lite | `xbl_config.elf` (XBL and UEFI nodes) | Not applicable | `xbl_config_kvm.elf` | `xbl_config.elf` |

For CQ2390M/CQ2390S and IQ10/Nord, `xbl_config.elf` contains only XBL-specific DT
nodes. UEFI-specific DT nodes are in `uefi_dtbs.elf` or
`uefi_dtbs_kvm.elf`.

For Hamoa/IQ-X7181 and Purwa/IQ-X5121, `xbl_config.elf` contains only
XBL-specific DT nodes. UEFI-specific DT nodes are in the compressed
`uefi_dtbs.xz` or `uefi_dtbs_kvm.xz` image. Use the compressed `.xz` image on
these platforms.

For IQ9/IQ-9075/Lemans, IQ8/IQ-8275/Monaco, and IQ6/IQ615/Talos,
`xbl_config.elf` contains DT nodes for both XBL and UEFI.

<Note>
  `VendorDtbOverlays` EFI variable updates are not required when switching
  between KVM and Gunyah on targets that use `uefi_dtbs.elf`, `uefi_dtbs.xz`,
  `uefi_dtbs_kvm.elf`, or `uefi_dtbs_kvm.xz`. On these targets, the boot mode
  is selected by flashing the matching UEFI DTB image.
</Note>

The files are located under `build/tmp/deploy/images/<machine-name>` in the
Yocto build output. Replace `<machine-name>` with the actual Yocto machine
configuration name, for example `iq-9075-evk`.

### Switch to KVM

If the platform boots into Gunyah instead of KVM, use the following steps to switch to KVM. Complete the steps in the sequential order:

1. Reboot into fastboot mode. Run the following command on the device shell:

   ```bash theme={null}
   reboot bootloader
   ```

2. Flash the KVM boot file from the host.

   For CQ2390M/CQ2390S and IQ10/Nord:

   ```bash theme={null}
   fastboot flash uefi_dtbs_a uefi_dtbs_kvm.elf
   ```

   For Hamoa/IQ-X7181 and Purwa/IQ-X5121:

   ```bash theme={null}
   fastboot flash uefi_dtbs_a uefi_dtbs_kvm.xz
   ```

   For IQ9/IQ-9075/Lemans, IQ8/IQ-8275/Monaco, IQ6/IQ615/Talos,
   QCS6490/Kodiak/RB3Gen2, and QCS5430/Kodiak/RB3Gen2Lite:

   ```bash theme={null}
   fastboot flash xbl_config_a xbl_config_kvm.elf
   ```

3. Reboot the device:

   ```bash theme={null}
   fastboot reboot
   ```

<Note>
  Platforms configured to boot into KVM by default do not require these steps unless you need to switch back to KVM after booting into Gunyah.
</Note>

### Switch to Gunyah

Use the following steps:

1. Reboot into fastboot mode. Run the following command on the device shell:

   ```bash theme={null}
   reboot bootloader
   ```

2. Flash the Gunyah boot file from the host.

   For CQ2390M/CQ2390S and IQ10/Nord:

   ```bash theme={null}
   fastboot flash uefi_dtbs_a uefi_dtbs.elf
   ```

   For Hamoa/IQ-X7181 and Purwa/IQ-X5121:

   ```bash theme={null}
   fastboot flash uefi_dtbs_a uefi_dtbs.xz
   ```

   For IQ9/IQ-9075/Lemans, IQ8/IQ-8275/Monaco, IQ6/IQ615/Talos,
   QCS6490/Kodiak/RB3Gen2, and QCS5430/Kodiak/RB3Gen2Lite:

   ```bash theme={null}
   fastboot flash xbl_config_a xbl_config.elf
   ```

3. Reboot the device:

   ```bash theme={null}
   fastboot reboot
   ```

## **Build the guest VM artifacts**

Before you launch a guest VM, build a guest kernel image and a guest root file system. The host image doesn't include either component.

### Build the guest kernel

The guest runs at EL1 and doesn't require Qualcomm board device trees or EL2 configuration. Build the guest kernel by using the upstream arm64 defconfig. Use the same kernel source tree that you use for the host:

1. Build the kernel with the standalone `kmake` workflow. For host setup, see
   [Build the kernel without Yocto](./build-kernel-standalone).

   ```bash theme={null}
   kmake O=../kobj-guest ARCH=arm64 defconfig
   kmake O=../kobj-guest ARCH=arm64 -j"$(nproc)" Image
   ```

2. Add the virtio front-end drivers the guest needs. The upstream `defconfig`
   already enables the common ones; confirm and add any that are missing using a
   configuration fragment:

   ```bash theme={null}
   cat >> ../kobj-guest/.config <<'EOF'
   CONFIG_VIRTIO_BLK=y
   CONFIG_VIRTIO_NET=y
   CONFIG_VIRTIO_CONSOLE=y
   CONFIG_VIRTIO_PCI=y
   CONFIG_VIRTIO_VSOCKETS=y
   EOF
   kmake O=../kobj-guest ARCH=arm64 olddefconfig
   kmake O=../kobj-guest ARCH=arm64 -j"$(nproc)" Image
   ```

   For the full list of virtio Kconfig symbols by device, see
   [Supported virtio devices](#virtio-device-support).

The build process writes the guest kernel image to
`../kobj-guest/arch/arm64/boot/Image`.

### Build the guest root file system

Use Yocto to build a minimal guest root file system. The same recipe generates both the CPIO ramdisk and the ext4 disk image.

1. Open a kas shell in your workspace:

   ```bash theme={null}
   kas shell meta-qcom/ci/<SoC>-<board>.yml:meta-qcom/ci/qcom-distro.yml
   ```

2. Add the required image types to `build/conf/local.conf` so the build produces
   both a ramdisk and a disk image:

   ```text theme={null}
   IMAGE_FSTYPES:append = " cpio.gz ext4"
   ```

3. Build the console image, which is the smallest supported image and adequate
   for a guest:

   ```bash theme={null}
   bitbake qcom-console-image
   ```

4. Collect the artifacts from the deploy directory:

   ```text theme={null}
   build/tmp/deploy/images/<machine>/qcom-console-image-<machine>.rootfs.cpio.gz
   build/tmp/deploy/images/<machine>/qcom-console-image-<machine>.rootfs.ext4
   ```

<Note>
  You can use any arm64 root file system as a guest. If you don't need a Yocto-built guest image, use an aarch64 distribution cloud image with the -drive option instead of rootfs.ext4.
</Note>

### Stage the artifacts on the device

Copy the guest kernel and root file system to the host device and place them in
the directory that the following examples use:

```bash theme={null}
# On the build host
mkdir -p guest-artifacts
cp ../kobj-guest/arch/arm64/boot/Image guest-artifacts/
cp build/tmp/deploy/images/<machine>/qcom-console-image-<machine>.rootfs.cpio.gz \
    guest-artifacts/rootfs.cpio.gz
cp build/tmp/deploy/images/<machine>/qcom-console-image-<machine>.rootfs.ext4 \
    guest-artifacts/rootfs.ext4

# Push to the device
adb shell mkdir -p /mnt/overlay/guest
adb push guest-artifacts/. /mnt/overlay/guest/
```

Confirm the guest kernel is the raw image that QEMU expects:

```bash theme={null}
# On the device
file /mnt/overlay/guest/Image
# Expected: Linux kernel ARM64 boot executable Image, little-endian
```

## **Launch a guest VM**

Before launching a guest VM, ensure the guest kernel image (`Image`), root file system CPIO (`rootfs.cpio.gz`), and root file system image (`rootfs.ext4`) are present in the `/mnt/overlay/guest/` directory on the host. To produce them, see [Build the guest VM artifacts](#build-the-guest-vm-artifacts).

### Using QEMU

Boot with a ramdisk:

```bash theme={null}
qemu-system-aarch64 \
    -M virt -m 2G \
    -kernel /mnt/overlay/guest/Image \
    -initrd /mnt/overlay/guest/rootfs.cpio.gz \
    -cpu host --enable-kvm -smp 4 -nographic
```

Boot with a root file system image:

```bash theme={null}
qemu-system-aarch64 \
    -M virt -m 2G \
    -kernel /mnt/overlay/guest/Image \
    -drive file=/mnt/overlay/guest/rootfs.ext4,if=virtio,format=raw \
    -append "root=/dev/vda" \
    -cpu host --enable-kvm -smp 4 -nographic
```

### Validate guest shell access

When QEMU is launched with `-nographic`, the guest serial console is attached to the current terminal. The boot log should print in the same terminal and end at either an automatic guest shell or a login prompt.

Expected guest console flow:

```text theme={null}
Boot Guest VM
      ↓
Guest Login Prompt
      ↓
Login
      ↓
root@guest:~#
```

If the guest image requires authentication, log in with the credentials configured for that image. For test images that allow direct root access, the console might enter the root shell automatically or accept the `root` username without a password.

Guest shell PASS criteria:

| Check | PASS | FAIL |
| - | - | - |
| Boot output | Kernel boot messages continue until user space starts | Boot stops, panics, or repeatedly resets |
| Console access | A login prompt or shell prompt appears on the QEMU console | No prompt appears after boot completes |
| Login | The configured credentials open a shell | Login fails or immediately returns to the prompt |
| Shell prompt | Commands can be run from a guest prompt, for example `root@guest:~#` | The console is unresponsive or stays in boot logs only |

Run a basic command in the guest shell:

```bash theme={null}
uname -a
```

The command must return kernel information from inside the guest VM.

### Validate host stability

While the guest VM is running, open a separate host shell and check host system health:

```bash theme={null}
uptime
free -h
ps -ef | grep qemu
dmesg | tail -50
```

Host stability PASS criteria:

| Command | PASS | FAIL |
| - | - | - |
| `uptime` | The host remains responsive and reports load averages | The command hangs or load rises unexpectedly and does not recover |
| `free -h` | Available memory remains within the expected range for the guest memory size | Free memory is exhausted or swap usage grows unexpectedly |
| `ps -ef \| grep qemu` | The expected `qemu-system-aarch64` process is running | QEMU is missing, repeatedly restarting, or duplicated unexpectedly |
| `dmesg \| tail -50` | No new kernel panic, KVM, memory allocation, or I/O errors appear | New host kernel errors appear while the guest is running |

Guest virtualization validation passes when the guest reaches an interactive shell and the host remains responsive without new virtualization-related kernel errors.

### Using libvirt

Libvirt manages VMs through the `virsh` command-line utility and the `libvirtd` daemon. Define a VM from an XML domain file, then control it with the following commands:

**Table: Common virsh VM management commands**

| Command | Purpose |
| - | - |
| `virsh define <xml-file>` | Register a new VM domain from an XML definition file |
| `virsh list --all` | List all defined VM domains and their state |
| `virsh start <domain>` | Start a stopped VM |
| `virsh console <domain>` | Connect to the VM serial console (Ctrl+] to disconnect) |
| `virsh shutdown <domain>` | Gracefully shut down a running VM |
| `virsh undefine <domain>` | Remove a VM definition |

Replace the following:

* `<xml-file>` by the path to the libvirt XML domain definition file.
* `<domain>` by the VM domain name as defined in the XML `<name>` element.

For libvirt XML domain definition examples, see the [libvirt domain format documentation](https://libvirt.org/formatdomain.html).

## **Virtio device support**

Virtio provides a paravirtualized I/O framework for high-performance device emulation between guest VMs and the host. Front-end drivers run in the guest OS; back-end drivers run in QEMU or the kernel. Communication uses virtqueues (ring buffers) to minimize guest-to-host transitions.

**Table: Supported virtio devices**

| Device | Host Kconfig | Guest Kconfig | Purpose |
| - | - | - | - |
| virtio-block | — | `CONFIG_VIRTIO_BLK` | Block storage device for guest VMs |
| virtio-net | — | `CONFIG_VIRTIO_NET` | Network interface for guest VMs |
| virtio-serial | — | `CONFIG_VIRTIO_CONSOLE` | Serial communication channels between host and guest |
| virtio-9p | `CONFIG_NET_9P`, `CONFIG_NET_9P_VIRTIO` | `CONFIG_NET_9P`, `CONFIG_NET_9P_VIRTIO`, `CONFIG_9P_FS` | Host-to-guest directory sharing over the 9P protocol |
| VSOCK | `CONFIG_VSOCKETS`, `CONFIG_VHOST_VSOCK` | `CONFIG_VSOCKETS`, `CONFIG_VIRTIO_VSOCKETS` | Host-to-guest socket communication |
| virtio-IOMMU | — | `CONFIG_VIRTIO_IOMMU` | Paravirtualized IOMMU for DMA management |
| virtio-balloon | — | `CONFIG_VIRTIO_BALLOON` | Dynamic guest memory reclaim and allocation |

### Host-to-guest file sharing (virtio-9p)

Pass a host directory to a guest VM using the 9P file system:

```bash theme={null}
qemu-system-aarch64 \
    -M virt -m 2G \
    -kernel /mnt/overlay/guest/Image \
    -drive file=/mnt/overlay/guest/rootfs.ext4,if=virtio,format=raw \
    -append "root=/dev/vda" \
    -cpu host --enable-kvm -smp 4 -nographic \
    -fsdev local,id=fsdev0,path=/mnt/overlay/test_dir,security_model=passthrough \
    -device virtio-9p-pci,fsdev=fsdev0,mount_tag=hostshare
```

Mount the shared directory inside the guest:

```bash theme={null}
mount -t 9p -o trans=virtio hostshare <mount-point>
```

Replace the following:

* `<mount-point>` by the directory inside the guest to mount the shared folder.

### Virtual sockets (VSOCK)

VSOCK enables socket communication between guest VMs and the host using a context identifier (CID). The host CID is always 2; guest CIDs start from 3.

**Table: Reserved CID values**

| CID | Description |
| - | - |
| -1 | Any address (for binding) |
| 0 | Hypervisor |
| 1 | Loopback |
| 2 | Host |

Add VSOCK to a QEMU invocation:

```bash theme={null}
-device vhost-vsock-pci,guest-cid=<cid>
```

Replace the following:

* `<cid>` by the CID to assign to the guest VM, for example `73`.

### Device passthrough

Physical devices can be passed through to a guest VM using VFIO (PCI), libusb (USB), or a chardev backend (UART).

Identify USB devices on the host:

```bash theme={null}
lsusb
```

Identify PCI devices on the host:

```bash theme={null}
lspci
```

Add USB passthrough to a QEMU invocation using the vendor and product IDs from `lsusb`:

```bash theme={null}
-device qemu-xhci -device usb-host,vendorid=<vid>,productid=<pid>
```

Add PCI passthrough using the domain, bus, slot, and function from `lspci`:

```bash theme={null}
-device vfio-pci,host=<bus>:<slot>.<function>
```

Replace the following:

* `<vid>` and `<pid>` by the USB vendor and product ID in hex, for example `0x0781` and `0x5567`.
* `<bus>`, `<slot>`, `<function>` by the PCI address components from `lspci` output.

For UART passthrough, configure a `chardev` backend pointing to the host TTY device and expose it to the guest as a `virtserialport`.

## **Observability and maintenance**

### KVM traces

Enable KVM event tracing via tracefs:

```bash theme={null}
echo 1 > /sys/kernel/tracing/events/kvm/enable
cat /sys/kernel/tracing/trace
```

QEMU trace events can be redirected to the kernel ftrace buffer. To launch a guest VM with virtio traces enabled:

```bash theme={null}
qemu-system-aarch64 \
    -M virt -m 2G \
    -kernel /mnt/overlay/guest/Image \
    -drive file=/mnt/overlay/guest/rootfs.ext4,if=virtio,format=raw \
    -append "root=/dev/vda" \
    -cpu host --enable-kvm -smp 4 -nographic \
    -trace "virtio*"
```

### Watchdog

QEMU emulates an I6300 ESB watchdog device, exposed to the guest as a standard watchdog character device. Enable it in the guest kernel:

```text theme={null}
CONFIG_I6300ESB_WDT=y
```

Add the watchdog to the libvirt domain XML:

```xml theme={null}
<devices>
  <watchdog model='i6300esb' action='reset'/>
</devices>
```

The `action` attribute controls behavior on timeout: `reset` restarts the guest, `poweroff` shuts it down. For details, see the [libvirt watchdog documentation](https://libvirt.org/formatdomain.html#watchdog-devices).

### Remote command execution

The QEMU guest agent (`qemu-ga`) allows commands to be run on a guest VM from the host without a network connection. Enable `qemu-ga` in the guest OS user space and configure it through a virtio-serial interface.

Use `virsh qemu-agent-command` with the `guest-exec` subcommand to execute commands remotely:

```bash theme={null}
virsh qemu-agent-command <domain> '{"execute":"guest-exec","arguments":{"path":"cat","arg":["/proc/meminfo"],"capture-output":true}}'
```

Replace the following:

* `<domain>` by the VM domain name.

For more information, see [QEMU Guest Agent](https://qemu-project.gitlab.io/qemu/interop/qemu-ga.html).
