如需大致了解 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。供应商专用驱动程序将首先绑定,并导出 MSD 的 banjo 接口以开启/关闭 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_connection_set_notification_callback
- 创建信号量
- msd_semaphore_import
- msd_semaphore_destroy
- 故障处理
- 电源管理
硬件成功开机后,下一步是决定如何将现有 ioctl 映射到 MSD 入口点。
在大多数情况下,Linux DRI ioctl 与 MSD 函数之间的映射关系非常简单。不过,内存管理方面存在一个例外:在 Magma 中,分配和映射内存的是 ICD,而不是 MSD(或内核驱动程序)。这可能会改变分配 VMO 的某些命令的流程,因为 MSD 必须将已有的缓冲区导入到 GPU 硬件中。
如果该方法不适用于某些类型的内存,驱动程序可能会使用 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 build 可以移植到 Bazel,也可以封装在 Bazel build 中。rules_foreign_cc 是一个很好的示例,展示了如何封装现有 build。
由于 ICD ABI 限制,ICD 必须静态关联到其所有依赖项。它们只能引用以下共享库:
libc.solibzircon.so
这会限制 ICD 可以使用的依赖项。例如,以下是一些不被允许的库和可能的替代库:
- HLCPP:必须替换为新的 C++ 绑定。
- syslog:可替换为 syslog/structured_backend。
- async-default:必须始终明确指定异步调度程序。
- libtrace-engine:目前还没有替代方案。
- libsvc:可替换为
fuchsia.io调用和vk_icdInitializeOpenInNamespaceCallback(见下文)。
在此阶段,您可以根据需要将所有其他引用存根化。ICD 还必须链接到 SDK 中提供的 Magma 运行时库,即 @fuchsia_pkg//pkg/magma_client。
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 时(例如在设备重新启动时)可能会发生变化。
可以使用 vk_icdInitializeOpenInNamespaceCallback 打开每个 magma 设备路径,并使用 magma_device_import 将生成的 zircon 渠道提供给 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 事件或从 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 提供并维护系统驱动程序。