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

<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（请参阅[支持的目标平台](#supported-targets)），主机会在 EL2 启动，请跳过本节。

如果目标平台的默认启动模式为 Gunyah，请按照以下说明切换到 KVM。请按给定顺序完成各步骤。

1. 使用默认镜像启动。

2. 更新 EFI 变量以选择 KVM 设备树 overlay：

   ```bash theme={null}
   echo -n "el2kvm" > /var/data
   efivar -n 882f8c2b-9646-435f-8de5-f208ff80c1bd-VendorDtbOverlays -w -f /var/data
   efivar -n 882f8c2b-9646-435f-8de5-f208ff80c1bd-VendorDtbOverlays -p
   sync
   ```

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

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

4. 从主机烧写 KVM XBL 配置镜像：

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

   `xbl_config_kvm.elf` 文件位于 Yocto 构建输出的 `build/tmp/deploy/images/<machine-name>` 下。

   替换以下内容：

   * `<machine-name>` 替换为实际的 Yocto machine 配置名称，例如 `iq-9075-evk`。

5. 重启设备。

## **构建客户机 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
```

### 使用 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)。
