域分配
在硬件初始化过程中,Dxgkrnl 会为系统上的每个逻辑适配器创建一个域。 域会管理逻辑地址空间,并跟踪页表和其他必要的映射数据。 单个逻辑适配器中的所有物理适配器都属于同一个域。 Dxgkrnl 会通过新的分配回调例程来跟踪所有映射的物理内存,以及 VidMm 本身分配的任何内存。
域将在首次创建安全虚拟机时附加到设备上,如果使用上述注册表项,则会在设备启动后不久附加到设备上。
独占访问
IOMMU 域的连接和分离速度很快,但目前还不是原子式的。 由于它不是原子式的,因此在交换到具有不同映射的 IOMMU 域时,通过 PCIe 发出的事务并不能保证正确转换。
为了处理这种情况,从 Windows 10 版本 1803 (WDDM 2.4) 开始,KMD 必须实现以下 DDI 对,以便 Dxgkrnl 调用:
- 将调用 DxgkDdiBeginExclusiveAccess,以通知 KMD IOMMU 域切换即将发生。
- DxgkDdiEndExclusiveAccess 在 IOMMU 域切换完成后调用。
这些 DDI 构成了开始/结束配对,其中 Dxgkrnl 会要求硬件在总线上保持静默。 每当设备切换到新的 IOMMU 域时,驱动程序必须确保其硬件保持无提示。 也就是说,驱动程序必须确保在这两次调用之间不会从设备读取或写入系统内存。
在这两个调用之间,Dxgkrnl 做出以下保证:
- 计划程序已挂起。 所有活动工作负荷都被刷新,并且不会向硬件发送或在硬件上计划新的工作负荷。
- 没有进行其他 DDI 调用。
作为这些调用的一部分,驱动程序可以选择在独占访问期间禁用和禁止中断(包括 Vsync 中断),即使没有 OS 的明确通知。
Dxgkrnl 会确保硬件上计划的任何待处理工作均已完成,然后进入该独占访问区域。 在此期间,Dxgkrnl 会将域分配给设备。 在这些调用之间,Dxgkrnl 不会对驱动程序或硬件提出任何请求。
DDI 更改
为支持基于 IOMMU 的 GPU 隔离,对 DDI 进行了以下更改:
- IoMmuSecureModeSupported 上限已被添加到 DXGK_VIDMMCAPS 中
- 添加了 DXGKQAITYPE_FRAMEBUFFERSAVESIZE 枚举值和 DXGK_FRAMEBUFFERSAVEAREA 结构
- 添加了 DXGKQAITYPE_HARDWARERESERVEDRANGES 枚举值和 DXGK_HARDWARERESERVEDRANGE 结构
- 添加了 DXGK_MEMORY_CACHING_TYPE
- 内核模式驱动程序必须实现以下 DDI:
- DxgkDdiBeginExclusiveAccess
- DxgkDdiEndExclusiveAccess
- 添加了以下 Dxgkrnl 回调和回调参数结构。 回调函数必须在 IRQL <= APC_LEVEL 时调用。 从 WDDM 3.2 开始,调用这些函数中的任何一个驱动程序都会根据此要求进行验证,并在 IRQL 为 DISPATCH_LEVEL 或更高时进行错误检查。
回调 | 关联的回调结构 |
---|---|
DXGKCB_ALLOCATECONTIGUOUSMEMORY | DXGKARGCB_ALLOCATECONTIGUOUSMEMORY |
DXGKCB_FREECONTIGUOUSMEMORY | DXGKARGCB_FREECONTIGUOUSMEMORY |
DXGKCB_ALLOCATEPAGESFORMDL | DXGKARGCB_ALLOCATEPAGESFORMDL |
DXGKCB_FREEPAGESFROMMDL | DXGKARGCB_FREEPAGESFROMMDL |
DXGKCB_MAPMDLTOIOMMU | DXGKARGCB_MAPMDLTOIOMMU |
DXGKCB_UNMAPMDLFROMIOMMU | DXGKARGCB_UNMAPMDLFROMIOMMU |
DXGKCB_PINFRAMEBUFFERFORSAVE | DXGKARGCB_PINFRAMEBUFFERFORSAVE |
DXGKCB_UNPINFRAMEBUFFERFORSAVE | DXGKARGCB_UNPINFRAMEBUFFERFORSAVE |
DXGKCB_MAPFRAMEBUFFERPOINTER | DXGKARGCB_MAPFRAMEBUFFERPOINTER |
DXGKCB_UNMAPFRAMEBUFFERPOINTER | DXGKARGCB_UNMAPFRAMEBUFFERPOINTER |
内存分配和映射 IOMMU
Dxgkrnl 向内核模式驱动程序提供上表中的前六个回调,使其能够分配内存并将其重新映射到 IOMMU 的逻辑地址空间。 这些回调函数模拟了 Mm API 接口提供的例程。 它们为驱动程序提供 MDL 或指针,用于描述映射到 IOMMU 的内存。 这些 MDL 会继续描述物理页面,但 IOMMU 的逻辑地址空间是以相同的地址映射的。
Dxgkrnl 会跟踪对这些回调的请求,以帮助确保驱动程序不会发生泄漏。 分配回调会提供另一个句柄,作为输出的一部分,该句柄必须提供给相应的释放回调。
对于无法通过所提供的分配回调之一进行分配的内存,我们提供了 DXGKCB_MAPMDLTOIOMMU 回调,以便通过 IOMMU 来跟踪和使用驱动程序管理的 MDL。 使用此回调的驱动程序有责任确保 MDL 的有效期超过相应的取消映射调用。 否则,取消映射调用将产生未定义的行为。 这种未定义的行为可能会导致 MDL 页面的安全性受损,因为 Mm 页面在取消映射时会被重新使用。
VidMm 会自动管理它在系统内存中创建的任何分配,例如,DdiCreateAllocationCb、监控围栏等。 驱动程序无需执行任何操作就能让这些分配生效。