驱动程序通过读取和写入设备内存中的位和字节来与系统中的设备通信。在 Fuchsia 中,驱动程序会执行以下操作来设置对设备内存的访问权限:
图 1。进程会将虚拟内存对象 (VMO) 映射到自己的虚拟内存地址区域 (VMAR),以访问 VMO 表示的内存区域。
从驱动程序访问设备的内存
在 Fuchsia 中,驱动程序通过获取表示系统中设备内存区域的虚拟内存对象 (VMO),获得在设备内存中读取和写入数据的能力。
与通常由 DRAM 支持的进程内存不同,设备的内存区域由寄存器支持。因此,设备的 VMO 表示指向这些寄存器的内存映射 I/O (MMIO) 区域。当驱动程序从此区域读取或向此区域写入时,其效果与直接从设备寄存器读取或向设备寄存器写入相同。
根总线驱动程序(例如平台、PCI 或 ACPI 总线驱动程序)知道连接到总线的所有设备,并且具有为系统中的设备创建 VMO 的特殊功能。创建后,这些 VMO 可传递给其他驱动程序。然后,驱动程序使用 VMO 访问其目标设备的内存映射 I/O 区域。
以下序列将详细介绍使驱动程序能够访问设备内存的事件:
- 驱动程序管理器会发现设备并将驱动程序绑定到该设备。
- 驱动程序会请求为设备生成 VMO。
- 根总线驱动程序会调用 syscall 来为设备创建 VMO。
- Zircon 内核会创建一个 VMO,用于表示设备的内存区域。
- 根总线驱动程序会接收 VMO 并将其传递给驱动程序。
- 驱动程序会将 VMO 映射到驱动程序主机中的虚拟内存地址区域 (VMAR)。
- 驱动程序会在驱动程序主机的映射地址空间中读取和写入数据,以便与设备通信。
为设备生成 VMO
Fuchsia 提供了以下(但不限于)用于检索表示设备内存的 VMO 的方法:
- PCI FIDL 协议提供了
GetBar()
,用于从设备检索基准区域寄存器 (BAR)。 - ACPI FIDL 协议提供
GetMmio()
来检索设备的内存区域。 - 平台设备 FIDL 协议提供
GetMmio()
以检索设备的内存区域。
驱动程序获取设备的 VMO 后,会将 VMO 映射到驱动程序主机中的虚拟地址空间区域。为了让驱动程序能够在设备内存中读取和写入数据,必须进行此设置(请参阅在驱动程序中将 VMO 映射到 VMAR)。
虽然图 1 似乎表明每部设备都有一个 VMO,但设备在实体内存中可以有多个内存区域。在这种情况下,设备可能需要生成多个 VMO 来单独表示这些内存区域。
物理内存中的设备 VMO
VMO 通常表示虚拟内存的连续区域。但对于设备,VMO 的创建方式与其他常规 VMO 不同。设备的 VMO 由覆盖系统物理内存中连续区域的物理页面提供支持。
如需创建此类 VMO,具有特殊功能的总线驱动程序会调用 zx_vmo_create_physical
系统调用,并向 Zircon 内核提供设备内存区域的物理地址和大小。然后,内核会返回一个 VMO,该 VMO 由系统内存中一组连续的物理页面提供支持。
设备的地址空间分配
在 Fuchsia 系统中,设备的地址空间区域通过以下任一方式确定:
不过,USB 设备永远不会收到地址空间区域。换句话说,当 USB 设备热插拔到 Fuchsia 系统时,系统不会为其生成任何 VMO。而是由 USB 控制器驱动程序获取所有 USB 设备的内存映射资源。USB 设备的驱动程序使用 FIDL 协议与其 USB 控制器驱动程序通信,然后 USB 控制器驱动程序会将这些交互转换为其自己的内存映射地址空间中的读取和写入操作。
对于也支持热插拔的 PCI 设备,系统会为所有可能的热插拔 PCI 设备预分配特定的地址空间区域。此地址空间的子部分会分发给绑定到热插拔 PCI 设备的驱动程序。PCI 控制器驱动程序有责任向驱动程序提供此地址空间的正确区域。
在驱动程序中将 VMO 映射到 VMAR
VMAR(虚拟内存地址区域)表示进程中虚拟地址空间的连续区域。在 Fuchsia 中,进程通常会将 VMO(表示系统中虚拟内存的连续区域)映射到 VMAR,以便在 VMO 表示的内存区域中读取和写入数据。
如果未映射到 VMAR,进程只能使用一组系统调用(例如 zx_vmo_read
和 zx_vmo_write
)与 VMO 交互。这是因为所有内存都映射到内核的地址空间,并且只有系统调用可以切换到内核模式,以直接读取或写入内存。
驱动程序不得使用这些系统调用与设备内存 VMO 交互,并且必须将表示设备内存区域的 VMO 映射到驱动程序主机(即驱动程序所在的进程)中的 VMAR。驱动程序不得使用系统调用来与设备的内存进行交互,因为:
- 与寄存器映射方式相关的问题(请参阅已缓存和未缓存的寄存器)。
- 对可用于与寄存器交互的安全指令类型的限制。
如果单个驱动程序主机中有多个驱动程序,则同一驱动程序主机中的所有驱动程序共享相同的根 VMAR。
用于与设备内存交互的辅助库
Fuchsia 提供了一个辅助库(请参阅 lib/mmio
),用于提取与 VMO 和 VMAR 相关的操作细节。此辅助库提供的抽象可确保从设备上的寄存器读取和向其写入数据是安全的。例如,由于并非系统中与内存交互的每条指令都被视为安全,因此辅助库包含内嵌汇编指令,以确保在寄存器中读取和写入数据时正确无误。
缓存的寄存器和未缓存的寄存器
在 Fuchsia 中,最好以缓存一致的方式映射寄存器。在 x64 上,外围内存始终是缓存一致的,因此可以映射为缓存。不过,在 ARM 上,外围内存不具有缓存一致性,因此通常会映射为未缓存。
Fuchsia 允许使用其他方式映射寄存器,并手动管理这些寄存器的缓存一致性。不过,只有在使用未缓存的映射时遇到性能问题的高级开发者才应考虑这种方法。