如何通过内存映射(内核 3.14)从 Linux 内核 space 访问 PCI 内存
How to access PCI memory from Linux kernel space by memory mapping (Kernel 3.14)
我正在寻找一种无需使用 DMA 和 IO 映射即可访问 PCI 设备(显式 BAR2 和 BAR3)内存 space 的方法。我已经阅读了很多文档,但我从来没有看到流程图或一步一步的操作方法。所以我所有的尝试都不成功。
这些是里面的步骤pci_probe
我实际尝试过:
data = kzalloc( sizeof(*data) , GFP_KERNEL );
pci_set_drvdata(pdev, data);
pci_enable_device(pdev);
现在的问题是使用 writeb
或 readb
访问 BAR2+offset
的正确地址是什么?或者是否有从这个 space 到 read/write 的另一个函数?
PS:关于 iomap
的类似问题已发布 here。
经过大量研究,我找到了一种读写 PCI 的方法 BAR2
。似乎 ioremap
、pci_ioremap_bar
或 memremap()
(内核 4.3+)允许 CPU 缓存 PCI 设备和内核 space 内存之间传输的数据。这会导致损坏的数据。但我不知道它最终是从哪里来的。
解决此问题的方法使用 io.h
中的 ioremap_nocache
。以下代码显示了 PCI 探测功能。
static int
_pci_probe ( struct pci_dev *pdev,
const struct pci_device_id *ent )
{
int ret = 0;
int i;
unsigned long *pbas2addr;
u8 buf8;
u8 *mem8;
buf8 = 0xF0;
// put mem8 to the heap and initialize them with zeros
mem8 = kcalloc((0x020000),sizeof(u8), GFP_KERNEL);
// enabling the device
ret = pci_enable_device(pdev);
if( ret )
{
printk(KERN_ERR "Failed to enable PCI device.\n");
goto no_enable;
}
// take ownership of pci related regions
pci_request_regions(pdev, "expdev");
// checking if PCI-device reachable by checking that BAR0 is defined and
// memory mapped
if( !(pci_resource_flags(pdev,0) & IORESOURCE_MEM) )
{
printk(KERN_ERR "Incorrect BAR configuration.\n");
ret = -ENODEV;
goto bad_bar;
}
// remap BAR2 avoiding the use of CPU cache
pbas2addr = ioremap_nocache(pci_resource_start(pdev,2),
pci_resource_len(pdev,2));
printk(KERN_INFO "BAR2 Addr: %p\n",pbas2addr);
printk(KERN_INFO "BAR2 len: %x\n",(int)pci_resource_len(pdev,2));
// write something to BAR2
buf8 = 0xF0;
for ( i = 0x000000; i<0x020000; i++ )
{
*((u8*)pbas2addr+i) = buf8; // it's important to cast the pointer
}
// read back
buf8 = 0;
for ( i = 0x000000; i<0x020000; i++ )
{
mem8[i] = *((u8*)pbas2addr+i);
}
return 0;
bad_bar:
pci_disable_device(pdev);
no_enable:
return ret;
}
此外:
- 使用
iowrite
访问映射内存并不稳定。有时会在 PCI BAR2
内存中发现人工垃圾。也许这个命令附带了保持序列。我不知道。
- 在我的例子中,
BAR2
内存的某些地址范围需要写入两次。我认为这是该设备的特殊行为。
- 在我的例子中,并不是所有的地址范围都可以通过 32 位访问达到。我认为这也是该设备的特殊行为。
一种可移植的方法是使用 pci_iomap() 函数。它会自动确定 BAR 的类型并与 MMIO 和 PIO 一起使用。而不是 writeb()/readb() 你应该使用 ioread*()/iowrite*()
我正在寻找一种无需使用 DMA 和 IO 映射即可访问 PCI 设备(显式 BAR2 和 BAR3)内存 space 的方法。我已经阅读了很多文档,但我从来没有看到流程图或一步一步的操作方法。所以我所有的尝试都不成功。
这些是里面的步骤pci_probe
我实际尝试过:
data = kzalloc( sizeof(*data) , GFP_KERNEL );
pci_set_drvdata(pdev, data);
pci_enable_device(pdev);
现在的问题是使用 writeb
或 readb
访问 BAR2+offset
的正确地址是什么?或者是否有从这个 space 到 read/write 的另一个函数?
PS:关于 iomap
的类似问题已发布 here。
经过大量研究,我找到了一种读写 PCI 的方法 BAR2
。似乎 ioremap
、pci_ioremap_bar
或 memremap()
(内核 4.3+)允许 CPU 缓存 PCI 设备和内核 space 内存之间传输的数据。这会导致损坏的数据。但我不知道它最终是从哪里来的。
解决此问题的方法使用 io.h
中的 ioremap_nocache
。以下代码显示了 PCI 探测功能。
static int
_pci_probe ( struct pci_dev *pdev,
const struct pci_device_id *ent )
{
int ret = 0;
int i;
unsigned long *pbas2addr;
u8 buf8;
u8 *mem8;
buf8 = 0xF0;
// put mem8 to the heap and initialize them with zeros
mem8 = kcalloc((0x020000),sizeof(u8), GFP_KERNEL);
// enabling the device
ret = pci_enable_device(pdev);
if( ret )
{
printk(KERN_ERR "Failed to enable PCI device.\n");
goto no_enable;
}
// take ownership of pci related regions
pci_request_regions(pdev, "expdev");
// checking if PCI-device reachable by checking that BAR0 is defined and
// memory mapped
if( !(pci_resource_flags(pdev,0) & IORESOURCE_MEM) )
{
printk(KERN_ERR "Incorrect BAR configuration.\n");
ret = -ENODEV;
goto bad_bar;
}
// remap BAR2 avoiding the use of CPU cache
pbas2addr = ioremap_nocache(pci_resource_start(pdev,2),
pci_resource_len(pdev,2));
printk(KERN_INFO "BAR2 Addr: %p\n",pbas2addr);
printk(KERN_INFO "BAR2 len: %x\n",(int)pci_resource_len(pdev,2));
// write something to BAR2
buf8 = 0xF0;
for ( i = 0x000000; i<0x020000; i++ )
{
*((u8*)pbas2addr+i) = buf8; // it's important to cast the pointer
}
// read back
buf8 = 0;
for ( i = 0x000000; i<0x020000; i++ )
{
mem8[i] = *((u8*)pbas2addr+i);
}
return 0;
bad_bar:
pci_disable_device(pdev);
no_enable:
return ret;
}
此外:
- 使用
iowrite
访问映射内存并不稳定。有时会在 PCIBAR2
内存中发现人工垃圾。也许这个命令附带了保持序列。我不知道。 - 在我的例子中,
BAR2
内存的某些地址范围需要写入两次。我认为这是该设备的特殊行为。 - 在我的例子中,并不是所有的地址范围都可以通过 32 位访问达到。我认为这也是该设备的特殊行为。
一种可移植的方法是使用 pci_iomap() 函数。它会自动确定 BAR 的类型并与 MMIO 和 PIO 一起使用。而不是 writeb()/readb() 你应该使用 ioread*()/iowrite*()