从内核模式驱动程序发出 CPU 事件信号

在某些情况下,内核模式驱动程序 (KMD) 需要向 CPU 事件发出信号,以通知用户模式驱动程序 (UMD) 某些内容:例如:

  • 当 KMD 检测到其某个对象处于错误状态并需要通知 UMD 时。
  • 在 GPU 调试期间,KMD 需要与 UMD 通信,发生了某些事件。 对于具有 GPU 控制面板的 IHV,通过 KMD 向 CPU 事件发出信号允许 KMD 通知控制面板应用有关内部事件的信息。

通常,UMD 可以创建 CPU 事件,并将其 NT 句柄传递到转义专用数据中的 KMD。 此方法在 GPU 半虚拟化 (GPU-PV) 方案中不起作用,因为 NT 句柄不能跨虚拟机边界使用。

从 Windows 11 版本 21H2 (WDDM 3.0) 开始,WDDM API 已扩展,允许 UMD 创建可由 KMD 发出信号的 CPU 事件对象。 当 UMD 在主机上运行或使用 GPU-PV 在虚拟机中运行时,此功能都有效。

功能流

同步对象不能插入上下文队列。 它只能由 KMD 使用 DXGKCB_SIGNALEVENT发出信号。

用于处理 CPU 事件同步对象的用户模式 API

创建 KMD CPU 事件对象

KMD CPU 事件对象是使用以下项调用 D3D12DDICB_CREATESYNCHRONIZATIONOBJECT2 作为 GPU 同步对象创建的:

设置 SignalByKmd 标志后,将调用 DXGKDDI_CREATECPUEVENT 以创建 KMD CPU 事件对象。 请注意,创建同步对象时必须指定设备句柄。

同步对象不能用于信号和等待 API (D3DKMTSignalSynchronizationObjectD3DKMTWaitForSynchronizatioObject) 。 它只能由 KMD 发出信号,UMD 可以等待相应的 CPU 事件。

用于定义 KMD CPU 事件同步对象的用法的 UMD 转义

已知转义已添加到 D3DDDI_DRIVERESCAPETYPED3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE 用于通知 KMD 有关 KMD CPU 事件对象的预期使用情况。 已知转义通过设置 DXGKARG_ESCAPE::Flags.DriverKnownEscape = 1 来定义。 已知转义会从安全虚拟机发送到主机。

以下代码片段是一个用法示例。

D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE Command = {};
Command.EscapeType = D3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE;
Command.hSyncObject = SyncObjectHandle;
Command.Usage[0] = 1;

D3DKMT_ESCAPE Args = {};
Args.hAdapter = AdapterHandle;
Args.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
Args.Flags.DriverKnownEscape = 1;
Args.Flags.NoAdapterSynchronization = 1; // Prevent waking up the device from D3
Args.pPrivateDriverData = &Command;
Args.PrivateDriverDataSize = sizeof(Command);

NTSTATUS Status = D3DKMTEscape(&Args);

Dxgkrnl 将使用以下项调用 DXGKDDI_ESCAPE

  • hDevice 设置为用于创建同步对象的微型端口设备句柄
  • pPrivateDriverData 指向 D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE 结构
  • PrivateDriverDataSize 设置为 sizeof(D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE)

创建和销毁 KMD CPU 事件对象

以下 DDI 用于创建和销毁 KMD CPU 事件同步对象:

从 KMD 向 CPU 事件对象发出信号

为了向 CPU 事件 对象发出信号 ,KMD 在 IRQL <= DISPATCH_LEVEL 调用DXGKCB_SIGNALEVENT,并设置 如下DXGKARGCB_SIGNALEVENT 结构值:

  • hDxgkProcess 等于 0。

  • hEvent 等于传入到DXGKDDI_CREATECPUEVENTDxgkrnl CPU 事件对象句柄。

  • CpuEventObject 必须为 1。

  • 保留 必须为 0。