On the journey to develop a pci bus filter driver

Hi all,

I am trying to develop a pci bus filter driver that reads the configuration space of its devices and it has been really challenging just to start off. I have done lots of research here and on MSDN forums, and I understand that to develop such a driver, I need to know deeply the ins and outs of WDM. In fact, I have been studying the book “Programming The Microsoft Windows Driver Model” written by Walter Oney, which I think is the only reference out there. Unfortunately, there is not much guidance out there for this endeavor.

Based on this book, we start developing just a small sample of the driver, just to see the AddDevice function being called. First, we defined the class filter on the INF file by specifying the class key [1] and appending it to the UpperFilters list. I understand that this is the easy part based on the book “just make yourself a device UpperFilter in the hardware key for the bus driver”. In the DriverEntry routine, I defined a callback to all major PNP IRPs and also for the AddDevice function.

As I understood, in the AddDevice function I need to attach only to the device that has DEVPKEY_Device_EnumeratorName related to pci (because there are other types of buses). Besides that, when I tried to debug, I thought that the AddDevice function would be called by the PnP, but this did not happen (the DriverEntry worked, I could debug it). So, I had the brilliant idea of restarting the VM, which crashed the system. Am I missing something here in the installation process?

Let’s say that the AddDevice function is working and I insert my filter on each child device stack right about the PDOs that the bus driver creates, what would be the next steps? I know that there are PnP and also the Power Management transitions to support, but this is still too abstract for me. I probably know the answer for that, but are there any samples of this kind of driver out there? Some documentation, or maybe something about this state machine that needs to be implemented for the PnP and Power Management.


[1] - CLASSKEY=System\CurrentControlSet\Control\Class{4d36e97d-e325-11ce-bfc1-08002be10318}

A couple of things about your AddDevice. First it is only going to be called for the bus drivers, so the PnP transitions you mention will be needed to handle the individual PDO’s the bus creates. Second, it is going to be called for a lot more than just PCI, so you need to check if the call is for PCI. I haven’t done this in a lot of years, but when I did a couple of these drivers, I used IoGetDeviceProperty(xxx, DevicePropertyHardwareID, …) to get the ID then checked for “ACPI\PNP0A08” or “ACPI\PNP0A03” or “*PNP0A08” or “*PNP0A08” and only created a filter driver object and attached it if it was one of those.

This is the easy stuff, it is handling all the PCI transitions and keeping the state table that becomes tricky.

Hi Don,

First of all, thank you for your reply, the list of IDs to check with IoGetDeviceProperty is really helpful. When you say “handling all the PCI transitions”, do you mean the PnP transitions that will happen within the PCI driver? Further, you wrote “keeping the state table”, what is this table? Does this table refer only to PnP state, or does it also refer to Power?

One more thing, for this type of driver, when we install it on the computer (for instance using the INF file), when the AddDevice function will be called? Initially, based on the book, I thought the PnP manager would call this function automatically after the installation, and using the debug I would be able to see all the PDOs arriving, but that’s not what happened.

On PCI transitions I actually meant PnP transitions, you need IRP_MN_START_DEVICE, IRP_MN_REMOVE_DEVICE, IRP_MN_QUERY_DEVICE_RELATIONS at a minimum. IRP_MN_QUERY_DEVICE_RELATIONS is the place you need to do the real work since you have to handle any new device you want to filter by detecting it is new, and then adding a filter device object to the stack.

On the AddDevice are you setting:

HKLM, System\CurrentControlSet\Control\Class{4d36e97d-e325-11ce-bfc1-08002be10318}, UpperFilters, 0x00010008, FilterName

Also, you need this to be a boot start driver, with LoadOrderGroup = “Boot Bus Extender”

You will need to reboot to get this to work. This is standard when you want to put a driver that early into the boot process.

Hi Don,

For the HKLM, my INF file is exactly like you put it; however, I did not know about this “LoadOrderGroup”, thanks for that very helpful. I understand that IRP_MN_QUERY_DEVICE_RELATIONS will have a function that is going to be the one where new devices will arrive (for instance, the user plugs in something) and also that devices will no longer exist (thus, needs to maintain this list).

I’m trying to get my head around this, and to come up with a minimum working solution that works for most cases, because without this I’m going to keep crashing my VMs. Do you think that by just giving support to these PnP transitions, I will get something minimal working? Or will I need to also give support for the Power Management transitions? Or, finally, is this just the tip of the iceberg and still there are many other things to do?

For power you just need a simple standard function to forward the power IRPs. On the IRP_MN_QUERY_DEVICE_RELATIONS you will get the adding of a device which has to be done special. Basically, you need a list of the devices that have been enumerated before, and then check for new ones. The removal will act normally since you have by then attached a device object with a call to the PnP handler to the stack so you will see the removal like any other device.

Missing an important detail. The filter DO needs to know during the pnp remove if the underlying PDO was reported missing. If reported missing the filter DO detaches and deletes. If the PDO is still reported as present the filter DO should not detach and delete during the pnp remove.

1 Like

Doron, glad you caught that. I realized it has been close to 10 years since I did my last PCI bus filter driver.

1 Like

Hi all

First of all, thank you very much for your help. Your answers have been very helpful in guiding us through the development of this bus filter driver.

I would like to clarify a few things. In order to achieve what we want with this driver, which is access the configuration space of pci devices, do we really need to attach a new Device Object on top of each of the pci children? I mean, couldn’t we just keep track of the updated list of PDOs created by the pci bus through the IRP_MN_QUERY_DEVICE_RELATIONS and later send IRPs to the desired PDOs to access their config space? For your knowledge, the way we intend to access the config space is based on the first method of this white paper [1].

