在驱动程序中映射设备内存

驱动程序通过读取和写入设备内存中的位和字节与系统中的设备进行通信。在 Fuchsia 中,驱动程序可执行以下操作来设置对设备内存的访问:

alt_text

图 1. 进程将虚拟内存对象 (VMO) 映射到自己的虚拟内存地址区域 (VMAR),以访问由 VMO 表示的内存区域。

通过驱动程序访问设备内存

在 Fuchsia 中,驱动程序通过获取表示系统中设备内存区域的虚拟内存对象 (VMO),获得在设备内存中读取和写入数据的能力。

与进程的内存(通常由 DRAM 提供支持)不同,设备的内存区域由寄存器提供支持。因此,设备的 VMO 表示指向这些寄存器的内存映射 I/O (MMIO) 区域。当驱动程序对此区域执行读写操作时,其效果与直接读取或写入设备的寄存器相同。

根总线驱动程序(例如平台、PCI 或 ACPI 总线驱动程序)可识别连接到总线的所有设备,具有为系统中设备创建 VMO 的特殊功能。创建后,这些 VMO 可以传递给其他驱动程序。然后,驱动程序会使用 VMO 访问其目标设备的内存映射 I/O 区域。

下面的序列介绍了可让驱动程序访问设备内存的事件:

  1. 驱动程序管理器发现设备并将驱动程序与其绑定
  2. 驱动程序发出请求,以便为设备生成 VMO
  3. 根总线驱动程序会调用系统调用来为设备创建 VMO。
  4. Zircon 内核会创建一个代表设备内存区域的 VMO。
  5. 根总线驱动程序会接收 VMO 并将其传递给驱动程序。
  6. 驱动程序会将 VMO 映射驱动程序主机中的虚拟内存地址区域 (VMAR)。
  7. 驱动程序在驱动程序主机的映射地址空间中读取和写入数据,以与设备进行通信。

为设备生成 VMO

Fuchsia 提供以下(但不限于)用于检索代表设备内存的 VMO 的方法:

  • PCI FIDL 协议提供 GetBar(),用于从设备检索基础区域寄存器 (BAR)。
  • ACPI FIDL 协议提供用于检索设备内存区域的 GetMmio()
  • 平台设备协议(基于 Banjo)提供了用于检索设备内存区域的 GetMmio()

驱动程序获取设备的 VMO 后,会将 VMO 映射到驱动程序主机中的虚拟地址空间的区域。必须进行此设置,驱动程序才能在设备内存中读取和写入数据(请参阅将 VMO 映射到驱动程序中的 VMAR)。

虽然图 1 似乎表明每个设备存在一个 VMO,但设备的物理内存中可以具有多个内存区域。在这种情况下,设备可能需要生成多个 VMO,以分别表示这些内存区域。

物理内存中的设备 VMO

一般来说,VMO 表示虚拟内存的连续区域。但对于设备而言,VMO 的创建方式与其他常规 VMO 不同。设备的 VMO 由覆盖系统物理内存中的连续区域的物理页面提供支持。

为了创建此类 VMO,具有特殊功能的总线驱动程序会调用 zx_vmo_create_physical 系统调用,并向 Zircon 内核提供设备内存区域的物理地址和大小。然后,内核会返回一个 VMO,该 VMO 由系统内存中的连续物理页面集群提供支持。

设备的地址空间分配

在 Fuchsia 系统中,设备的地址空间区域通过以下某种方式确定:

  • 开发板驱动程序会解读 ACPI设备树,并向驱动程序框架提供静态地址范围。

  • 设备会动态协商一系列地址,通常在启动时进行。

不过,USB 设备永远不会收到地址空间区域。换言之,当 USB 设备热插拔到紫红色系统时,系统不会为其生成 VMO。相反,USB 控制器驱动程序会获取所有 USB 设备的内存映射资源。USB 设备的驱动程序使用 FIDL 协议与其 USB 控制器驱动程序进行通信,然后 USB 控制器驱动程序会在自己的内存映射地址空间中将这些交互转换为读写操作。

对于 PCI 设备(也允许热插拔),系统会在系统中为可能被热插拔的所有可能的 PCI 设备预先分配特定区域的地址空间。此地址空间的子部分会分布到绑定到热插拔的 PCI 设备的驱动程序。PCI 控制器驱动程序负责为驱动程序提供此地址空间的正确区域。

在驱动程序中将 VMO 映射到 VMAR

VMAR(虚拟内存地址区域)表示进程中虚拟地址空间的连续区域。在 Fuchsia 中,进程通常会将 VMO(表示系统中虚拟内存的连续区域)映射到 VMAR,以便读取和写入 VMO 所表示的内存区域中的数据。

如果不映射到 VMAR,进程只能使用一组系统调用(例如 zx_vmo_readzx_vmo_write)与 VMO 进行交互。这是因为所有内存都映射到内核的地址空间,并且只有系统调用可以切换到内核模式,以直接对内存执行读写操作。

驱动程序不得使用这些系统调用与设备内存 VMO 进行交互,并且必须将表示设备内存区域的 VMO 映射到驱动程序主机(驱动程序所在的进程)中的 VMAR。由于以下原因,驱动程序不得使用系统调用与设备内存进行交互:

  • 有关如何映射寄存器的问题(请参阅缓存和未缓存的寄存器)。
  • 有关可安全用于与寄存器交互的指令类型的限制。

如果单个驱动程序主机中有多个驱动程序,则同一驱动程序主机中的所有驱动程序会共享同一个根 VMAR

用于与设备内存交互的辅助库

Fuchsia 提供了一个辅助库(请参阅 lib/mmio),该库对使用 VMO 和 VMAR 的详细信息进行了抽象化处理。此帮助程序库提供的抽象可确保在设备上对寄存器执行读写操作都是安全的。例如,由于并非与系统中的内存交互的每个指令都被认为是安全的,因此帮助程序库包含内嵌汇编指令,以确保在寄存器中读取和写入数据时正确性。

缓存和未缓存的寄存器

在 Fuchsia 中,最好以缓存连贯的方式映射寄存器。在 x64 上,外设内存始终是缓存一致的,因此可以映射为已缓存。 不过,在 ARM 上,外设内存不具备缓存一致性,因此通常映射为未缓存。

在 Fuchsia 中,允许使用替代方式映射寄存器并手动管理这些寄存器的缓存一致性。但是,只有在使用未缓存的映射时遇到性能问题的高级开发者才应考虑使用此方法。