PCI Configuration Registers

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Ryu

PCI Configuration Registers

Post by Ryu »

In the PCI 2.2 specifications, I am a bit confused how would one determine the range of either I/O or mapped addresses of the device? I understand that there is base address registers at register windows 10h, 14h, 18h, 1Ch, 20h however nothing there that gives me the range. My goal is to be able to map all PCI I/O or memory address spaces without relying on BIOS interrupt services. If someone could shed some light, thanks :)

Actually I have another question, I've just made a simple test function that enumarates all devices. This is done by checking if vendor and manufactor id is not 0FFFFh, however my question is I get some devices that are the same but differnt function numbers but the base addesses are the same, in fact the entire configuration registers are the same. Is it safe to mark these to be same devices?

Ex. 1) bus 0, device 0, function 0
Ex. 2) bus 0, device 0, function 1
Ex. 3) bus 0, device 0, function 2

Three of these enumerated devices have exact same configuration in its registers.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:PCI Configuration Registers

Post by Brendan »

Hi,
Ryu wrote:In the PCI 2.2 specifications, I am a bit confused how would one determine the range of either I/O or mapped addresses of the device? I understand that there is base address registers at register windows 10h, 14h, 18h, 1Ch, 20h however nothing there that gives me the range. My goal is to be able to map all PCI I/O or memory address spaces without relying on BIOS interrupt services. If someone could shed some light, thanks :)
If you write 0xFFFFFFFF (or 0xFFFFFFFFFFFFFFFF for 64 bit) to the BARs then the hardware will zero out some of the lower bits. The number of bits that it sets to zero will tell you the range of the area.

For example:

Code: Select all

unsigned int BAR_offset;

void some_PCI_init_loop(bus, device, function) {
    u64_int size;

    BAR_offset = 0x10;
    do {
        size = get_BAR_size(bus, device, function);
    } while (BAR_offset < 0x28);
}


u64_int get_BAR_size(bus, device, function) {
    u64_int BAR;
    u64_int temp_BAR;
    u64_int size;

    BAR = PCI_read_32(bus, device, function, BAR_offset);
    if(BAR = 0) {
        // BAR not implemented
        size = 0;
        BAR_offset += 4                  // Set offset of next BAR (if any)
    } else {
        if ( (BAR & 1) == 0) {
            // Memory
            type = (BAR >> 1) & 0x03;
            if( (type & 2) == 0) {
                // 32 bit Memory
                temp_BAR = BAR;
                disable_ints();
                PCI_write_32(bus, device, function, BAR_offset, 0xFFFFFFFF);
                BAR = PCI_read_32(bus, device, function, BAR_offset);
                BAR = BAR & 0xFFFFFFF0;          // Clear flags
                size = (BAR ^ 0xFFFFFFFF) + 1;
                PCI_write_32(bus, device, function, BAR_offset, temp_BAR);
                enable_ints();
                printf("PCI BAR %d:%d:%d offset %d = %d bytes (32 bit)\n", bus, device, function, BAR_offset, size);
                BAR_offset += 4                  // Set offset of next BAR (if any)
            } else {
                // 64 bit Memory
                temp_BAR = PCI_read_64(bus, device, function, BAR_offset);
                disable_ints();
                PCI_write_64(bus, device, function, BAR_offset, 0xFFFFFFFFFFFFFFFF);
                BAR = PCI_read_64(bus, device, function, BAR_offset);
                BAR = BAR & 0xFFFFFFFFFFFFFFF0;          // Clear flags
                size = (BAR ^ 0xFFFFFFFFFFFFFFFF) + 1;
                PCI_write_64(bus, device, function, BAR_offset, temp_BAR);
                enable_ints();
                printf("PCI BAR %d:%d:%d offset %d = %d bytes (64 bit)\n", bus, device, function, BAR_offset, size);
                BAR_offset += 8                  // Set offset of next BAR (if any)
            }
        } else {
            // IO space
            temp_BAR = BAR;
            disable_ints();
            PCI_write_32(bus, device, function, BAR_offset, 0xFFFFFFFF);
            BAR = PCI_read_32(bus, device, function, BAR_offset);
            BAR = BAR & 0xFFFFFFFC;          // Clear flags
            size = (BAR ^ 0xFFFFFFFF) + 1;
            PCI_write_32(bus, device, function, BAR_offset, temp_BAR);
            enable_ints();
            printf("PCI BAR %d:%d:%d offset %d = %d I/O ports\n", bus, device, function, BAR_offset, size);
            BAR_offset += 4                  // Set offset of next BAR (if any)
        }
    }
    return size;
}
Ryu wrote:Actually I have another question, I've just made a simple test function that enumarates all devices. This is done by checking if vendor and manufactor id is not 0FFFFh, however my question is I get some devices that are the same but differnt function numbers but the base addesses are the same, in fact the entire configuration registers are the same. Is it safe to mark these to be same devices?
No - if your code doesn't have bugs then these would be seperate devices. For example, bus 0, device 3 might be a multi-function device, where function 0 is the primary ATA/ATAPI controller and function 1 is the secondary ATA/ATAPI controller.

