PCIE区域未对齐,且不一致

时间:2017-11-03 02:47:27

标签: c linux linux-kernel driver pci

我正在为openwrt开发PCIE设备驱动程序,在尝试访问定时器中断中的io-memory时遇到了数据总线错误,我在last question中提到过。经过大量研究后,我想我可能找到了原因,但我无法解决。以下是我的麻烦。

上周我发现系统启动时pcie区域大小可能已经改变。我的驱动程序中bar0的区域大小为4096(从pci_resource_len返回),lspci -vv中的区域大小为4097,这打破了linux内核的页面大小。通过阅读pciutil的源代码,我发现lspci命令从/sys/devices/pci0000:00/0000:00:00.0/resouce文件中获取pcie信息。所以我删除了所有自定义组件并在我的路由器上运行原始的openwrt。通过cat /sys/devices/pci0000:00/0000:00:00.0/resouce,结果的第一行(bar0)是

0x0000000010008000 0x0000000010009000 0x0000000000040200

此外,我还检查了/proc/iomem的内容,与PCIE相关的内容是

10000000-13ffffff : mem_base
    10000000-13ffffff : PCI memory space
        10000000-10007fff : 0000:00:00.0
        10008000-10008fff : 0000:00:00.0

上面两个文件指示的bar0的区域大小不同,这是非常奇怪的!根据PCIE的机制,区域大小应始终为2的幂。区域大小如何变为4097?

1 个答案:

答案 0 :(得分:0)

花了几周阅读linux内核的源代码后,我发现这是linux内核4.4.14的一个错误。

/sys/devices/pci0000:00/0000:00:00.0/resouce的内容是通过文件resource_show中的函数drivers/pci/pci-sysfs.c生成的。相关代码是

for (i = 0; i < max; i++) {
    struct resource *res =  &pci_dev->resource[i];
    pci_resource_to_user(pci_dev, i, res, &start, &end);
    str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n",
               (unsigned long long)start,
               (unsigned long long)end,
               (unsigned long long)res->flags);
}

实际调用的函数pci_resource_to_user位于arch/mips/include/asm/pci.h

static inline void pci_resource_to_user(const struct pci_dev *dev, int bar,
        const struct resource *rsrc, resource_size_t *start,
        resource_size_t *end)
{
    phys_addr_t size = resource_size(rsrc);

    *start = fixup_bigphys_addr(rsrc->start, size);
    *end = rsrc->start + size;
}

*end的计算错误,应由

替换
*end = rsrc->start + size - (size ? 1 : 0)