> ## 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.

# 启用虚拟化

Qualcomm<sup>®</sup> Linux 通过 KVM（基于内核的虚拟机）支持硬件辅助虚拟化。KVM 直接集成到 Linux 内核中，以接近原生的性能运行客户机操作系统。在实现 Armv8.1 或更高版本的 Qualcomm SoC 上，虚拟主机扩展（VHE）默认启用，允许主机内核在 EL2 运行，而客户机内核和用户空间在 EL1 和 EL0 运行。

## **支持的目标平台**

所有 Qualcomm<sup>®</sup> Linux 开发套件都支持基于 KVM 的虚拟化。每个目标平台的默认启动模式为 KVM 或 Gunyah 两者之一。

**表：各目标平台的虚拟化支持情况**

| 硬件 SoC | 开发套件 | 默认启动模式 |
| - | - | - |
| 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 可选夹层板 | 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>
  请参阅发行说明，了解平台能力以及对 hypervisor 方案的支持情况。
</Note>

## **前提条件和 Kconfig**

在主机内核配置中启用以下选项：

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

KVM 要求内核在异常级别 2（EL2）启动。启动后在设备上验证 KVM 是否可用：

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

## **在 EL2 启动内核**

在支持的平台上将 KVM 配置为默认启动模式。请参阅发行说明以确定各平台的虚拟化能力并识别任何例外情况。

启动模式由烧写到目标平台的平台专用 DTB 或 XBL 配置镜像选择。请参阅下表，确定包含 UEFI 设备树节点的镜像，以及用于选择 KVM 或 Gunyah 启动的文件。

| 平台 | XBL 配置文件 | UEFI DTB 文件 | 基于 KVM 的启动文件 | 基于 Gunyah 的启动文件 |
| - | - | - | - | - |
| CQ2390M/CQ2390S | `xbl_config.elf`（仅 XBL 节点） | `uefi_dtbs.elf`（UEFI 节点） | `uefi_dtbs_kvm.elf` | `uefi_dtbs.elf` |
| Hamoa/IQ-X7181 | `xbl_config.elf`（仅 XBL 节点） | `uefi_dtbs.xz`（UEFI 节点，已压缩） | `uefi_dtbs_kvm.xz` | `uefi_dtbs.xz` |
| Purwa/IQ-X5121 | `xbl_config.elf`（仅 XBL 节点） | `uefi_dtbs.xz`（UEFI 节点，已压缩） | `uefi_dtbs_kvm.xz` | `uefi_dtbs.xz` |
| IQ10/Nord | `xbl_config.elf`（仅 XBL 节点） | `uefi_dtbs.elf`（UEFI 节点） | `uefi_dtbs_kvm.elf` | `uefi_dtbs.elf` |
| IQ9/IQ-9075/Lemans | `xbl_config.elf`（XBL 和 UEFI 节点） | 不适用 | `xbl_config_kvm.elf` | `xbl_config.elf` |
| IQ8/IQ-8275/Monaco | `xbl_config.elf`（XBL 和 UEFI 节点） | 不适用 | `xbl_config_kvm.elf` | `xbl_config.elf` |
| IQ6/IQ615/Talos | `xbl_config.elf`（XBL 和 UEFI 节点） | 不适用 | `xbl_config_kvm.elf` | `xbl_config.elf` |
| QCS6490/Kodiak/RB3Gen2 | `xbl_config.elf`（XBL 和 UEFI 节点） | 不适用 | `xbl_config_kvm.elf` | `xbl_config.elf` |
| QCS5430/Kodiak/RB3Gen2Lite | `xbl_config.elf`（XBL 和 UEFI 节点） | 不适用 | `xbl_config_kvm.elf` | `xbl_config.elf` |

对于 CQ2390M/CQ2390S 和 IQ10/Nord，`xbl_config.elf` 仅包含 XBL 专用的 DT
节点。UEFI 专用的 DT 节点位于 `uefi_dtbs.elf` 或 `uefi_dtbs_kvm.elf` 中。

对于 Hamoa/IQ-X7181 和 Purwa/IQ-X5121，`xbl_config.elf` 仅包含 XBL 专用的
DT 节点。UEFI 专用的 DT 节点位于压缩的 `uefi_dtbs.xz` 或 `uefi_dtbs_kvm.xz`
镜像中。在这些平台上请使用压缩的 `.xz` 镜像。

