为什么手动 pci 地址 space 计算与 lspci 输出不同

Why the manual pci address space calculation is not same as lspci output

我有一个连接到 PCI 总线的以太网控制器。

$ lspci -s 02:01 -v
02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01)
    Subsystem: VMware PRO/1000 MT Single Port Adapter
    Physical Slot: 33
    Flags: bus master, 66MHz, medium devsel, latency 0, IRQ 19
    Memory at fd5c0000 (64-bit, non-prefetchable) [size=128K]
    Memory at fdff0000 (64-bit, non-prefetchable) [size=64K]
    I/O ports at 2000 [size=64]
    [virtual] Expansion ROM at fd500000 [disabled] [size=64K]
    Capabilities: <access denied>
    Kernel driver in use: e1000
    Kernel modules: e1000

写了一个示例 linux 内核模块来计算内存映射的大小 I/O space。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>

#define PCI_INDEX_PORT  0xcf8
#define PCI_DATA_PORT   0xcfc


unsigned short read_pci_config_short(unsigned char bus, unsigned char slot, unsigned char func,
                    unsigned char offset)
{
    outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
    return inw(0xcfc + (offset&2));
}

unsigned int read_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset)
{
        unsigned int v;
        outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
        v = inl(0xcfc);
        return v;
}

void write_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset,
                                    unsigned int val)
{
        outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
        outl(val, 0xcfc);
}


int __init  hello_init(void)
{   
    u64 address;
    //02:01.0 Ethernet controller
    unsigned int bar_register = read_pci_config(2, 1, 0, 0x10);
    pr_info("bar register:%02x\n", bar_register);

    if (bar_register & 0x01) {
        pr_info("I/O Mapped I/O\n");
        address = (bar_register&0xfffffffc);
    }
    else {
        pr_info("Memory Mapped I/O\n");
        if (((bar_register >> 1) & 0x03) == 0x00) {
            pr_info("32-BIT Address space\n");
            address = (bar_register & 0xFFFFFFF0U);
        }
        else {
            unsigned long int bar_register1 = read_pci_config(2, 1, 0, 0x14);
            unsigned int size, size_bar_register1, size_bar_register2;

            pr_info("64-bit address space\n");
            pr_info("bar register2:%02lx\n", bar_register1);
            address = (bar_register & 0xFFFFFFF0U) +
                ((bar_register1 & 0xFFFFFFFFU) << 32) ;


            //write all ones
            write_pci_config(2, 1, 0, 0x10, 0xfffffffffLU);
            write_pci_config(2, 1, 0, 0x14, 0xfffffffffLU);

            //read it
            size_bar_register1 = read_pci_config(2, 1, 0, 0x10);
            size_bar_register2 = read_pci_config(2, 1, 0, 0x14);

            //restore the original value
            write_pci_config(2, 1, 0, 0x10, bar_register);
            write_pci_config(2, 1, 0, 0x14, bar_register1);


            pr_info("size_bar_register1:%02x\n", size_bar_register1);
            pr_info("size_bar_register2:%02x\n", size_bar_register2);

            //Masking
            pr_info("size_bar_register1:%02x\n", ~size_bar_register1 + 1);
            pr_info("size_bar_register2:%02x\n", ~size_bar_register2 + 1);
        }
    }
    pr_info("BAR Address Register:%02llx\n", address);
    return 0;
}

void  __exit hello_exit(void)
{

}

MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

我得到 0x1fffc 作为输出而不是 0x20000。上面的代码有什么错误。

$ [29956.534327] bar register:fd5c0004
[29956.534328] Memory Mapped I/O
[29956.534339] 64-bit address space
[29956.534340] bar register2:00
[29956.536082] size_bar_register1:fffe0004
[29956.536083] size_bar_register2:ffffffff
[29956.536083] size_bar_register1:1fffc
[29956.536083] size_bar_register2:01
[29956.536083] BAR Address Register:fd5c0000

如果是内存映射IO区;在执行 ~size_bar_register1 + 1 部分之前,您需要清除最低的 4 位(用于可预取或不可预取标志、地址大小、类型等)。

例如:

    size_bar_register1 = read_pci_config(2, 1, 0, 0x10); // 0xFFFE0004
    size_bar_register1 &= ~15;                           // 0xFFFE0000
    size_bar_register1 = ~size_bar_register2 + 1;        // 0x00020000 = 128 KiB