Search code examples
x86pci

Should I shift PCI register number or AND with 0xFC?


This is the PCI CONFIG_ADDRESS register from http://pds5.egloos.com/pds/200709/07/88/pci21.pdf :

The PCI CONFIG_ADDRESS register

It shows the register number as bits [7-2]. This tells me I should left shift the register value by 2 when forming a CONFIG_ADDRESS value. So

(1 << 31) | (bus << 16) | (device << 11) | (function << 8) | (register << 2)

Seen here: https://anadoxin.org/blog/pci-device-enumeration-using-ports-0xcf8-0xcfc.html/

But I've also seen ANDing the register value with 0xFC like so:

(1 << 31) | (bus << 16) | (device << 11) | (function << 8) | (register & 0xFC)

Seen here: https://wiki.osdev.org/Pci#Configuration_Space_Access_Mechanism_.231 in pciConfigReadWord.

These two methods produce different values for the CONFIG_ADDRESS register, so which one is correct?


Solution

  • From PCI's perspective, it's 32 bit registers with a "register number" starting at bit 2.

    However, software typically creates a more abstract interface (not least of all to seamlessly support "memory mapped configuration space" introduced in PCI-express, and the rare old "mechanism #2").

    For a more abstract interface it's common to support multiple sizes (32 bit, 16 bit and 8 bit) with "natural alignment". E.g. so that a caller can do "PCI_read_byte(0x23);" and get the third byte in "32-bit register number 8".

    In other words, for the caller of the abstract interface it's a byte addressable offset and the implementation of the abstraction converts "byte addressable offset" into "register number" (and takes care of masking and shifting where needed).

    You'll also find that most documentation (PCI specs, datasheets for various PCI devices, ..) use the same "byte addressable offset" numbering scheme. E.g. they'll say "the 32-bit register at offset 0x3C" (and not "register number 15").

    The consequence is that almost all software uses your second version, like:

    (1 << 31) | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC)
    

    ..just with "offset" instead of "register number" to make it a little more clear.