3
votes

I am new to PCI express, I want to read/write into PCI Express configuration space via MMIO addresses. I know how port mapped IO read/write into PCI express config space via 0xCFC and 0xCF8 port addresses(on x86). I also wrote a sample linux kernel module to read pci config space via port mapped io which worked fine. I want to do the same via MMIO/MMCFG access.

I also did a search around but could not find a convincing answer. I am looking for the details and also some code sample to understand it better.

Any help is appreciated.

1

1 Answers

3
votes

Hardware

The base address of the MMIO area for the configuration space of each PCIe devices in a PCI segment group is given in the ACPI table MCFG.

The MCFG table lists, for each PCI segment group, the first and last (inclusive) bus number of the PCI segment group and the base address of the extended configuration space.

The MCFG table is setup by the BIOS/UEFI based upon the value of the PCIEXBAR (for my processor is at offset 60h) in the Host Bridge/DRAM registers device located at 00:00.0.
This is the usual address, the device is integrated in the processor socket since the Nehalem architecture and never changed address.
You can google your processor generation datasheet to get the correct device address and register offset.

Also note that not all of the 256 MiB area may be mapped, my processor allows 256/128/64 MiB mapping with 128 MiB being the one selected by the BIOS/UEFI.


Linux

I don't know how to correctly handle this in Linux, there are the pci_{read|write}_config_XXX function function that seems to use the PCIe extended config space.
So accessing the config space should be very easy.

Alternatively, the pci_mcfg_lookup will give the physical address of extended configuration space for a PCI segment group and a bus range (you should be able to make it work by defining a resource structure with only the start and end fields set to the bus number).
In case you wanted a lower level approach.

Finally, you could get the address of the MCFG table and (re)parse it yourself - I don't know how to get such an address in Linux, exactly.
There is a acpi_tb_find_table where you can pass the signature of the table and null oem and table ids to get a table index.
At line 114 of the same file there is a piece of code that access a table by index, you can use it as documentation.
You probably have to import one or more symbols from the ACPI module.