Search code examples
cwindowssizeregionspci

How do I obtain PCI Region size in Windows?


I needed to scan my PCI bus and obtain information for specific devices from specific vendors. My goal is to find the PCI Region size for the AMD Graphics card, in order to map the PCI memory of that card to userspace in order to do i2c transfers and view information from various sensors.

For scanning the PCI bus I downloaded and compiled pciutils 3.1.7 for Windows x64 around a year ago. It supposedly uses DirectIO.

This is my code.

int scan_pci_bus()
{
    struct pci_access *pci;
    struct pci_dev *dev;
    int i;

    pci = pci_alloc();
    pci_init(pci);

    pci_scan_bus(pci);

    for(dev = pci->devices; dev; dev = dev->next) 
    {
        pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_PHYS_SLOT);
        if(dev->vendor_id == 0x1002 && dev->device_id == 0x6899)
        {
            //Vendor is AMD, Device ID is a AMD HD5850 GPU
            for(i = 0; i < 6; i++) 
            {
                printf("Region Size %d %x ID %x\n", dev->size[i], dev->base_addr[i], dev->device_id);
            }
        }
    }


    pci_cleanup(pci);

    return 0;
}

As you see in my printf line, I try to print some data, I am successfully printing device_id and base_addr however size which should contain the PCI region size for this device is always 0. I expected, at least one of the cycles from the loop to display a size > 0.

My code is based on a Linux application which uses the same code, though it uses the pci.h headers that come with Linux(pciutils apparenltly has the same APIs). Apparently, Windows(that is Windows 7 x64 in my case) does not show this information or the at the very least is not exposed to PCIUtils.

How do you propose I obtain this information? If there are alternatives to pciutils for Windows and provide this information, I'd be glad to obtain a link to them.

EDIT:I have still found no solution. If there are any solutions to my problem and also work for 32-bit Windows, It would be deeply appreciated.


Solution

  • Well whamma gave a very good answer [but] there's one thing he was wrong about, which is region sizes. Region sizes are pretty easy to find, here i will show two ways, the first by deciphering it from the address of the bar, the second through Windows user interface.

    Let's assume that E2000000 is the address of the Base Register. If we convert that to binary we get: 11100010000000000000000000000000

    Now there are 32 bits here in total, you can count them if you must. Now if you are not familiar with how the bits in a BAR are layed out, look here -> http://wiki.osdev.org/PCI , specifically "Base Address Registers" and more specifically the image that reads "Memory Space BAR Layout". Now lets start reading the bits from the right end to the left end and use the image in the link i pointed to you above as a guide.

    So the first bit(Bit 0) starting from the right is 0, indicating that this is a memory address BAR. Bits(1-2) are 0, indicating that it's a 32-bit(note this is not the size) memory BAR. Bit 3 is 0, indicating that it's not Prefetchable memory. Bits 4-31 represent the address.

    The page documents the PCI approved process:

    To determine the amount of address space needed by a PCI device, you must save the original value of the BAR, write a value of all 1's to the register, then read it back. The amount of memory can then be determined by masking the information bits, performing a bitwise NOT ('~' in C), and incrementing the value by 1. The original value of the BAR should then be restored. The BAR register is naturally aligned and as such you can only modify the bits that are set.

    The other way is using Device Manager: Start->"Device Manager"->Display Adapters->Right Click your video card->Properties->Resources. Each resource type marked "Memory Range" should be a memory BAR and as you can see it says [start address] to [end address]. For example lets say it read [00000000E2000000 - 00000000E2FFFFFF], to get the size you would take [start address] from [end address]: 00000000E2FFFFFF - 00000000E2000000 = FFFFFF, FFFFFF in decimal = 16777215 = 16777215 bytes = 16MB.