如需了解 Magma 的概览(包括背景、硬件要求和架构说明),请参阅 Magma:概览。
本文档介绍了驱动程序开发者可遵循的将 Vulkan 驱动程序移植到 Fuchsia 的流程。
要求
将 Vulkan 驱动程序移植到 Fuchsia 需要满足以下条件:
- 硬件文档(寄存器规范、运作原理)。
- 一个参考 Vulkan 实现(Linux)。
- 客户端驱动程序库 (ICD) 应提供符合 Vulkan 1.1/1.2 规范的实现。
硬件必须已通过启动,才能实现网络访问、磁盘存储和 fx pave 功能。
目前,Magma 仅支持 UMA 设备,但如果您有雄心壮志,可以尝试移植到具有独立显存的 GPU。
创建桩系统驱动程序
Magma 系统驱动程序 (MSD) 类似于其他操作系统上的内核驱动程序。此时,请考虑是从其他操作系统移植现有内核驱动程序,还是从头开始编写一个驱动程序。
此时,您应阅读驱动程序概念部分,了解 Fuchsia 驱动程序模型。
此选择取决于现有内核驱动程序代码的许多方面。一些注意事项:
- 驱动程序是否具有平台抽象层,还是依赖于 Linux 内核接口?
- 许可。
- 其 ioctl 接口与 Magma 入口点有多相似?
编码的第一步是创建一个可构建的基本 MSD。MSD 位于驱动程序目录中。它们使用 GN 构建系统构建为 fuchsia_drivers
。Magma 系统驱动程序必须是开源的(但不是 GPL),并托管在 fuchsia.googlesource.com 上。
Fuchsia 驱动程序是常规的用户空间进程。这意味着,它们可以访问大多数 C 库和部分 POSIX API。与大多数进程不同,此类进程不允许访问文件系统。
目前,Magma 支持两种不同的设备类型:平台设备和 PCI 设备。
- SoC 通常具有平台设备。这些设备不是即插即用,而是需要开发板驱动程序将适当的资源委托给它们。
- PCI 设备位于 PCI 总线上,并由 PCI 总线驱动程序委托资源。
Fuchsia 驱动程序框架支持这两种类型,因此它们可以像常规 Fuchsia 驱动程序一样创建和绑定。
分担责任?(SoC 版本)
在 SoC 上,GPU 硬件通常是单独的 IP 块,可由 SoC 供应商进行自定义。具体取决于供应商的定制程度,您可以通过多种方式继续操作。
- 统一的 MSD,但之前加载了 SoC 专用驱动程序。
- 为每个 SoC 或 SoC 供应商创建单独的 MSD。
如果对 SoC 进行的自定义较少,最好使用统一的 MSD。特定于供应商的驱动程序将先绑定,并将 banjo 接口导出给 MSD,以便开启/关闭 GPU、更改时钟等。这样做的好处是,通过实现新的特定于供应商的驱动程序,可以更轻松地将 MSD 移植到新硬件,而无需进行修改。如需查看此方法的示例,请参阅 msd-arm-mali 和 aml-gpu。
为每个 SoC 提供单独的 MSD 可提高灵活性,如果 GPU IP 供应商允许对 SoC 中的 IP 块进行大量自定义,则可能需要这样做。不同 SoC 的 MSD 实现可以共享一个不依赖于 SoC 的代码库,但会编译为独立驱动程序。
分担责任?(PCI 版本)
PCI GPU 通常包含显示控制器硬件。最好将显示屏控制器驱动程序与 GPU 硬件分开实现,这样便可以在能够访问磁盘之前提供启动控制台。显示屏控制器应公开硬件专用 banjo 接口,并且 MSD 可以绑定到显示屏驱动程序。
如需查看拆分为两个部分的 PCI 驱动程序示例,请参阅 msd-intel-gen 和 intel-display。
开机
现在,MSD 正在构建中,下一步是编写代码来重置设备并使其进入操作模式。此类信息包括:
- 启动设备(可能使用 fuchsia.hardware.power.Device banjo 接口)。
- 启用时钟(可能使用 fuchsia.hardware.clock.Clock FIDL 接口)。
- 启用总线主控或内存访问。
- 正在加载固件。
驱动程序还应根据需要获得对 MMIO 范围的访问权限,并应开始处理中断。对于 SoC,必须修改板级驱动程序,以将这些资源传递给 MSD 或 SoC 专用驱动程序,并且必须添加一个设备以供 MSD 绑定。
此阶段的测试:
- 在驱动程序启动时记录 MMIO 寄存器。
实现 MSD
下面列出了驱动程序可以实现的主要函数:
- 初始化硬件
- msd_driver_create
- msd_driver_configure
- msd_driver_destroy
- msd_driver_create_device
- msd_device_destroy
- 支持参数查询
- msd_device_query
- 支持状态转储
- msd_device_dump_status
- 创建关联
- msd_device_open
- msd_connection_close
- 创建缓冲区
- msd_buffer_import
- msd_buffer_destroy
- 设置内存空间和缓冲区映射
- msd_connection_map_buffer_gpu
- msd_connection_unmap_buffer_gpu
- msd_connection_commit_buffer
- msd_connection_release_buffer
- 设置硬件上下文
- msd_connection_create_context
- msd_context_destroy
- 命令缓冲区调度
- msd_context_execute_command_buffer
- msd_context_execute_immediate_commands
- msd_connection_set_notification_callback
- 创建信号量
- msd_semaphore_import
- msd_semaphore_destroy
- 故障处理
- 电源管理
硬件成功开机后,下一步是确定如何将现有 ioctl 映射到 MSD 入口点。
在大多数情况下,Linux DRI ioctl 与 MSD 函数之间的映射非常简单。内存管理是一个例外情况:在 Magma 中,ICD 会分配和映射内存,而不是 MSD(或内核驱动程序)。由于 MSD 必须将现有缓冲区导入 GPU 硬件,因此这可能会更改分配 VMO 的某些命令的流程。
如果这种方法不适用于某些类型的内存,驱动程序可以使用 Sysmem 堆来处理该内存的分配。客户端使用 Sysmem 分配内存,并使用常规 Magma 接口导入句柄。然后,MSD 可以与 sysmem 通信,以获取有关内存的更多信息。
驱动程序可能不需要实现所有函数。我们建议您根据 ICD 的需要逐步实现 MSD 函数。这可以在实现 MSD 函数时提供上下文,并有助于避免在不需要的函数上浪费精力。
此阶段的测试:
- 驱动程序专用单元测试(非硬件专用)
- 硬件专用驱动程序测试(请参阅示例)。这些测试应以最少的方式运行 GPU,例如写入寄存器或导致 GPU 修改内存位置。
- 使用 Magma 接口的驱动程序专用集成测试。
- magma-conformance-tests(Magma L0 的一部分)。
- magma-info-test(属于 Magma L0)。
构建 ICD
IHV ICD 必须移植到 Fuchsia。ICD 应使用 Bazel SDK 构建。ICD 构建可以移植到 Bazel,也可以封装在 Bazel 构建中。rules_foreign_cc 就是关于如何封装现有 build 的一个很好的示例。
由于 ICD abi 限制,ICD 必须与其所有依赖项进行静态关联。它们只能引用以下共享库:
libc.so
libzircon.so
这会限制 ICD 可以使用的依赖项。例如,以下是一些不允许使用的库和可能的替代库:
- HLCPP:可替换为新的 C++ 绑定。
- syslog:可替换为 syslog/structured_backend。
- async-default:必须始终明确指定异步调度程序。
- libtrace-engine:目前尚无替代方案。
- libsvc:可以替换为
fuchsia.io
调用和vk_icdInitializeOpenInNamespaceCallback
(见下文)。
在此阶段,您可以根据需要为所有其他引用添加桩。ICD 还必须关联到 SDK 中以 @fuchsia_pkg//pkg/magma_client 形式提供的 Magma 运行时库。
Vulkan 加载程序服务会从软件包中检索 ICD,并将其通告给 Vulkan 客户端。ICD 必须包含在包含元数据和清单 JSON 文件的 Fuchsia 软件包中,如加载器服务文档中所述。您可以使用 Bazel SDK 代码库命令将此软件包提供给设备。
如果 ICD 软件包包含在 universe 中,则可以通过执行 fx shell killall vulkan_loader.cm
重新加载。之后启动的组件将获得新的 ICD 软件包,而较旧的组件在创建 Vulkan 实例时将会失败。
ICD 必须导出一组特定的符号 - 请参阅 Vulkan ABI 定义。您应在此时实现它们。
此阶段的测试:
readelf -d
以确保除了libc.so
和libzircon.so
之外,它没有其他依赖项。- 使用
fx shell cat /svc/fuchsia.vulkan.loader.Loader
启动 Vulkan 加载器,并检查ffx inspect show core/vulkan_loader
以查看是否已加载。错误将发送到 syslog。 - 运行 vulkan_icd_load 测试。此测试将检查系统上的任何 ICD 是否正常运行,因此请确保在运行此测试之前,系统上没有其他 ICD。
将 ICD 连接到 Magma
此时,ICD 应使用提供给 vk_icdInitializeOpenInNamespaceCallback
的回调连接到 /loader-gpu-devices/class/gpu
目录。ICD 可以使用 fuchsia.io.Directory
FIDL 协议列出目录内容。此目录包含系统上所有 MSD 的设备节点,每个节点都命名为一个唯一的三位数。这些编号在启动期间保持稳定,但每当重新加载 MSD(例如设备重启时)时都可能会发生变化。
每个 magma 设备路径都可以使用 vk_icdInitializeOpenInNamespaceCallback
打开,并且生成的 zircon 通道可以使用 magma_device_import 提供给 libmagma。如果系统上有多个 magma 设备,则驱动程序必须将 magma_query
与 MAGMA_QUERY_VENDOR_ID
搭配使用,以确定要使用哪个设备。
在此阶段之后,magma_*
函数将正常运行,因此 ioctl()
调用可以逐步转换为等效的 Magma 调用。
此阶段的测试:
- vkreadback(绘制颜色,然后读回帧缓冲区值)。这是 Magma L0 的一部分。
- Vulkan 一致性测试。理想情况下,完成此阶段后,通过率应为 100%。如需了解详情,请参阅 Magma 测试策略。
移除不允许使用的符号
在关联 ICD 时,请使用版本脚本,以确保它仅公开 Fuchsia 系统 ABI 允许的符号。
只能使用 ICD 中符号许可名单中列出的符号。如需进行检查,请将许可名单与在 ICD 共享库上运行 llvm-nm -gD
获得的列表进行比较。
某些不受支持的文件操作可能会替换为对 vk_icdInitializeOpenInNamespaceCallback
提供的 OpenInNamespace
回调的调用。
此阶段的测试:
- icd_conformance 测试成功。
实现 Fuchsia 扩展程序
目前,ICD 无法与 scenic 搭配使用,并且不支持窗口系统集成。驱动程序必须实现特定于 Fuchsia 的 Vulkan 扩展。客户端驱动程序库应提供符合 Vulkan 1.0/1.1/1.2 规范的实现。
VK_FUCHSIA_external_memory
此扩展类似于 VK_KHR_external_memory_fd,允许从/向 VMO 导入/导出 VkDeviceMemory。此扩展已上游到 Vulkan 规范。
此阶段的测试:
vkext --gtest_filter=VulkanExtension.*
(Magma L0 的一部分)。
VK_FUCHSIA_external_semaphore
此扩展类似于 VK_KHR_external_semaphore_fd,允许向/从 zircon 事件导入/导出二进制信号量。此扩展已上游到 Vulkan 规范。
此阶段的测试:
vkext --gtest_filter=VulkanExtension.*
(Magma L0 的一部分)。vulkan-cts-zircon
(Vulkan CTS 的一部分)。
VK_FUCHSIA_buffer_collection
此扩展程序与 sysmem 交互,允许客户协商图片格式并分配内存。如需了解详情,请参阅 sysmem 文档。
此扩展目前处于开发阶段,可能会发生变化,但可以在 Fuchsia 内部的 Vulkan 头文件中找到。
此阶段的测试:
vkext
(Magma L0 的一部分)。- vkcube-on-fb(动画,使用 VK_KHR_swapchain - Magma L1 的一部分)。
验证
上述每个子部分中列出的所有测试都必须通过。如需了解详情和测试用例的完整列表,请参阅测试策略文档。
长期支持
必须使用硬件供应商提供的新代码更新 MSD 和 ICD。理想情况下,代码会上游,并且 GPU 供应商将使用 Zircon DDK 提供和维护系统驱动程序。