对于 IQ9/IQ-9075/Lemans、IQ8/IQ-8275/Monaco 和 IQ6/IQ615/Talos，
`xbl_config.elf` 同时包含 XBL 和 UEFI 的 DT 节点。

<Note>
  在使用 `uefi_dtbs.elf`、`uefi_dtbs.xz`、`uefi_dtbs_kvm.elf` 或
  `uefi_dtbs_kvm.xz` 的目标平台上，在 KVM 和 Gunyah 之间切换时无需更新
  `VendorDtbOverlays` EFI 变量。在这些目标平台上，启动模式通过烧写匹配的
  UEFI DTB 镜像来选择。
</Note>

这些文件位于 Yocto 构建输出的 `build/tmp/deploy/images/<machine-name>` 下。
将 `<machine-name>` 替换为实际的 Yocto machine 配置名称，例如 `iq-9075-evk`。

### 切换到 KVM

如果平台启动进入 Gunyah 而不是 KVM，请使用以下步骤切换到 KVM。请按顺序完成各步骤：

1. 重启进入 fastboot 模式。在设备 shell 上运行以下命令：

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

2. 从主机烧写 KVM 启动文件。

   对于 CQ2390M/CQ2390S 和 IQ10/Nord：

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

   对于 Hamoa/IQ-X7181 和 Purwa/IQ-X5121：

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

   对于 IQ9/IQ-9075/Lemans、IQ8/IQ-8275/Monaco、IQ6/IQ615/Talos、
   QCS6490/Kodiak/RB3Gen2 和 QCS5430/Kodiak/RB3Gen2Lite：

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

3. 重启设备：

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

<Note>
  默认配置为启动进入 KVM 的平台无需执行这些步骤，除非您在启动进入 Gunyah 后需要切换回 KVM。
</Note>

### 切换到 Gunyah

请使用以下步骤：

1. 重启进入 fastboot 模式。在设备 shell 上运行以下命令：

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

2. 从主机烧写 Gunyah 启动文件。

   对于 CQ2390M/CQ2390S 和 IQ10/Nord：

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

   对于 Hamoa/IQ-X7181 和 Purwa/IQ-X5121：

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

   对于 IQ9/IQ-9075/Lemans、IQ8/IQ-8275/Monaco、IQ6/IQ615/Talos、
   QCS6490/Kodiak/RB3Gen2 和 QCS5430/Kodiak/RB3Gen2Lite：

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

3. 重启设备：

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

## **构建客户机 VM 产物**

在启动客户机 VM 之前，请先构建客户机内核镜像和客户机根文件系统。主机镜像不包含这两个组件。

### 构建客户机内核

客户机运行在 EL1，不需要 Qualcomm 开发板设备树或 EL2 配置。请使用上游 arm64 defconfig 构建客户机内核。使用与主机相同的内核源码树：

1. 使用独立的 `kmake` 工作流构建内核。有关主机环境设置，请参阅
   [不使用 Yocto 构建内核](./build-kernel-standalone)。

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

