x86osdevpcibase-address

PCI I/O BAR Addresses - 32-bit or 16-bit?


I'm currently working on developing an operating system, and I'm trying to implement PCI. Specifically, I'm trying to implement an RTL8139 driver, not that that detail matters. Anyways, as far as I'm aware, I'm supposed to use I/O and not memory mapping to address the different registers, and I'm having a bit of trouble understanding one thing in particular:

The Base Address Registers in PCI configuration space are of 32-bit width. To my understanding, the x86 I/O bus uses 16-bit port numbers. I can't seem to find an explanation for how to use the BARs properly, other than to configure them and use that address, which, knowing this, doesn't make sense. I'm aware of the requirement to align by 4 (for I/O), but I'm not sure how to work with the addresses. Am I supposed to use only the lower 16 bits? Is there some other operation I need to do?

I would appreciate it if this could be clarified. Thanks in advance.


Solution

  • For PCI device BARs there are 3 possibilities:

    a) It uses IO ports and not memory mapped registers; and the lowest bit of the BAR will be hard-wired to 1. In this case, for 80x86, the BAR must be set to a "16-bit base IO port" (and the upper 16 bits of the BAR need to be zero because 80x86 doesn't support 32-bit IO port addresses); but the computer's firmware should have already done this for you. Of course PCI specs are intended to be used by all kinds of different CPUs (and PCI is not intended to be "80x86 PCs only"), and (in theory but probably not in practice) a completely different type of CPU might support 32-bit IO port addresses.

    b) It uses memory mapped registers with 32-bit physical addresses, the lowest bit of the BAR will be hard-wired to 0 and bits 1 and 2 will be hard-wired 00b.

    c) It uses memory mapped registers with 64-bit physical addresses, the lowest bit of the BAR will be hard-wired to 0 and bits 1 and 2 will be hard-wired 10b.

    Am I supposed to use only the lower 16 bits?

    If the BAR says "IO ports"; you'd use bits 2 to 15, like "base_IO_port = raw_value_from_BAR & 0xFFFC" (although I'd be tempted to have an "if(raw_value_from_BAR >> 16 != 0) { /* Something is wrong, those bits should be zeros */" sanity check).