Installing two separate .sys drivers for the same hardware with a single .inf file?

I’ve written a KMDOD driver to support the Desktop Window Manager integration with a toy GPU I’ve created, which works great and allows me to use the hardware to display the Windows OS. Now, the GPU is not capable of supporting a full-stack WDDM driver (with D3D integration and all that), which means that I am unable to use WDDM’s DMA interfaces to have usermode code DMA data to and from the device. However, I have written a WDF driver that does not do any of the DWM integration but does allow me to utilize the device’s DMA from usermode code.

I’m trying to integrate the two drivers. My plan is to have the KMDOD expose a device interface that the WDF driver can wait for and once it’s notified that the interface exists, I can use that interface to communicate between the two. I’d like to have a single .inf file that installs and starts up both drivers simultaneously.

I’ve tried simply adding two separate AddService tokens to the [Device.NT.Services] section, where the WDDM one (the “primary”) is specified as the SPSVCINST_ASSOCSERVICE, and the WDF (the “secondary”) has no flags set, but I can never seem to get it to load the secondary driver.

Is there something else I need to do to get it to load both .sys files when my hardware is detected by the system?

That isn’t going to work. Just install the second driver independently - is it even a pnp driver?

Both are PnP drivers. Originally I did try having two entirely separate ones, but VisualStudio’s driver installation stuff kept uninstalling one to install the other, I’m assuming due to the fact that the same hardware ID was used for both. I could not find any mechanism to change that behavior, so I figured it was probably not the best route to take. It certainly would not work at all for the debugging workflow I currently have (as hacky as it may be) - I have to completely uninstall the device and driver, run deployment from VS, tell it to chill out when it complains it failed, set up the debugger as I want it, then I can finally search for new devices from the device manager and hit the moment my driver’s binary is loaded. I need both to not currently exist while I’m actively debugging in order to debug the behavior when they both start up at once.

I’ve also already tried doing both the WDDM and WDF stuff in the exact same driver binary. Doing so left the WDDM stuff working, but the DeviceAdd event was never called from the WDF side. I assume that WDDM is eating all of those notifications to make its own stuff work.

I’m more than willing to change this workflow, so long as whatever it’s changed to also gives me the ability to break at the moment the driver images are loaded and push-button installation. It would be preferable to have a single entry in the DeviceManager for my device, but so long as it works I’m fine with there being one for each co-driver.

It IS possible to have multiple devices matched in a single INF, but all of them must be the same install class (the “Class” and “ClassGUID” entries in the [Version] section). Your second driver doesn’t fit into the display class, so you can’t combine them.

I don’t want to match multiple devices, I want to match a single device but install two separate driver binaries for it. When I was trying to use two separate driver INF files, both had the exact same Class and ClassGUID entries (Display, as you noted). Each would show up individually in the Display Adapters section of the Device Manager, as expected. While trying to deploy and debug them, VS’s deployment kept uninstalling one driver when I would go to deploy the other, which I assumed meant that it really wasn’t happy with me trying to have two separate driver INF files for the same hardware ID. If that is NOT the case, perhaps it is worth it for me to figure out how to script the install of the drivers so that I can avoid VS’s attempt to remove the companion driver?

Yeah, that’s impossible. A single hardware identifier can only have one driver. Now, you CAN certainly add filter drivers in your INF. I think KMDOD drivers are PnP and thus would support filter drivers. That’s the only way to have multiple drivers on one device – stack them. Otherwise, the system can’t know where to send requests.

Ah, okay! So a filter driver seems like it should do exactly what I need - I can install it above the WDDM driver and have it intercept just the requests I need to expose the DMA file and associated objects, and it should also be able to directly query the driver below it for the interface the WDDM driver exposes with which I can connect the two. Great!