Another question is considering we really need to attach a new Device Object to each of the PCI children. In this case, to keep track of the PDOs received in the IRP_MN_QUERY_DEVICE_RELATIONS do we need to create a new list structure, for example in the device extension? Couldn’t we just use the list of Device Objects presented on the DriverObject and then use the LowerDeviceObject pointer (that we already have on our device extension) to access the PCI children PDOs?

Also, Doron Holan, could you please elaborate more on how we could detect if a PDO was reported missing?

[1] http://www.hollistech.com/Resources/Misc%20articles/getbusdata.htm

You won’t be able to correctly manage the state of the pdo objects you are
tracking if you don’t attach to the pdo stack. And you need to create your
own list you really can’t use the one presented by some other entity, as
that entity is free to do anything it wants with the objects. It is just a
pointer, it isn’t a big deal to create a new pointer.

Also I am always shocked whenever that doc gets mentioned. It is ancient.

Mark Roddy

It is ancient.

But, from a quick review, it still seems to be accurate.

For a WDF solution, you probably want to call WdfFdoQueryForInterface.

Peter

Thank you very much for your answers.

You won’t be able to correctly manage the state of the pdo objects you are
tracking if you don’t attach to the pdo stack.

If you do not mind, what do you mean by ‘correctly manage the state of the pdo objects’? I don’t understand why keeping track of the IRP_QUERY_DEVICE_RELATIONS and saving the PDOs in a list for later use isn’t enough to have an accurate view of the PDOs.

As I understand, the PnP Manager sends this IRP after receiving an IoInvalidateDeviceRelations from the PCI (when there is a change on the list of child devices) and, since we are attached to the PCI bus, we could have access to the updated list before even the PnP Manager has seen it. And isn’t it just after receiving the response to the IRP_QUERY_DEVICE_RELATIONS that the PnP manager sends an IRP_MN_REMOVE_DEVICE to invalidate the PCI child’s PDO(which we would also receive it if we were attached to the child pdos)?

**And you need to create your own list, you really can’t use the one presented by some other entity, as that entity is free to do anything it wants with the objects. It is just a pointer, it isn’t a big deal to create a new pointer. **

As I understand, this list is kept by the PnP Manager after a successful call to IoCreateDevice, isn’t it safe to use? I would like to stress that I am aware that the strategy of using this list depends on the fact that we would have created a new device to attach to each PCI child’s PDO.

Also I am always shocked whenever that doc gets mentioned. It is ancient.

I really could not find much on this subject, there is almost nothing. Also, thank you very much for your contribution to the field, since this doc is still relevant after all this time.

@“Peter_Viscarola_(OSR)” , unfortunately, it looks like using WDF is not an option in our scenario based on answers from previous posts on the subject. [1] [2]

[1] - https://community.osr.com/discussion/comment/298103
[2] - https://community.osr.com/discussion/216336/usb-bus-filter-driver-using-kmdf

WDF is an option you just have to wrap your own interfaces around dispatch
routines in order to handle the PDO IRPs as WDF hasn’t a clue about them.
It is a bit of an abomination, but you get your FDO filter code basically
for free from WDF and only need to get the PDO filter code right.

Also I did ask like 15 years ago or so when WDF was being developed for a
bus filter driver model, but there was not much interest.

Mark Roddy

Thank you very much for your answers.

You won’t be able to correctly manage the state of the pdo objects you are
tracking if you don’t attach to the pdo stack.

If you do not mind, what do you mean by ‘correctly manage the state of the pdo objects’? I don’t understand why keeping track of the IRP_QUERY_DEVICE_RELATIONS and saving the PDOs in a list for later use isn’t enough to have an accurate view of the PDOs.

As I understand, the PnP Manager sends this IRP after receiving an IoInvalidateDeviceRelations from the PCI (when there is a change on the list of child devices) and, since we are attached to the PCI bus, we could have access to the updated list before even the PnP Manager has seen it. And isn’t it just after receiving the response to the IRP_QUERY_DEVICE_RELATIONS that the PnP manager sends an IRP_MN_REMOVE_DEVICE to invalidate the PCI child’s PDO(which we would also receive it if we were attached to the child pdos)?

And you need to create your own list, you really can’t use the one presented by some other entity, as that entity is free to do anything it wants with the objects. It is just a
pointer, it isn’t a big deal to create a new pointer.

As I understand, this list is kept by the PnP Manager after a successful call to IoCreateDevice, isn’t it safe to use?
I would like to stress that I am aware that the strategy of using this list depends on the fact that we would have created a new device to attach to each PCI child’s PDO.

Also I am always shocked whenever that doc gets mentioned. It is ancient.

I really could not find much on this subject, there is almost nothing. Also, thank you very much for your contribution to the field, since this doc is still relevant after all this time.

@“Peter_Viscarola_(OSR)” , unfortunately, it looks like using WDF is not an option in our scenario based on answers from previous posts on the subject. [1] [2]

[1] - https://community.osr.com/discussion/comment/298103
[2] - https://community.osr.com/discussion/216336/usb-bus-filter-driver-using-kmdf

A bit late in this thread, but… WHY you want (or need) to read the configuration space?
A staple question here. Can it be done in much simpler way?

– pa

Hi @Pavel_A,

I am working on a hardware diagnostics software. For this, I need to be able to access the hardware as directly as possible without the interference from other software. The PCI configuration space of some types of devices contains information about the device’s current health state. For instance, from the Intel HDA specification [1], I want to read the CORB Status register, the specification part about CORB Status register reads “If this status bit is set, the controller has detected an error in the pathway between the controller and memory”. This is also true for other kinds of devices attached to the PCI bus. Thus, I am developing a filter driver in order to achieve this.

[1] -https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf

1 Like

Well so you want to look at config space of specific device(s) - then a lower device filter may be easier and less intrusive to rest of the system.

– pa