2. 添加客户机所需的 virtio 前端驱动。上游 `defconfig` 已启用常用驱动；请确认并通过配置片段补充缺失的驱动：

   ```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
   ```

   有关按设备划分的完整 virtio Kconfig 符号列表，请参阅
   [Virtio 设备支持](#virtio-device-support)。

构建过程会将客户机内核镜像写入
`../kobj-guest/arch/arm64/boot/Image`。

### 构建客户机根文件系统

使用 Yocto 构建一个最小的客户机根文件系统。同一个 recipe 会同时生成 CPIO ramdisk 和 ext4 磁盘镜像。

1. 在您的工作区中打开 kas shell：

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

2. 将所需的镜像类型添加到 `build/conf/local.conf`，使构建同时生成 ramdisk 和磁盘镜像：

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

3. 构建 console 镜像，这是受支持的最小镜像，作为客户机已足够：

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

4. 从 deploy 目录收集产物：

   ```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>
  您可以使用任何 arm64 根文件系统作为客户机。如果不需要 Yocto 构建的客户机镜像，可以改用 aarch64 发行版云镜像，配合 -drive 选项使用，替代 rootfs.ext4。
</Note>

### 将产物部署到设备

将客户机内核和根文件系统复制到主机设备，并放入以下示例所使用的目录中：

```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/
```

确认客户机内核是 QEMU 所需的原始镜像格式：

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

## **启动客户机 VM**

在启动客户机 VM 之前，请确保客户机内核镜像（`Image`）、根文件系统 CPIO（`rootfs.cpio.gz`）和根文件系统镜像（`rootfs.ext4`）位于主机的 `/mnt/overlay/guest/` 目录中。有关如何生成这些文件，请参阅[构建客户机 VM 产物](#build-the-guest-vm-artifacts)。

### 使用 QEMU

使用 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
```

使用根文件系统镜像启动：

```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
```

### 验证客户机 shell 访问

使用 `-nographic` 启动 QEMU 时，客户机串口控制台会连接到当前终端。启动日志应在同一终端中输出，并最终进入自动打开的客户机 shell 或登录提示符。

预期的客户机控制台流程：

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

如果客户机镜像需要身份验证，请使用为该镜像配置的凭据登录。对于允许直接 root 访问的测试镜像，控制台可能会自动进入 root shell，或接受无密码的 `root` 用户名。

客户机 shell 通过（PASS）标准：

| 检查项 | 通过（PASS） | 失败（FAIL） |
| - | - | - |
| 启动输出 | 内核启动消息持续输出，直到用户空间启动 | 启动停止、出现 panic 或反复重置 |
| 控制台访问 | QEMU 控制台上出现登录提示符或 shell 提示符 | 启动完成后未出现提示符 |
| 登录 | 使用配置的凭据可打开 shell | 登录失败或立即返回提示符 |
| Shell 提示符 | 可以在客户机提示符下运行命令，例如 `root@guest:~#` | 控制台无响应或仅停留在启动日志 |

在客户机 shell 中运行一条基本命令：

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

该命令必须返回客户机 VM 内部的内核信息。

### 验证主机稳定性

在客户机 VM 运行期间，打开另一个主机 shell 并检查主机系统运行状况：

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

主机稳定性通过（PASS）标准：

| 命令 | 通过（PASS） | 失败（FAIL） |
| - | - | - |
| `uptime` | 主机保持响应并报告平均负载 | 命令挂起，或负载意外升高且无法恢复 |
| `free -h` | 可用内存保持在与客户机内存大小相符的预期范围内 | 可用内存耗尽，或 swap 使用量意外增长 |
| `ps -ef \| grep qemu` | 预期的 `qemu-system-aarch64` 进程正在运行 | QEMU 进程缺失、反复重启或意外重复 |
| `dmesg \| tail -50` | 未出现新的内核 panic、KVM、内存分配或 I/O 错误 | 客户机运行期间出现新的主机内核错误 |

当客户机进入交互式 shell，且主机保持响应、未出现新的虚拟化相关内核错误时，客户机虚拟化验证即为通过。

### 使用 libvirt

Libvirt 通过 `virsh` 命令行实用程序和 `libvirtd` 守护进程管理 VM。从 XML 域文件定义一个 VM，然后使用以下命令进行控制：

**表：常用 virsh VM 管理命令**

| 命令 | 用途 |
| - | - |
| `virsh define <xml-file>` | 从 XML 定义文件注册新的 VM 域 |
| `virsh list --all` | 列出所有已定义的 VM 域及其状态 |
| `virsh start <domain>` | 启动已停止的 VM |
| `virsh console <domain>` | 连接到 VM 串口控制台（按 Ctrl+] 断开） |
| `virsh shutdown <domain>` | 优雅地关闭正在运行的 VM |
| `virsh undefine <domain>` | 移除 VM 定义 |

替换以下内容：

* `<xml-file>` 替换为 libvirt XML 域定义文件的路径。
* `<domain>` 替换为 XML `<name>` 元素中定义的 VM 域名称。