I'm wondering if you code has bugs though - does it match the information you get from other OSs, or does it match your hardware?
Ryu wrote:Ex. 1) bus 0, device 0, function 0
Ex. 2) bus 0, device 0, function 1
Ex. 3) bus 0, device 0, function 2
Also note that bus 0, device 0 is the PCI host bridge. If this is a multi-function device then you've got multiple PCI host bridges with seperate PCI buses behind each of them...


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Ryu

Re:PCI Configuration Registers

Post by Ryu »

Greetings Brendan,
Brendan wrote: I'm wondering if you code has bugs though - does it match the information you get from other OSs, or does it match your hardware?
:-
Brendan wrote: stepping through the devices by adding the address register by 80h when i should be adding them by 100h (where function number starts). Which should explain the registers being exact same.
Brendan wrote: Also note that bus 0, device 0 is the PCI host bridge. If this is a multi-function device then you've got multiple PCI host bridges with seperate PCI buses behind each of them...
Sorry if I wasn't clear but those were just random bus and device numbers to clearify what my problem was. At the time they were some bus and device number other then 0, but thats irrevelant now.

I really appreciate the clearification of BARs, really, one million thanks buddy! ;D ..Theres another thing, that clouds me with curiousity is; after mapping all the I/O and memory address spaces by these configuration registers, is there anything else that I should be aware of that requires mapping besides , A0000h-0FFFFFh? Also, is AGP devices part of PCI bus and can be found using this PCI enumeration method?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:PCI Configuration Registers

Post by Brendan »

Hi,
Ryu wrote:I really appreciate the clearification of BARs, really, one million thanks buddy! ;D ..Theres another thing, that clouds me with curiousity is; after mapping all the I/O and memory address spaces by these configuration registers, is there anything else that I should be aware of that requires mapping besides , A0000h-0FFFFFh? Also, is AGP devices part of PCI bus and can be found using this PCI enumeration method?


There's also figuring out how each card's IRQs are mapped to the PIT (and possibly the I/O APIC). There's a few ways to do this ranging from using the value that the BIOS put in the "interrupt line" field (the low 8 bits of PCI configuration space location 0x3C) to using PCI BIOS functions or ACPI to find which IRQs are used (and possibly reconfigure them).

For newer PCI cards there's also MSI (Message Signalled Interrupts) where the PIC and I/O APIC aren't used (the device sends the IRQ directly to the CPUs local APIC). This solves the IRQ sharing problem and is a little more efficient, but it's also optional...

For AGP, physically everything is built into the memory controller and doesn't exist on the PCI bus at all - normally the same "northbridge" chip contains the memory controller, AGP and the PCI host controller/s so that memory and I/O accesses can be routed between them. Despite this the AGP does have it's own PCI configuration space, and the AGP device does behave like it is a PCI device on the PCI bus.

Unfortunately (AFAIK) to use AGP properly you need to setup the AGP Graphics Address Range Table (GART), which is chipset specific. For this reason I'd forget about AGP for now and just pretend that it's a normal PCI video card...


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Ryu

Re:PCI Configuration Registers

Post by Ryu »

Thanks a ton Brendan.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:PCI Configuration Registers

Post by Pype.Clicker »

don't miss the FAQ page about PCI, too. I don't remind whether we have info about BARs range detection, but we certainly have hints about enumeration.
Post Reply