IoCallDriver() in IRP_MN_START_DEVICE is returning STATUS_INVALID_PARAMETER (0xc000000d)

Hi, I have a WDM driver for a PCI express device on Windows 10. I am building it with latest SDK, WDK and Visual Studio 2019.
It installs fine but the IRP_MN_START_DEVICE code fails in IoCallDriver(). The IoCallDriver() itself returns STATUS_PENDING but the Irp->IoStatus.Status is showing 0xC000000d. Because of this I cannot call my own StartDevice() handling to map the BARs. The Irp itself has the translated resources (I can see it in the debugger).

Here is my code for IRP_MN_START_DEVICE. The PCIe device has one BAR (lower nibble of BAR0 is 0x8 indicating 32-bit prefetchable memory). The length of the BAR is 0x20000 (128k). If I dump the IRP current stack translated resources, I do see the first resource of type CmResourceMemory and length 128k. But I cannot map that using MmMapIoSpace() before IoCallDriver() succeeds otherwise the command register in the PCIe function will not be set properly so uncached memory accesses will not be forwarded to the PCI express function

Here is what is happening. IoCallDriver() returns STATUS_PENDING so my dispatch routine waits on the event which is triggered by the completion routine. After the return from KeWaitForSingleObject() the value of Irp->IoStatus.Status is STATUS_INVALID_PARAMETER. This means that the driver below (PCI.sys or ACPI) has failed this call but I do not know what parameter it is complaining about.

FYI, I am thinking of converting the driver to KMDF but I want to understand why this is not working. This PCI express device is a non-standard one. I have written other WDM PCI drivers and KMDF drivers before and cannot explain this error. Could this be anything to do with the device itself?.

Linux driver loads fine on the same device but Linux has much lesser requirements than Windows does.

	IoCopyCurrentIrpStackLocationToNext(Irp);
	// - Set up an event notification data structure
	KeInitializeEvent(&event,
		NotificationEvent,
		FALSE
	);
	// - Define the entry point of the routine that will be executed when the Pci bus
	//   driver signals it is done with the start device packet.
	IoSetCompletionRoutine(Irp,
		(PIO_COMPLETION_ROUTINE)MyCompletionRoutine
		&event,
		TRUE,
		TRUE,
		TRUE);
	// - Pass the packet down the stack to the Pci bus driver                       
	status = IoCallDriver(DeviceInfo->NextLowerDriver, Irp);
	// - Wait for the Pci Bus driver to process the packet
	if (STATUS_PENDING == status)
	{
		KeWaitForSingleObject(
			&event,
			Executive,  // A driver uses this reason code.
			KernelMode, // We are waiting in kernel mode.
			FALSE,      // No allert
			NULL);      // No timeout

		status = Irp->IoStatus.Status;   **[RK] Here status is being set to STATUS_INVALID_PARAMETER**
	}
	// - If all is well execute this driver's device start code.
	if ( NT_SUCCESS(status) )
	{
		status = MyStartDevice(DeviceObject, Irp);

am thinking of converting the driver to KMDF

Just do it.

Linux driver loads fine on the same device but Linux has much lesser requirements than Windows does.

That’s nonsense. It’s a popular meme, but it’s not true. Windows and Linux are just two different operating systems. Their requirements are sometimes different… but are mostly the same. Especially for Express devices.

a WDM driver for a PCI express device on Windows 10

Why in the world are you writing a WDM driver for this device… I’m 2019?

This driver model has been “out of date” for almost 15 years now.

Use WDF and don’t worry about WDM any more.

Peter

Peter, thanks. Just for the facts, this was a driver written in WDM handed over to me. I am just trying to make it work. I will work on WDF/KMDF

Peter, I did a dump of the PCI configuration space on this device and I noticed that the first capability starts at offset 0x40 (ID is 1) which is PCI power management capability. Its next pointer is 0 which means no more capabilities. But a visual inspection of the configuration space shows that the PCI Express capability is in the legacy PCI configuration space, it is just not reachable using the capability lists. I think this is what is causing the error. Do you think this error in the capability list could be causing the pci bus driver to not enable the PCI express function?

The fact that your resources are passed into your START_DEVICE indicates that the device was recognized by the PCI Bus Driver.

Enabled driver verifier, if you haven’t already.

It is theoretically possible to get trace output from the PCI Bus Driver… but I haven’t tried to do it in a long, long, time.

Look: It’ll take you 15 minutes, no more, to code up enough of a driver in KMDF so that you at least can rule-out something YOU are doing wrong in your driver. Just do that. Then see what you get in EvtDevicePrepareHardware.

Peter

Peter, you are right. KMDF makes this a lot easier than WDM and I am working on it. It is just that I am trying to understand what could be wrong and I do not see any issues with my driver. I will work on enabling PCI bus traces and enable driver verifier. Thanks for your help

Peter, converted my driver to KMDF driver. It is a bare minimal driver just to get to PrepareHardware and be able to map my resources. I have DriverContextCleanup, DeviceContextcleanup, PrepareHardware and ReleaseHardware implemented and of course AddDevice. DriverEntry gets called, AddDevice gets called and I set up the PrepareHardware and ReleaseHardware in the callbacks. But the PrepareHardware never gets called. AddDevice() of course is a success. Then DeviceContextCleanup() gets called and DriverContextCleanup() called and the driver load fails

Well, I am not sure whether my advice is not too time consuming… but if you really wish to know what is going on (and where exactly the failure occurs), you may probably compile WDF by yourself and do some step-by-step debugging. At least, you may examine how much code is executed between your AddDevice and the moment when the PrepareHardware callback should be invoked.

WDF source code is here:
https://github.com/microsoft/Windows-Driver-Frameworks

Martin, did not realize WDF framework code is available on github. That is cool and I will try your suggestion. I have asked the device developer (FW guy) to fix the PCI configuration space so I eliminate the PCI express device as the failure. This has been really bothering me. We have a PCIX version of the same device (different set of resources but it is a PCIx device) and the driver for that device (WDM not KMDF) loads just fine and my PCI Express WDM source code is modeled after that. That PCIx device has no issues in its PCI configuration space but then it is a normal PCI device

Now, one obvious question. My PCI express device requests only one BAR (BAR0 lowest nibble 8 which means 32-bit prefetchable space). I assume this is not a problem as BIOS and Windows are able to map this into system MMIO space .

as BIOS and Windows are able to map this into system MMIO space .

Well, the register will be allocated at a location below 0xFFFFFFFF… and then your DRIVER maps it into kernel virtual address space.

I still do t know why you just don’t write the KMDF code to claim the device, and see if that works. Seriously… why are you torturing yourself, when you can know if the problem is your driver or your device with 15 minutes work? I just don’t get it.

Peter

Peter, I already wrote the KMDF driver. It is done. The KMDF driver also does not work. I get the call to AddDevice() where I register my PrepareHardware and ReleaseHardware routines. These routines never get called. Instead, AddDevice() gets called, I return STATUS_SUCCESS and then DeviceContextCleanup() gets called followed by DriverContextCleanup().

OK! Finally. Now that’s helpful.

What does !WDFLOGDUMP say?

P