PCMCIA on Windows 10

All,
We have an old PCMCIA driver that we are trying to port to 64-bit Windows 10. The port mostly works but we ran into a weird situation where we have full read/write access to the cards memory but only when the driver has been compiled in debug mode. When it compiled in release mode, we can read the card’s memory just fine but we can not write to it!

The driver was developed using the WDM model and the PCMCIA card (a memory card) is non standard. It’s non-standard because it does not have a proper attribute space to report IDs and required resources. To get around this, we are processing IRP_MN_FILTER_RESOURCE_REQUIREMENTS to request a 64K block of memory. This works fine and the memory resource is available by the time StartDevice is called. Here we’re using MmMapIoSpaceEx(, PAGE_READWRITE | PAGE_NOCACHE), to obtain a virtual address to the cards memory. When the driver is compiled in debug mode, we have full read/write access of the card and everything works fine. However, if the driver is compiled in release mode, we can only read from the card.

Below is a code that we’re using to request the memory resource. Btw: the driver is being compiled with VS2019 and WDK 10.0.18346.1000.

Any ideas would be greatly appreciated.
Jim.

case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
{
status = Pcd_CallPcmciaDriver(AttachedPDO, Irp);
NewRequirements = ExAllocatePool(PagedPool, sizeof(IO_RESOURCE_REQUIREMENTS_LIST));

    if( NewRequirements == NULL )
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
    }
    else
    {
        RtlZeroMemory(NewRequirements, sizeof(IO_RESOURCE_REQUIREMENTS_LIST));
        NewRequirements->ListSize         = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
        NewRequirements->InterfaceType    = 0;  // not used by WDM drivers;
        NewRequirements->BusNumber        = 0;  // not used by WDM drivers
        NewRequirements->SlotNumber       = 0;  // not used by WDM drivers
        NewRequirements->Reserved[0]      = 0;  // not used by WDM drivers
        NewRequirements->Reserved[1]      = 0;  // not used by WDM drivers
        NewRequirements->Reserved[2]      = 0;  // not used by WDM drivers
        NewRequirements->AlternativeLists = 1;

        NewRequirements->List[0].Version  = 1;
        NewRequirements->List[0].Revision = 1;
        NewRequirements->List[0].Count    = 1;
        NewRequirements->List[0].Descriptors[0].Option                           = 0;
        NewRequirements->List[0].Descriptors[0].Type                             = CmResourceTypeMemory;
        NewRequirements->List[0].Descriptors[0].ShareDisposition                 = CmResourceShareDeviceExclusive;
        NewRequirements->List[0].Descriptors[0].Spare1                           = 0;
        NewRequirements->List[0].Descriptors[0].Flags                            = CM_RESOURCE_MEMORY_READ_WRITE;
        NewRequirements->List[0].Descriptors[0].Spare2                           = 0;
        NewRequirements->List[0].Descriptors[0].u.Memory.Length                  = (64 * 1024);
        NewRequirements->List[0].Descriptors[0].u.Memory.Alignment               = (64 * 1024);
        NewRequirements->List[0].Descriptors[0].u.Memory.MinimumAddress.QuadPart = 0;
        NewRequirements->List[0].Descriptors[0].u.Memory.MaximumAddress.QuadPart = (ULONG)-1;

        IrpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList = NewRequirements;

        if( Irp->IoStatus.Information )
        {
            ExFreePool( (void *)Irp->IoStatus.Information );
            Irp->IoStatus.Information = 0;
        }

        Irp->IoStatus.Information = (ULONG_PTR)NewRequirements;

        status = STATUS_SUCCESS;
    }

    break;

So, what happens when you write: exception?

and unrelated…

NewRequirements->List[0].Descriptors[0].u.Memory.MaximumAddress.QuadPart = (ULONG)-1;
QuadPart is 64 bit. ULONG is 32 bit.

– pa

Pavel’s point is important. The value of (ULONG)-1 is 0x00000000FFFFFFFF, which would limit you to the low 4GB. It’s possible that’s what your hardware requires, but you need to be sure of that.

Yes! Good point indeed and thanks for making it.
At some point I did change the code by casting to a ULONGLONG. I also changed the code to set the descriptor using RtlIoEncodeMemIoResource() - which is apparently a new(er) requirement. Neither of the changes made a difference.

Good question about the write: The write operation just doesn’t have any affect - it just seems to be ignored. The reads seem to work perfectly fine.
I’m able to use an off-line utility to modify the card data to verify that the correct data is being read.

Is there a possibility that Win10 does not allow a release driver to write to a non-standard PCMCIA card???

Thanks for your input.

Windows cannot block PCI writes. Those are CPU instructions, working beneath the Windows level.

Here’s one possibility. Many primitive devices required 32-bit accesses, and would not work with byte or word accesses. If you are using WRITE_REGISTER_ULONG, then you are guaranteed to do 32-bit accesses. If you are doing it yourself through pointer arithmetic, it is POSSIBLE that the compiler has optimized things in a way that don’t write full words. This is especially true if you got tricky and used a union to access indiviidual fields.

If you are doing it yourself through pointer arithmetic

You are in big trouble, unless you manually code the right barrier instructions.

After years of preaching at the altar of WRITE_REGISTER_xxxx architectural purity, the slothfulness of “just get this shit written” bit me hard in a project last year in which the device had a particularly horrible register layout that necessitated all manner of mathematical gymnastics for certain writes. “While I’m writing the code, I’ll just do it by pointer, so I can easily read the code… I’ll go back later, before Final Release, and change it all to use the HAL functions.” Yeah… you know what? If you don’t use the HAL functions, or at least call the barrier macros yourself, those writes might not do what you think. Now I can say I know this for certain.

Peter

@“Peter_Viscarola_(OSR)” said:

If you are doing it yourself through pointer arithmetic

You are in big trouble, unless you manually code the right barrier instructions.

I have the same experience. In the driver I’m maintaining, PCI registers were originally just accessed via pointers. This worked for many years on different CPUs and chipsets, but the driver failed to work on some very current systems with the latest CPUs and chipsets.

Changing the simple pointer access to using macros/inline functions from the DDK headers fixed the problem.

Martin

Thanks for the help guys. That was the problem - accessing the memory directoy without using the HAL functions - READ/WRITE_REGISTER_UCHAR().
Jim