Ah, autumn. Always my favorite time of year. The crispness in the air. The leaves turning colors, inviting all the elders (like Mom) up here to see the trees. Time to curl up with an interesting selection from the bookshelf and lie by the radiator. It starts up interestingly enough.
In the beginning, there was the loading of vga.sys. And things were pretty good. But, apparently, not good enough. In an apparent fit of pique, vga.sys is banished (unloaded) from NT. But, there always is tomorrow. So, there is a second loading of vga.sys. And things are swell again. These twisting and turning of events seemed odd, so I retreated to the preface of this tome to see what was going on. However, there seemed to be a bunch of pages missing. So, I brought out my trusty (for random values of “trusty”) Windbg, clicked on the assembler window and set to task, since graphics startup always seemed to be a bit of a mystery to me.
Finding no vga.dbg in the standard distribution means that more clever reverse engineering techniques would have to be employed (e.g., dig deeper with dummy drivers, assembler reading, register poking, etc.). When all was said and done, it turns out that vga.sys gets called twice, being invoked with different registry paths -- one for VgaStart and one for VgaSave. Looking back in the registry, we note that there are actually 3 video groups : VideoInit, Video and VideoSave. Our display drivers (DDIs) are always in the Video group. The default VGA drivers are normally always in the VideoInit and VideosSave groups. So, since VideoInit is the first group in the ServiceGroupOrder to be invoked, and the only service in this group is VgaStart, the registry path for the first invocation of vga.sys is VgaStart. However, this invocation of vga.sys seems to take a failure path. I was at a loss as to exactly why this first invocation of vga.sys with the VgaStart registry path fails.
For the second invocation, the VgaSave registry path is specified, but this time it seems that much more happens, and the return value is success. As with the VideoInit group, the only entry in the VideoSave group is VgaSave service. Looking into the registry for the VgaStart and VgaSave services, I found some differences. Most notably, VgaStart seems to have a FORCEVGA flag set to 1. Setting this flag to 0 had no effect, but deleting this flag loads the VGA driver, seemingly canceling the subsequent load of the Video group (my DDI). Strange.
So, the facts are that vga.sys gets loaded twice. The first time as a member of the VideoInit group (with the FORCEVGA flag defined in the VgaStart service), resulting in a return back to the VideoPort driver of STATUS_UNSUCCESSFUL, and subsequent vga.sys image unload. The second time, vga.sys is loaded as part of the VideoSave group and that load succeeds. Of course, the VideoSave load is after the “real” miniport driver for my spiffy 3D board gets loaded as part of the Video group.
While the existence of the VideoStart group is not clear, there seems to be a good reason for the VideoSave group. Suppose your newly compiled DDI fails to load? While they could have hard-coded usage of the VGA driver as the “backup” for such situations, the MS folks have provided a hook to enable generic fallback video support.
So, one question sort of answered, but spawning yet more questions for the next disassembler explorer. Just why is FORCEVGA still in the registry if it’s not being used? What is its purpose in NT? Is VideoSave group only used for failover? Why is there no blue food?.
Well, onto the other half – the DDI. This is loaded by our provider, GDI, also known as Win32K.sys to her friends. Now, Win32K.dbg does exist, so we can actually stop and look around at the roses. And, from the looks of it, our DrvEnableDriver() also gets called twice. More than a coincidence? I mean, Kennedy had a secretary named Lincoln, and Lincoln had a secretary named Kennedy. The letters in the abbreviation for Windows NT are just one off from VMS. Hmmmm…
Well, in this case there seems to be much less of a conspiracy. It looks like the first load of our DDI is to determine what modes are supported, GDI calling our DrvGetModes(). Given that information, however, GDI promptly unloads the DDI. It munches on the values, and then determines if this DDI is good enough to be used, based on the mode information. GDI then loads the selected DDI -- the same one it just unloaded. Interesting. This enforces the old design rule on the DDI writers – don’t use global variables for state since the PDEV (which should contain all of your DDI’s state) may change from underneath you. That is, all the context a DDI ever needs should be in its PDEV. So, while this double loading may seem like a “don’t care” situation for most good DDI writers, all the GDI filter driver writers out there should be aware that this occurs.
Well, some of the mysteries of graphics device driver loading are sort of solved. I close the chartreuse covered book, figuring to ruminate over the new knowledge of the mechanics of the graphics device loading and spy the book’s dedication: “This was brought to you by the OsrLoadOrder utility, the Osr GDI Filter Driver, with additional support from MS Windbg and its disassembly window, Mom, and from listeners like you”. Guess that’s a shameless plug to get the PBS pledge cards in.