In this blogpost I’m going to talk about how the PNP Manager is able to discover devices and install/run the appropriate drivers from INF files.
Some terminology:
- Physical Device Object (PDO) – the bus driver uses it to represent the connection between the device and the bus
- Function Device Object (FDO) – the function driver uses it to manage the functionality of the device
- Plug and Play (PNP) – primary component that supports PNP function ability and is responsible for device detection/enumeration, maintaining the device tree, and adding/removing devices whilst the system is running
Driver types:
- Bus driver – manages the connection between the hardware and OS
- Function driver – it understands the details of the hardware, does I/O operations, handles interrupts, and allows user to control the device
- Filter driver – modifies behaviour of an existing function driver. Upper filters can see IRPs before function driver, and Lower filters can modify stream of bus operations that the function driver is trying to perform
Bus enumeration and IRP_MN
- Bus driver detects the insertion of hardware
- Bus driver calls the kernel API nt!IoInvalidateDeviceRelations(), to notify PNP Manager that child devices of the bus have changed
- PNP Manager sends IRP_MN_QUERY_DEVICE_RELATIONS to the bus driver, to get an up-to-date list of PDOs for the child devices
- PNP Manager sends IRP_MN_QUERY_ID to the bus driver to obtain Device ID of the hardware
- PNP Manager locates INF/PNF in driver store, runs the script (Install, CopyFiles, AddService), and the Memory Manager loads the driver into memory and calls its DriverEntry()
- PNP Manager then calls AddDevice() to inform the driver that a new instance of the device has been discovered
- PNP Manager sends IRP_MN_QUERY_RESOURCE_REQUIREMENTS to the bus driver asking for hardware’s resource requirements
- PNP Manager configures the hardware with a set of resource arbitrators to assign resources to the device
- PNP Manager sends IRP_MN_START_DEVICE to the driver
Fig 1 below shows just the state transitions of a PNP device and the IRP minor codes associated.
Device enumeration
PNP Manager begins device enumeration with the root device (a virtual bus driver), which represents all devices within the system and acts as a bus driver for non-PNP drivers and the Hardware Abstraction Layer (HAL). The HAL acts as a bus driver that enumerates devices connected to the motherboard. An example of a device tree can be seen below in Fig 2, where each node in the tree is a devnode
. As you can see the PCI bus also serves as the primary bus which USB, ISA, SCSI buses, etc are connected to. One can also open “Device Manager” to visually see the tree and the devnodes.
Using windbg you can also issue !devnode 0 1
to dump the devnodes from root.
Dumping IopRootDeviceNode (= 0xffff9382c8fced20) DevNode 0xffff9382c8fced20 for PDO 0xffff9382c8fcfe40 InstancePath is "HTREE\ROOT\0" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffff9382c8fd1d20 for PDO 0xffff9382c8fc9e40 InstancePath is "ROOT\volmgr\0000" ServiceName is "volmgr" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) [..] DevNode 0xffff9382c8fd6b00 for PDO 0xffff9382c8fd6e40 InstancePath is "ROOT\ACPI_HAL\0000" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffff9382c8fa3d20 for PDO 0xffff9382c8fd2bf0 InstancePath is "ACPI_HAL\PNP0C08\0" ServiceName is "ACPI" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffff9382ca5c1d20 for PDO 0xffff9382ca4f3870 InstancePath is "ACPI\PNP0A03\2&daba3ff&0" ServiceName is "pci" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffff9382ca56bb20 for PDO 0xffff9382ca4bb060 InstancePath is "PCI\VEN_8086&DEV_7190&SUBSYS_197615AD&REV_01\3&61..." State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) [..] DevNode 0xffff9382ca55bd20 for PDO 0xffff9382ca49a060 InstancePath is "PCI\VEN_15AD&DEV_07A0&SUBSYS_07A015AD&REV_01\3&61..." ServiceName is "pci" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffff9382ca4a19f0 for PDO 0xffff9382ca4a5060 InstancePath is "PCI\VEN_8086&DEV_10D3&SUBSYS_07D015AD&REV_00\000C..." ServiceName is "e1iexpress" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) [..]
If you want to see the other devices attached below and above a driver in the device stack, you can issue !devstack <deviceobject>
and looking at the disk.sys driver, we see partmgr above us, and LSI_SAS is below us.
0: kd> !drvobj disk [..] Device Object list: ffff9382ca7b4060 0: kd> !devstack ffff9382ca7b4060 !DevObj !DrvObj !DevExt ObjectName ffff9382ca7b49d0 \Driver\partmgr ffff9382ca7b4b20 > ffff9382ca7b4060 \Driver\Disk ffff9382ca7b41b0 DR0 ffff9382ca52d380 \Driver\LSI_SAS ffff9382ca52d4d0 0000006c !DevNode ffff9382ca533d20 : DeviceInst is "SCSI\Disk&Ven_VMware_&Prod_VMware_Virtual_S\5&1ec51bf7&0&0000" ServiceName is "disk"
PNP Manager finding drivers for HWID
After an INF is installed, and once the device is inserted, 3 important locations in registry will be updated thereby not requiring an INF load on subsequent device insertions.
- HKLM\System\CCS\Enum – (hardware key) settings for known hardware devices
- HKLM\System\CCS\Services – (software key) settings for drivers
- HKLM\System\CCS\Control\Class – (class key) settings for device types
PNP uses Device Instance IDs (DIID), which are a combination of Device ID and Instance ID. Where a Device ID is a unique name for a vendor device, and Instance ID is either a bus relative location or a globally unique descriptor like a serial number.
This DIID is used to find information in the registry \Enum\<Enumerator>\<DevID>\<InstId>. For example, seen below in Fig 3 operating on the PCI Bus, is my Intel network card, with a Device ID of “VEN_8086&DEV_10D3&SUBSYS_07D015AD&REV_00” and an Instance ID of “000C29FFFF0D981300”.
And the Service seen above in Fig 3 is e1express associated with this driver can then be found in the Services\subkey seen below in Fig 4. Now on a reboot, and when the PNP device is enumerated and found, the PNP Manager will search the regkeys as mentioned, and on finding this service driver, it would load e1i63x64.sys, call it’s DriverEntry() then AddDevice(), and the driver will create an FDO.
The \Class key is related to the ClassGUID assigned in the INF and associated with the generic type of that device. Which allows UpperFilters and LowerFilters to be installed on any generic classes of a particular device, regardless if it’s set in the device ID key.
Driver loading
Under the Services subkey is the start value of the driver. Only drivers can specify SERVICE_BOOT_START and SERVICE_SYSTEM_START as user mode doesn’t exist at that point in boot. The following list shows what start policies can be applied for a driver, and looking back at Fig 4, our e1iexpress service is Start (3), so it will only be loaded when the PNP manager enumerates it on the system.
- SERVICE_BOOT_START (0) – loaded by the boot loader
- SERVICE_SYSTEM_START (1) – after the execute is initialized
- SERVICE_AUTO_START (2) – loaded by SCM
- SERVICE_DEMAND_START (3) – loaded on demand
- SERVICE_DISABLED (4) – not loaded
INF PNP device installation
Previously we discussed the bus enumeration and IRP_MN steps, now we’ll go through the INF steps.
During INF installation the PNP Manager checks the signing policy, warns of unsigned drivers if enabled, and attempts to loads the .CAT file as stated in the INF. It then does a signature check based on the hash inside the CAT and the hashed value of any relevant files. On success, the CAT is copied to c:\windows\system32\catroot.
Drivers installed manually (not via INF), that configure the registry, and copy drivers to the correct locations, are not checked for signatures by the PNP Manager signing policy. Instead they are checked against kernel-mode code signing (Vista and above).
INF steps:
- The Bus driver informs the PNP Manager a new piece of hardware has been attached to the system, and the Device ID is returned.
N.B. PNP Manager checks a driver list maintained by Windows Update, and blocks installation of incompatible/buggy/malicious drivers. - The PNP Manager checks the registry for that Device ID, if found loads the relevant binaries into memory as explained above.
- If not found, it checks all the INF files in C:\Windows\System32\DriverStore\FileRepository
N.B. c:\windows\system32\drvinst.exe does the driver installation from the driver store - If not found, the infamous yellow bang appears in device manager, and you must manually point it to an INF containing an appropriate HWID, or compatible ID.
N.B. compatible ID’s contain generic functionality, i.e. the generic keyboard that can send all basic 0-255 scan codes, but any special macro keys on the keyboard will not work until a more relevant HWID INF is found. - If multiple INFs are found, the order of precedence is:
– More precise match of HWID over less precise
– Signed INF over unsigned INF
– Newer signed INF over older signed INF - The INF locates the driver files, service files, and adds registry keys as indicated.
- A PNF file is also created from the INF, which becomes a pre-compiled INF, for more efficient processing, and ensures uniqueness per machine of that INF
- The devnode is created, and the device stack is created.
References and further reading
Windows Internals 7th edition (Part 1) – PNP section
Several dozens of pages relating to PNP on MSDN https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-plug-and-play
INF sections of PNP https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-sections
Programming the Windows Driver Model – PNP sections
Microsoft DDK 7 contains WDM source code for PNP samples.
Modern Windows driver samples on GitHub contains PNP samples for WDF https://github.com/microsoft/Windows-driver-samples