Discovering and connecting VIRTIO devices

Posted by Underdog Linux on December 7, 2023

当guest关闭时,无论是受控关闭还是非受控关闭,qvm 进程都应尝试静默guest使用的任何物理设备。 这可确保guest驱动程序不会继续写入物理内存,这可能会使硬件处于意外甚至损坏的状态。

当guest关闭并因此其设备驱动程序消失时,主机硬件不能保持在相同状态,因为设备可能正在异步操作(例如,进行 DMA 写入、生成中断)。 为了保护硬件的完整性,可以将guest运行的 qvm 进程设计为在guest终止时静默任何此类设备(即使其进入休眠状态)。

为了遵循此设计,qvm 进程可以包含一个 vdev,用于静默设备并配置此 vdev,以便在 qvm 进程终止时、释放驱动程序进程的资源之前运行其回调函数。 回调函数必须执行任何所需的清理,包括静止。 这需要尽快关闭设备,以便当 qvm 进程消失时,设备不再:

  • 写入物理内存(非常危险)
  • 生成中断(危险性较小,因为hypervisor 可以确认 IRQ 并忽略它)

有关创建自定义 vdev 的信息,参阅 Virtual Device Developer’s Guide

有关定义 vdev 的控制函数以注册在进程关闭期间运行的回调的信息,参阅Handling a qvm termination

guest 可以尝试让设备停顿; 只是hypervisor 不能依赖guest这样做。 
运行 QNX Hypervisor for Safety 变体时,您必须提供 vdev 以在关闭期间使物理设备停顿。 
对于非安全 QNX Hypervisor 变体,建议但不是必需的。

有多种方法可用于将guest中的设备驱动程序连接到其相应的hypervisor 主机设备。

在guest中运行的设备驱动程序可以使用以下方法之一连接到主机中的 VIRTIO vdev

您使用的方法取决于您的guest 的配置方式,以及您希望更改设备位置的难易程度。 例如,在生产系统中,direct memory mapping(在配置中写入设备内存中的位置)可能是可以接受的,甚至是更可取的,但在项目的开发阶段,您可能需要更灵活的方法,因此可能需要 使用 PCI discovery

有关 devb-ahcidevb-virtio 等驱动程序及其选项的更多信息,请参阅 Utilities Reference

PCI discovery

PCI discovery方法在为 x86 平台设计的guest中最常见,但 ARM 平台上的客户机也可以使用它。 PCI 硬件不需要支持这种发现机制,因为在hypervisor中,guest PCI 是完全虚拟的。

以下步骤说明了如何使用 PCI mapping 来使guest中的驱动程序能够发现并连接到 VIRTIO 设备。 该示例使用 virtio-blk vdev,但也可以使用其他 VIRTIO vdev,例如 virtio-console 或 virtio-net。

使用 virtio-blk 作为我们的示例,以及 QNX guest:

  1. 在hypervisor 主机中,启动 AHCI SATA 接口 (devb-ahci) 的驱动程序。
  2. 配置托管guest的虚拟机,通过 VIRTIO 将 AHCI SATA 驱动程序的位置(位于 /dev/hd1t178)映射到guest; 例如:
    vdev virtio-blk
         hostdev /dev/hd1t178
         name virtio-blk_qvm178
    

    有关在虚拟机中配置此 vdev 的更多信息,请参阅 vdev virtio-blk

  3. 启动 QNX guest。
  4. 在来宾中,运行 pci-server(请参阅Utilities Reference中的 pci-server); 例如:
    pci-server –bus-scan-limit=8
    
  5. 在 QNX guest 中,使用 pci-tool 查询 PCI 设备(请参阅Utilities Reference中的 pci-tool); 例如: ```markdown [x86 guest QNX 7.1]% pci-tool -v

B000:D00:F00 @ idx 0 vid/did: 1c05/0002 BlackBerry QNX, n/a QVM PCI host bridge class/subclass/reg: 06/00/00 Host-to-PCI Bridge Device

B000:D01:F00 @ idx 1 vid/did: 1af4/1042 <vendor id - unknown>, <device id - unknown> class/subclass/reg: 01/80/00 Other Mass Storage Controller

B000:D02:F00 @ idx 2 vid/did: 1af4/1041 <vendor id - unknown>, <device id - unknown> class/subclass/reg: 02/80/00 Other Network Controller

B000:D03:F00 @ idx 3 vid/did: 1c05/0001 BlackBerry QNX, n/a QVM guest shared memory factory class/subclass/reg: 05/80/00 Other Memory Controller

B000:D04:F00 @ idx 4 vid/did: 1af4/1043 <vendor id - unknown>, <device id - unknown> class/subclass/reg: 07/80/00 Other Simple Communications Controller


请注意,磁盘控制器的 VID/DID(vendor ID/device ID)为 1af4/1042; 这是大容量存储设备的 VIRTIO 标准参考。 
其他设备(例如内存设备或网络设备)也将显示为 PCI 设备。

在 Linux 客户机中,您可以使用 `lspci`。 您可能会看到VID/DID为1c05/0042; 这是 BlackBerry VIRTIO 供应商 ID。 Linux 内核模块知道这是一个块设备。

6. 在 QNX 客户机中,启动块设备的 VIRTIO 驱动程序 (devb-virtio); 例如:
```markdown
devb-virtio

驱动程序会扫描PCI空间,找到具有1af4/1042供应商ID/设备ID的设备,并将其挂载为块卷; 例如,上面的命令将在 guest 虚拟机中创建 /dev/hd0

  1. 如果块存储卷已格式化为 QNX6 power-safe 文件系统,则可以挂载它; 例如:
    mount -t qnx6 /dev/hd0 /mydisk
    

Direct memory mapping

以下步骤说明如何在主机中映射设备,然后将映射传递到guest中的驱动程序。 该示例使用 virtio-blk vdev,但也可以使用其他 VIRTIO vdev,例如 virtio-consolevirtio-net

使用 virtio-blk 作为我们的示例,以及 QNX guest:

  1. 在hypervisor 主机中,启动相应接口的驱动程序(例如 devb-ahci)。
  2. 使用主机文件的路径配置虚拟机以用于设备的内容(参阅Virtual Device Reference章节的vdev virtio-blk)

使用guest-physical内存中未由guest中的任何其他驱动程序使用的位置,以及未由guest中的任何其他驱动程序或服务使用的中断。 在此示例中,我们使用 0x1c0d0000 和 41:

vdev virtio-blk
        loc 0x1c0d0000
        intr gic:41
        hostdev /dev/hd1t178
        name virtio-blk_qvm178
  1. 启动 QNX 来宾。 不需要 PCI server 。
  2. 在 guest 虚拟机中启动 VIRTIO 块设备驱动程序 (devb-virtio),使用驱动程序的启动选项映射内存并在 virtio-blk vdev 配置中指定中断; 在我们的示例中,选项如下:
    devb-virtio virtio smem=0x1c0d0000,irq=41
    

guest驱动程序现在应该通过 VIRTIO 设备找到主机中的设备。

您可以使用FDT来扩展direct mapping以支持设备的动态发现。 例如,在 ARM 平台上,您可以使用 FDT 中的条目向guest提供有关虚拟设备的信息。 有关在 QNX hypervisor 系统中使用 FDT 的更多信息,参阅Configuration章节的ACPI tables and FDTs