Writing an ICD is a problem in itself. Add to this Windows kernel interfaces, virtIO queues management, resources transfer between host and guest, and BOOM, you are lost. This brings us to our first step: something not efficient but simpler, API Forwarding.
Tasks
- Hook OpenGL calls
- Serialize function calls
- Send them to the miniport-driver, then the host
- De-serialize calls and execute them on the host.
- Send some data back to the guest
Realization
ICD
The ICD part (Userland) is pretty straightforward. Make your own opengl32.dll, serialize the calls. Now find a sweet function in gdi32.dll to throw your mumbo-jumbo on the kernel side. Fortunately, we have this:
NTSTATUS APIENTRY DxgkDdiEscape(
_In_ const HANDLE hAdapter,
_In_ const DXGKARG_ESCAPE *pEscape
)
{ ... }
A beautiful function available on both DOD and full display driver. It takes a pointer on an userland buffer, and send it to our display driver. Wait… userland buffer, no check, kernel part ? Mmmmm…. What could go wrong ?
Kernel part
To initialize a display driver, you must call a function: DxgkInitialize This function will take a big structure, containing function pointers to your driver. For a display only driver, you will have a reduced set of function to implement. And for a full featured driver, well…
Anyway, now the game is to run the driver, and see where we crash. Sadly we cannot just hope to add some functions, and run only using the working DOD code base. Windows wants something more, and the game is to find what, Yay ! Since we have a working DOD driver, let’s find how we could trick.
ICD <=> Kernel communication
We can register two type of driver: a DOD driver using DxgkInitializeDisplayOnlyDriver and DxgkInitialize. Windows will then know which kind of features each driver can support (fine tune will be done using query callbacks). Both drivers can implement DxgkDdiEscape. Great, we will fool Windows and use this DOD as a fully featured 3D driver ! WRONG !
Setup of the ICD part, sending everything through our escape functions ? check. But return values seams off. After investigation, and any function taking a userland buffer, I came to a conclusion: OpenGL ICD part cannot communicate with a DOD driver. Windows knows we are display only, and fall-back our ICD calls on it’s own driver.
So now, what’s the plan ? Let’s put this problem aside, and try to focus on the real part: create proper commands for the host.