Now… I (think) I’ve set up the WDF driver as a filter in its INF file, but it still isn’t getting loaded. Here’s the INF for the WDDM driver, followed by what I’ve got so far for the filter. I copied all the filter stuff from one of the Microsoft samples, but it was quite old and perhaps isn’t actually the correct way to do things in 2023?

;
; FuryGPU.inf
;

[Version]
Signature = "$WINDOWS NT$"
Class = %ClassName%
ClassGuid = {4d36e968-e325-11ce-bfc1-08002be10318}
Provider = %ManufacturerName%
CatalogFile = FuryGPU.cat
;DriverVer= ; Is set via stampinf property page
PnpLockdown = 1

; ================= Class section =====================

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
FuryGPU_KMD.sys = 1

[DestinationDirs]
FuryGPU.Miniport = 12 ; drivers

;*****************************************
; Install Section
;*****************************************

[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[ControlFlags]
ExcludeFromSelect=*

[Standard.NT$ARCH$]
%FuryGPU.DeviceDesc%=FuryGPU_Device, PCI\VEN_DBDB&DEV_DB01

[FuryGPU_Device.NT]
FeatureScore = FB
CopyFiles = FuryGPU.Miniport

[FuryGPU.Miniport]
FuryGPU_KMD.sys,,,0x100

[FuryGPU_Device.NT.HW]
AddReg = FuryGPU_MSI_HardwareDeviceSettings
AddReg = FuryGPU_Security_Settings

[FuryGPU_MSI_HardwareDeviceSettings]
HKR,Interrupt Management,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1

[FuryGPU_Security_Settings]
HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"

;-------------- Service installation
[FuryGPU_Device.NT.Services]
AddService = FuryGPU,%SPSVCINST_ASSOCSERVICE%, FuryGPU_WDDM_Service_Inst, FuryGPU_EventLog_Inst

[FuryGPU_EventLog_Inst]
AddReg = FuryGPU_EventLog_AddReg

[FuryGPU_EventLog_AddReg]
HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\FuryGPU_KMD.sys"
HKR,,TypesSupported,%REG_DWORD%,7

; -------------- FuryGPU driver install sections
[FuryGPU_WDDM_Service_Inst]
DisplayName    = %FuryGPU.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\FuryGPU_KMD.sys

[Strings]
REG_MULTI_SZ  = 0x00010000
REG_EXPAND_SZ = 0x00020000
REG_DWORD     = 0x00010001
SPSVCINST_ASSOCSERVICE = 0x00000002

ManufacturerName = "Dylan Barrie"
ClassName = "Display"
DiskName = "FuryGPU Installation Disk"
FuryGPU.DeviceDesc = "FuryGPU"
FuryGPU.SVCDESC = "FuryGPU Service"

And the WDF filter driver’s INF:

;
; FuryGPU_WDF.inf
;

[Version]
Signature="$WINDOWS NT$"
Class=%ClassName%
ClassGuid={4d36e968-e325-11ce-bfc1-08002be10318}
Provider=%ManufacturerName%
CatalogFile=FuryGPU_WDF.cat
;DriverVer= ; TODO: set DriverVer in stampinf property pages
PnpLockdown=1

; ================= Class section =====================

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
FuryGPU_WDF.sys = 1

[DestinationDirs]
FuryGPU_WDF.Wdf = 12 ; drivers

;*****************************************
; Install Section
;*****************************************

[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[Standard.NT$ARCH$]
%FuryGPU_WDF.DeviceDesc%=FuryGPU_WDF_Filter, PCI\VEN_DBDB&DEV_DB01

[FuryGPU_WDF_Filter.NT]
Include = FuryGPU.inf
Needs = FuryGPU_Device.NT
CopyFiles=FuryGPU_WDF.Wdf

[FuryGPU_WDF.Wdf]
FuryGPU_WDF.sys,,,0x100

[FuryGPU_WDF_Filter.NT.HW]
Include = FuryGPU.inf
Needs = FuryGPU_Device.NT.HW

;-------------- Service installation
[FuryGPU_WDF_Filter.NT.Filters]
AddFilter = FuryGPU_WDF_Filter,,FuryGPU_WDF_Filter_Inst

[FuryGPU_WDF_Filter_Inst]
FilterPosition = Upper

[FuryGPU_WDF_Filter.NT.Services]
Include = FuryGPU.inf
Needs = FuryGPU_Device.NT.Services
AddService = FuryGPU_WDF,, FuryGPU_WDF_Service_Inst, FuryGPU_WDF_EventLog_Inst

[FuryGPU_WDF_EventLog_Inst]
AddReg = FuryGPU_WDF_EventLog_AddReg

[FuryGPU_WDF_EventLog_AddReg]
HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\FuryGPU_WDF.sys"
HKR,,TypesSupported,%REG_DWORD%,7

; -------------- FuryGPU driver install sections
[FuryGPU_WDF_Service_Inst]
DisplayName    = %FuryGPU_WDF.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\FuryGPU_WDF.sys

[Strings]
REG_MULTI_SZ  = 0x00010000
REG_EXPAND_SZ = 0x00020000
REG_DWORD     = 0x00010001
SPSVCINST_ASSOCSERVICE = 0x00000002

ManufacturerName="Dylan Barrie"
ClassName="Display"
DiskName = "FuryGPU (WDF) Installation Disk"
FuryGPU_WDF.DeviceDesc = "FuryGPU (WDF)"
FuryGPU_WDF.SVCDESC = "FuryGPU (WDF) Service"

My debugging process starts with having the device uninstalled and the drivers removed. I then have VisualStudio deploy the WDDM driver and start debugging (which fails because it can’t find the device), followed by doing the same for the filter driver. Once the filter driver is installed (but again, fails to start because the device was removed) and I’m actively debugging, I use the “Scan for hardware changes” command in the Device Manager to trigger the driver stack bring-up. When I do this it successfully starts the WDDM driver but does NOT start the WDF filter.

What am I doing wrong?

You can only Include first party INFs. Third party INFS don’t have a predictable name (oemx.inf). Simply install the kmdd and filter driver in the same inf.

EDIT: This is all left for context, but I figured it out. See end of post.

Forgive my ignorance, but it’s pretty difficult to find good examples (or I’m just failing miserably to find them) on how to set up INF files correctly. I’m trying to combine them into a single one as suggested, but I’m not sure how to indicate which of the two services belongs to the filter and which one belongs to the base driver.

If I try to specify two different names that point to the same hardware ID path (so I can configure one with the base driver and the second with the filter driver), I get a validation error:

[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[Standard.NT$ARCH$]
%FuryGPU.DeviceDesc%=FuryGPU_Device, PCI\VEN_DBDB&DEV_DB01
%FuryGPU.FilterDesc%=FuryGPU_Filter, PCI\VEN_DBDB&DEV_DB01 ; This causes a validation error

I’m trying to use the FuryGPU_Filter name to specify AddFilter to add it as a filter for the base driver, and to add its specific service:

[FuryGPU_Filter.NT.Filters]
AddFilter = FuryGPU_WDF_Filter, FuryGPU_Filter_Inst

[FuryGPU_Filter_Inst]
FilterPosition = Upper

[FuryGPU_Filter.NT.Services]
AddService = FuryGPU_Flt,, FuryGPU_WDF_Service_Inst, FuryGPU_WDF_EventLog_Inst

If I can’t do it this way, how do I indicate which of the driver binaries is the base and which is the filter?

EDIT: I did not understand the connection between the name specified in the AddFilter command and the name of the service. Also, it seems that the INF validation VS 2019 is doing is a bit too eager, and fails if you have a [DDInstall.NT.Filters] section, though I did not dig any further in to figure out why.

I figured out how to configure the INF properly, and now it is loading both driver binaries using a single INF! Here’s everything that’s in the INF file:

;
; FuryGPU.inf
;

[Version]
Signature = "$WINDOWS NT$"
Class = %ClassName%
ClassGuid = {4d36e968-e325-11ce-bfc1-08002be10318}
Provider = %ManufacturerName%
CatalogFile = FuryGPU.cat
;DriverVer= ; Is set via stampinf property page
PnpLockdown = 1

; ================= Class section =====================

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
FuryGPU_KMD.sys = 1
FuryGPU_WDF.sys = 1

[DestinationDirs]
FuryGPU.MiniportFiles = 12 ; drivers

;*****************************************
; Install Section
;*****************************************

[ControlFlags]
ExcludeFromSelect=*

[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[Standard.NT$ARCH$]
%FuryGPU.DeviceDesc%=FuryGPU_Device, PCI\VEN_DBDB&DEV_DB01

;-------------- WDDM Device
[FuryGPU_Device.NT]
FeatureScore = FB
CopyFiles = FuryGPU.MiniportFiles;, FuryGPU.UserModeFiles
;AddReg = FuryGPU_AddReg

[FuryGPU.MiniportFiles]
FuryGPU_KMD.sys,,,0x100
FuryGPU_WDF.sys,,,0x100

[FuryGPU_Device.NT.HW]
AddReg = FuryGPU_MSI_HardwareDeviceSettings
AddReg = FuryGPU_Security_Settings
AddReg = FuryGPU_FilterReg

[FuryGPU_MSI_HardwareDeviceSettings]
HKR,Interrupt Management,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1

[FuryGPU_Security_Settings]
HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"

; NOTE: Had to use this method instead of the new AddFilter method. My version of VS might be too old to support it properly.
[FuryGPU_FilterReg]
HKR,,"UpperFilters", 0x00010008, "FuryGPU_Filter"

;-------------- Service installation
[FuryGPU_Device.NT.Services]
AddService = FuryGPU,%SPSVCINST_ASSOCSERVICE%, FuryGPU_Service_Inst, FuryGPU_EventLog_Inst
AddService = FuryGPU_Filter,, FuryGPU_Filter_Service_Inst, FuryGPU_Filter_EventLog_Inst

[FuryGPU_EventLog_Inst]
AddReg = FuryGPU_EventLog_AddReg

[FuryGPU_Filter_EventLog_Inst]
AddReg = FuryGPU_EventLog_AddReg

[FuryGPU_EventLog_AddReg]
HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll"
HKR,,TypesSupported,%REG_DWORD%,7

[FuryGPU_Service_Inst]
DisplayName    = %FuryGPU.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\FuryGPU_KMD.sys

[FuryGPU_Filter_Service_Inst]
DisplayName    = %FuryGPU.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\FuryGPU_WDF.sys

[Strings]
REG_MULTI_SZ  = 0x00010000
REG_EXPAND_SZ = 0x00020000
REG_DWORD     = 0x00010001
SPSVCINST_ASSOCSERVICE = 0x00000002

ManufacturerName = "Dylan Barrie"
ClassName = "Display"
DiskName = "FuryGPU Installation Disk"
FuryGPU.DeviceDesc = "FuryGPU Device"
FuryGPU.SVCDESC = "FuryGPU Service"

Thanks for the pointers, everyone!

The only time you can have two INFs both load on the same ID is if one of them is an extension INF. The extension INF install data will be added to the base INF. You could have done it that way, but a single INF works too.

[FuryGPU_Filter.NT.Filters]
AddFilter = XXXXX, FuryGPU_Filter_Inst

[FuryGPU_Filter_Inst]
FilterPosition = Upper

[FuryGPU_Filter.NT.Services]
AddService = YYYYY,, FuryGPU_WDF_Service_Inst, FuryGPU_WDF_EventLog_Inst

In order for AddFilter to work, XXXXX and YYYYY need to be the same name. They’re different in your above code