WDDM KMDOD驱动介绍及驱动初始化
KMOD驱动是微软提供的一个Display Only驱动。
WDDM KMOD驱动初始化
Windows驱动的入口函数是DriverEntry,所以显示Mini小端口驱动程序也不例外。
和其它Mini小端口驱动的入口函数实现一致,在其DriverEntry只做一件事,就是分配系统指定的一个结构体,然后调用框架提供的接口函数即可。
主要过程如下:
操作系统调用display miniport驱动程序的DriverEntry函数。
DriverEntry分配一个 DRIVER_INITIALIZATION_DATA 结构,并初始化其成员变量 Version为DXGKDDI_INTERFACE_VERSION 和其它指针变量(主要是回调函数)。
DriverEntry调用DxgkInitialize函数来加载Microsoft DirectX图形内核子系统(DXGKNL.sys公司)并为DirectX图形内核子系统提供指向显示微型端口驱动程序的其他入口点函数的指针。
DxgkInitialize返回后,DriverEntry将DxgkInitialize的返回值传播回操作系统。
DxgkInitialize函数是DispLib导出的一个函数,这是函数用于显卡Mini小端口驱动向Windows显示子系统注册。
函数原型如下:
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8)
NTSTATUS
DxgkInitializeDisplayOnlyDriver(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath,
_In_ PKMDDOD_INITIALIZATION_DATA KmdDodInitializationData
);
#endif // DXGKDDI_INTERFACE_VERSION
通过条件宏可以看到,这个函数支持的是Win8及以上操作系统。
DxgkInitializeDisplayOnlyDriver函数的参数总共3个,前两个直接使用DriverEntry的两个参数,而第三个参数KmdDodInitializationData是一个大大的结构体,这个结构体中包含了显示驱动向框架提供的一系列回调函数(Callback Function)。框架会在合适的时候调用这些回调函数,完成对应功能。
DxgkInitializeDisplayOnlyDriver结构体的声名如下:
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8)
typedef struct _KMDDOD_INITIALIZATION_DATA {
ULONG Version;
PDXGKDDI_ADD_DEVICE DxgkDdiAddDevice;
PDXGKDDI_START_DEVICE DxgkDdiStartDevice;
PDXGKDDI_STOP_DEVICE DxgkDdiStopDevice;
PDXGKDDI_REMOVE_DEVICE DxgkDdiRemoveDevice;
PDXGKDDI_DISPATCH_IO_REQUEST DxgkDdiDispatchIoRequest;
PDXGKDDI_INTERRUPT_ROUTINE DxgkDdiInterruptRoutine;
PDXGKDDI_DPC_ROUTINE DxgkDdiDpcRoutine;
PDXGKDDI_QUERY_CHILD_RELATIONS DxgkDdiQueryChildRelations;
PDXGKDDI_QUERY_CHILD_STATUS DxgkDdiQueryChildStatus;
PDXGKDDI_QUERY_DEVICE_DESCRIPTOR DxgkDdiQueryDeviceDescriptor;
PDXGKDDI_SET_POWER_STATE DxgkDdiSetPowerState;
PDXGKDDI_NOTIFY_ACPI_EVENT DxgkDdiNotifyAcpiEvent;
PDXGKDDI_RESET_DEVICE DxgkDdiResetDevice;
PDXGKDDI_UNLOAD DxgkDdiUnload;
PDXGKDDI_QUERY_INTERFACE DxgkDdiQueryInterface;
PDXGKDDI_CONTROL_ETW_LOGGING DxgkDdiControlEtwLogging;
PDXGKDDI_QUERYADAPTERINFO DxgkDdiQueryAdapterInfo;
PDXGKDDI_SETPALETTE DxgkDdiSetPalette;
PDXGKDDI_SETPOINTERPOSITION DxgkDdiSetPointerPosition;
PDXGKDDI_SETPOINTERSHAPE DxgkDdiSetPointerShape;
PDXGKDDI_ESCAPE DxgkDdiEscape;
PDXGKDDI_COLLECTDBGINFO DxgkDdiCollectDbgInfo;
PDXGKDDI_ISSUPPORTEDVIDPN DxgkDdiIsSupportedVidPn;
PDXGKDDI_RECOMMENDFUNCTIONALVIDPN DxgkDdiRecommendFunctionalVidPn;
PDXGKDDI_ENUMVIDPNCOFUNCMODALITY DxgkDdiEnumVidPnCofuncModality;
PDXGKDDI_SETVIDPNSOURCEVISIBILITY DxgkDdiSetVidPnSourceVisibility;
PDXGKDDI_COMMITVIDPN DxgkDdiCommitVidPn;
PDXGKDDI_UPDATEACTIVEVIDPNPRESENTPATH DxgkDdiUpdateActiveVidPnPresentPath;
PDXGKDDI_RECOMMENDMONITORMODES DxgkDdiRecommendMonitorModes;
PDXGKDDI_GETSCANLINE DxgkDdiGetScanLine;
PDXGKDDI_QUERYVIDPNHWCAPABILITY DxgkDdiQueryVidPnHWCapability;
//
// New DDI for the PresentDisplayOnly function.
//
PDXGKDDI_PRESENTDISPLAYONLY DxgkDdiPresentDisplayOnly;
//
// New DDIs for PnP stop/start support.
//
PDXGKDDI_STOP_DEVICE_AND_RELEASE_POST_DISPLAY_OWNERSHIP DxgkDdiStopDeviceAndReleasePostDisplayOwnership;
//
// New DDIs for system display support.
//
PDXGKDDI_SYSTEM_DISPLAY_ENABLE DxgkDdiSystemDisplayEnable;
PDXGKDDI_SYSTEM_DISPLAY_WRITE DxgkDdiSystemDisplayWrite;
//
// New DDI for the monitor container ID support.
//
PDXGKDDI_GET_CHILD_CONTAINER_ID DxgkDdiGetChildContainerId;
//
// New DDI for HW VSync.
//
PDXGKDDI_CONTROLINTERRUPT DxgkDdiControlInterrupt;
PDXGKDDISETPOWERCOMPONENTFSTATE DxgkDdiSetPowerComponentFState;
PDXGKDDIPOWERRUNTIMECONTROLREQUEST DxgkDdiPowerRuntimeControlRequest;
//
// New DDI for the surprise removal support.
//
PDXGKDDI_NOTIFY_SURPRISE_REMOVAL DxgkDdiNotifySurpriseRemoval;
//
// Display only drivers support P-State management.
//
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WDDM2_0)
PDXGKDDI_POWERRUNTIMESETDEVICEHANDLE DxgkDdiPowerRuntimeSetDeviceHandle;
#endif
} KMDDOD_INITIALIZATION_DATA, *PKMDDOD_INITIALIZATION_DATA;
#endif // DXGKDDI_INTERFACE_VERSION
第一个参数Version用来标识你所编写的显示驱动使用哪个版本的WDDM。WDDM现在为止,已经有很多版本了,在VS2019+WDK10环境下,其定义为WDDM2.6版本。
#if !defined(DXGKDDI_INTERFACE_VERSION)
#define DXGKDDI_INTERFACE_VERSION DXGKDDI_INTERFACE_VERSION_WDDM2_6
#endif // !defined(DXGKDDI_INTERFACE_VERSION)
这里我们列出所有的WDDM版本,大家可以欣赏一下:
#define DXGKDDI_INTERFACE_VERSION_VISTA 0x1052
#define DXGKDDI_INTERFACE_VERSION_VISTA_SP1 0x1053
#define DXGKDDI_INTERFACE_VERSION_WIN7 0x2005
#define DXGKDDI_INTERFACE_VERSION_WIN8 0x300E
#define DXGKDDI_INTERFACE_VERSION_WDDM1_3 0x4002
#define DXGKDDI_INTERFACE_VERSION_WDDM1_3_PATH_INDEPENDENT_ROTATION 0x4003
#define DXGKDDI_INTERFACE_VERSION_WDDM2_0 0x5023
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1 0x6003
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1_5 0x6010 // Used in RS1.7 for GPU-P
#define DXGKDDI_INTERFACE_VERSION_WDDM2_1_6 0x6011 // Used in RS1.8 for GPU-P
#define DXGKDDI_INTERFACE_VERSION_WDDM2_2 0x700A
#define DXGKDDI_INTERFACE_VERSION_WDDM2_3 0x8001
#define DXGKDDI_INTERFACE_VERSION_WDDM2_4 0x9006
#define DXGKDDI_INTERFACE_VERSION_WDDM2_5 0xA00B
#define DXGKDDI_INTERFACE_VERSION_WDDM2_6 0xB004
可以看到,WDDM的技术一直在发展,虽然每个版本的变化都是增加一些回调函数等成员,但对于程序的开发我们只也只需要实现我们所支持的功能即可,完全不用实现所有功能。
DxgkInitializeDisplayOnlyDriver结构体中的回调函数按功能可分为三类:
- 第一部分为关于PNP处理的回调函数,如PDXGKDDI_ADD_DEVICE,PDXGKDDI_START_DEVICE…,这些回调函数用于处理设备创建,起始,停止,电源相关和子设备的相关回调。
InitialData.DxgkDdiAddDevice = BddDdiAddDevice;
InitialData.DxgkDdiStartDevice = BddDdiStartDevice;
InitialData.DxgkDdiStopDevice = BddDdiStopDevice;
InitialData.DxgkDdiResetDevice = BddDdiResetDevice;
InitialData.DxgkDdiRemoveDevice = BddDdiRemoveDevice;
InitialData.DxgkDdiUnload = BddDdiUnload;
InitialData.DxgkDdiDispatchIoRequest = BddDdiDispatchIoRequest;
第二部分是与总线子设备上报相关的。类示总线设备的IRP_MN_QUERY_DEVICE_RELATIONS及有PNP子设备。
InitialData.DxgkDdiQueryChildRelations = BddDdiQueryChildRelations; InitialData.DxgkDdiQueryChildStatus = BddDdiQueryChildStatus; InitialData.DxgkDdiQueryDeviceDescriptor = BddDdiQueryDeviceDescriptor; InitialData.DxgkDdiSetPowerState = BddDdiSetPowerState; InitialData.DxgkDdiQueryAdapterInfo = BddDdiQueryAdapterInfo; InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
第三部分为与硬件相关的IRP处理,如中断及DPC处理的DxgkDdiInterruptRoutine和DxgkDdiDpcRoutine,有获取设备属性,读写设备帧内存、显示桌面内容(Present)等函数,如DxgkDdiQueryVidPnHWCapability,DxgkDdiPresentDisplayOnly,DxgkDdiSystemDisplayEnable,DxgkDdiSystemDisplayWrite。
InitialData.DxgkDdiInterruptRoutine = BddDdiInterruptRoutine; InitialData.DxgkDdiDpcRoutine = BddDdiDpcRoutine; InitialData.DxgkDdiQueryVidPnHWCapability = BddDdiQueryVidPnHWCapability; InitialData.DxgkDdiPresentDisplayOnly = BddDdiPresentDisplayOnly; InitialData.DxgkDdiSystemDisplayEnable = BddDdiSystemDisplayEnable; InitialData.DxgkDdiSystemDisplayWrite = BddDdiSystemDisplayWrite
- 第四部分则是与显卡输出相关的操作,如包括对鼠标位置的更新,显示器Mode的枚举和设置等函数:
InitialData.DxgkDdiSetPointerPosition = BddDdiSetPointerPosition; InitialData.DxgkDdiSetPointerShape = BddDdiSetPointerShape; InitialData.DxgkDdiIsSupportedVidPn = BddDdiIsSupportedVidPn; InitialData.DxgkDdiRecommendFunctionalVidPn = BddDdiRecommendFunctionalVidPn; InitialData.DxgkDdiEnumVidPnCofuncModality = BddDdiEnumVidPnCofuncModality; InitialData.DxgkDdiSetVidPnSourceVisibility = BddDdiSetVidPnSourceVisibility; InitialData.DxgkDdiCommitVidPn = BddDdiCommitVidPn; InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath; InitialData.DxgkDdiRecommendMonitorModes = BddDdiRecommendMonitorModes;
完整的代码如下:
extern "C"
NTSTATUS
DriverEntry(
_In_ DRIVER_OBJECT* pDriverObject,
_In_ UNICODE_STRING* pRegistryPath)
{
PAGED_CODE();
// Initialize DDI function pointers and dxgkrnl
KMDDOD_INITIALIZATION_DATA InitialData = {0};
InitialData.Version = DXGKDDI_INTERFACE_VERSION;
//系统IRP回调及WDM框架相关
InitialData.DxgkDdiAddDevice = BddDdiAddDevice;
InitialData.DxgkDdiStartDevice = BddDdiStartDevice;
InitialData.DxgkDdiStopDevice = BddDdiStopDevice;
InitialData.DxgkDdiResetDevice = BddDdiResetDevice;
InitialData.DxgkDdiRemoveDevice = BddDdiRemoveDevice;
InitialData.DxgkDdiUnload = BddDdiUnload;
InitialData.DxgkDdiDispatchIoRequest = BddDdiDispatchIoRequest;
//子设备枚举相关
InitialData.DxgkDdiQueryChildRelations = BddDdiQueryChildRelations;
InitialData.DxgkDdiQueryChildStatus = BddDdiQueryChildStatus;
InitialData.DxgkDdiQueryDeviceDescriptor = BddDdiQueryDeviceDescriptor;
InitialData.DxgkDdiSetPowerState = BddDdiSetPowerState;
InitialData.DxgkDdiQueryAdapterInfo = BddDdiQueryAdapterInfo;
InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
//硬件及中断处理相关
InitialData.DxgkDdiInterruptRoutine = BddDdiInterruptRoutine;
InitialData.DxgkDdiDpcRoutine = BddDdiDpcRoutine;
InitialData.DxgkDdiQueryVidPnHWCapability = BddDdiQueryVidPnHWCapability;
InitialData.DxgkDdiPresentDisplayOnly = BddDdiPresentDisplayOnly;
InitialData.DxgkDdiSystemDisplayEnable = BddDdiSystemDisplayEnable;
InitialData.DxgkDdiSystemDisplayWrite = BddDdiSystemDisplayWrite;
//图像输出相关
InitialData.DxgkDdiSetPointerPosition = BddDdiSetPointerPosition;
InitialData.DxgkDdiSetPointerShape = BddDdiSetPointerShape;
InitialData.DxgkDdiIsSupportedVidPn = BddDdiIsSupportedVidPn;
InitialData.DxgkDdiRecommendFunctionalVidPn = BddDdiRecommendFunctionalVidPn;
InitialData.DxgkDdiEnumVidPnCofuncModality = BddDdiEnumVidPnCofuncModality;
InitialData.DxgkDdiSetVidPnSourceVisibility = BddDdiSetVidPnSourceVisibility;
InitialData.DxgkDdiCommitVidPn = BddDdiCommitVidPn;
InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath;
InitialData.DxgkDdiRecommendMonitorModes = BddDdiRecommendMonitorModes;
NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &InitialData);
if (!NT_SUCCESS(Status))
{
BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status);
}
return Status;
}
从代码来看,KMOD中并没有完全实现KMDDOD_INITIALIZATION_DATA的结构体,所以它不能支持WDDM提供的全部Display相关的功能。比如D3D用户程序通过DC句柄和显示驱动进行交互的escape回调函数,这里就没有实现。对于没有实现的回调函数,在结构体中的对应函数指针应被初始化为NULL。