我正在为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?
答案 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)