如何通过内存映射从Linux内核空间访问PCI内存(内核3.14)

时间:2016-02-28 12:23:47

标签: linux-kernel kernel kernel-module pci

我正在寻找一种方法来访问PCI设备的内存空间(显式BAR2和BAR3)而不使用DMA和IO映射。我已经阅读了很多文档,但我从未见过流程图或一步一步如何。所以我所有的尝试都没有成功。

这些是pci_probe我实际尝试的步骤:

  1. data = kzalloc( sizeof(*data) , GFP_KERNEL );
  2. pci_set_drvdata(pdev, data);
  3. pci_enable_device(pdev);
  4. 现在的问题是使用BAR2+offsetwriteb访问readb的正确地址是什么?或者是否有另一个从这个空间读/写的功能?

    PS:关于iomap的类似问题已发布here

2 个答案:

答案 0 :(得分:1)

经过大量研究,我找到了一种方法,可以读写PCI BAR2。似乎ioremappci_ioremap_barmemremap()(内核4.3+)允许CPU缓存在PCI设备和内核空间内存之间传输的数据。这会导致数据损坏。但我不知道它最终来自何处。

解决此问题的方法是使用ioremap_nocache中的io.h。以下代码显示了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;
}

此外:

  1. 使用iowrite访问映射内存不起作用。有时在PCI BAR2内存中发现了人为的废话。也许这个命令有保持序列。我不知道。
  2. 在我的情况下,BAR2内存的某些地址范围需要写入两次。我认为这是此设备的特殊行为。
  3. 在我的情况下,并非所有地址范围都可以通过32位访问来访问。我认为这也是这种设备的一种特殊行为。

答案 1 :(得分:0)

便携式方法是使用pci_iomap()函数。它会自动确定BAR的类型并与MMIO和PIO一起使用。而不是writeb()/ readb()你应该使用ioread *()/ iowrite *()