有关 libvirt XML 域定义示例，请参阅 [libvirt domain format documentation](https://libvirt.org/formatdomain.html)。

## **Virtio 设备支持**

Virtio 提供了一个半虚拟化 I/O 框架，用于客户机 VM 与主机之间的高性能设备模拟。前端驱动在客户机操作系统中运行；后端驱动在 QEMU 或内核中运行。通信使用 virtqueue（环形缓冲区），以最大限度地减少客户机到主机的切换。

**表：支持的 virtio 设备**

| 设备 | 主机 Kconfig | 客户机 Kconfig | 用途 |
| - | - | - | - |
| virtio-block | — | `CONFIG_VIRTIO_BLK` | 客户机 VM 的块存储设备 |
| virtio-net | — | `CONFIG_VIRTIO_NET` | 客户机 VM 的网络接口 |
| virtio-serial | — | `CONFIG_VIRTIO_CONSOLE` | 主机和客户机之间的串行通信通道 |
| virtio-9p | `CONFIG_NET_9P`、`CONFIG_NET_9P_VIRTIO` | `CONFIG_NET_9P`、`CONFIG_NET_9P_VIRTIO`、`CONFIG_9P_FS` | 通过 9P 协议实现主机到客户机的目录共享 |
| VSOCK | `CONFIG_VSOCKETS`、`CONFIG_VHOST_VSOCK` | `CONFIG_VSOCKETS`、`CONFIG_VIRTIO_VSOCKETS` | 主机到客户机的套接字通信 |
| virtio-IOMMU | — | `CONFIG_VIRTIO_IOMMU` | 用于 DMA 管理的半虚拟化 IOMMU |
| virtio-balloon | — | `CONFIG_VIRTIO_BALLOON` | 动态回收和分配客户机内存 |

### 主机到客户机的文件共享（virtio-9p）

使用 9P 文件系统将主机目录传递给客户机 VM：

```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
```

在客户机内部挂载共享目录：

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

替换以下内容：

* `<mount-point>` 替换为客户机内部用于挂载共享文件夹的目录。

### 虚拟套接字（VSOCK）

VSOCK 使用上下文标识符（CID）在客户机 VM 与主机之间实现套接字通信。主机 CID 始终为 2；客户机 CID 从 3 开始。

**表：保留的 CID 值**

| CID | 描述 |
| - | - |
| -1 | 任意地址（用于绑定） |
| 0 | Hypervisor |
| 1 | 环回 |
| 2 | 主机 |

将 VSOCK 添加到 QEMU 调用中：

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

替换以下内容：

* `<cid>` 替换为要分配给客户机 VM 的 CID，例如 `73`。

### 设备直通

可以使用 VFIO（PCI）、libusb（USB）或 chardev 后端（UART）将物理设备直通给客户机 VM。

在主机上识别 USB 设备：

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

在主机上识别 PCI 设备：

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

使用 `lsusb` 中的供应商和产品 ID 将 USB 直通添加到 QEMU 调用中：

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

使用 `lspci` 中的 domain、bus、slot 和 function 添加 PCI 直通：

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

替换以下内容：

* `<vid>` 和 `<pid>` 替换为十六进制的 USB 供应商和产品 ID，例如 `0x0781` 和 `0x5567`。
* `<bus>`、`<slot>`、`<function>` 替换为 `lspci` 输出中的 PCI 地址组成部分。

对于 UART 直通，请配置一个指向主机 TTY 设备的 `chardev` 后端，并将其作为 `virtserialport` 暴露给客户机。

## **可观测性和维护**

### KVM 跟踪

通过 tracefs 启用 KVM 事件跟踪：

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

QEMU 跟踪事件可以重定向到内核 ftrace 缓冲区。要启动一个启用了 virtio 跟踪的客户机 VM：

```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*"
```

### 看门狗

QEMU 模拟一个 I6300 ESB 看门狗设备，以标准看门狗字符设备的形式暴露给客户机。在客户机内核中启用它：

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

将看门狗添加到 libvirt 域 XML 中：

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

`action` 属性控制超时时的行为：`reset` 重启客户机，`poweroff` 关闭客户机。有关详细信息，请参阅 [libvirt watchdog documentation](https://libvirt.org/formatdomain.html#watchdog-devices)。

### 远程命令执行

QEMU 客户机代理（`qemu-ga`）允许在没有网络连接的情况下从主机在客户机 VM 上运行命令。在客户机操作系统用户空间中启用 `qemu-ga`，并通过 virtio-serial 接口对其进行配置。

使用 `virsh qemu-agent-command` 配合 `guest-exec` 子命令远程执行命令：

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

替换以下内容：

* `<domain>` 替换为 VM 域名称。

有关更多信息，请参阅 [QEMU Guest Agent](https://qemu-project.gitlab.io/qemu/interop/qemu-ga.html)。
