【代码模板】Linux内核中对PCIe BAR空间通用打印函数?(pci_resource_start、pci_resource_len、pdev)

背景

Linux内核态操作PCIe配置空间非常方便,接口也比较成熟。本文简单记录一个打印bar空间的函数,入参是标准的pci_dev。打印PCI设备所有BAR(Base Address Register)空间信息。该函数会遍历并打印每个BAR的基址、大小和类型等信息。

代码细节

#include <linux/pci.h>
#include <linux/device.h>

/**
 * print_pci_bar_info - 打印PCI设备的所有BAR信息
 * @pdev: PCI设备指针
 *
 * 该函数遍历PCI设备的所有BAR(0-5),并打印每个BAR的相关信息,
 * 包括基址、大小和类型(内存或I/O)。
 */
void print_pci_bar_info(struct pci_dev *pdev)
{
    int i;
    resource_size_t start, size;
    const char *type_str;

    if (!pdev) {
        dev_err(NULL, "Invalid PCI device pointer\n");
        return;
    }

    dev_info(&pdev->dev, "PCI device %04x:%02x %02x.%d BAR information:\n",
             pdev->vendor, pdev->device, pdev->bus->number,
             PCI_SLOT(pdev->devfn));

    for (i = 0; i < 6; i++) {
        start = pci_resource_start(pdev, i);
        size = pci_resource_len(pdev, i);

        if (start == 0 && size == 0) {
            dev_info(&pdev->dev, "  BAR %d: Not used\n", i);
            continue;
        }

        if (pci_resource_flags(pdev, i) & IORESOURCE_IO)
            type_str = "I/O";
        else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM)
            type_str = "Memory";
        else
            type_str = "Unknown";

        dev_info(&pdev->dev, "  BAR %d: %s at 0x%llx, size 0x%llx (%llu bytes)\n",
                 i, type_str,
                 (unsigned long long)start,
                 (unsigned long long)size,
                 (unsigned long long)size);
    }
}

函数说明:

  1. 该函数接收一个struct pci_dev指针作为参数
  2. 遍历PCI设备的6个BAR寄存器(索引0-5)
  3. 对于每个BAR,获取其起始地址、长度和标志
  4. 根据标志判断BAR类型(内存或I/O)
  5. 使用dev_info打印规范化的BAR信息
  6. 会正确处理未使用的BAR(起始地址和长度均为0的情况)

关键接口:

pci_resource_start 获取资源启示地址,函数内部实现是直接读取的struct中resource的数组,并且根据barid来的
pci_resource_len 资源大小,是根据end - start算出来的
pci_resource_flags 自动读取标志
dev_info 能够直接读取pcie的device的信息

输出示例:

PCI device 8086:1234 BAR information:
  BAR 0: Memory at 0xf0000000, size 0x1000000 (16777216 bytes)
  BAR 1: Not used
  BAR 2: I/O at 0x00003000, size 0x00000100 (256 bytes)
  BAR 3-5: Not used

实践

本文实验是放到了Mellanox OFED网卡驱动中,由于依赖较多不放在这里,仅做金丹展示,其他驱动类似,只需要传入pdev接口,并且pdev入参是系统传过来的。是PCIe驱动程序被PCIe框架调用的时候根据pci table匹配到的pcie的device。可以直接使用。
在这里插入图片描述
效果:
在这里插入图片描述
使用它lspci查看bar空间效果相同:
在这里插入图片描述

综述

本文整体围绕打印 PCI 设备 BAR 空间信息函数的实现、关键接口、应用场景和运行效果进行了介绍 ,提供了在 Linux 内核态打印 PCI 设备 BAR 空间信息的方法。核心关键就是pdev数据结构存储了很多信息,可以直接用内核接口访问,方便后期快速取用和进一步迭代

<think>好的,我现在需要回答用户关于在Linux内核编程中使用pci_resource_len的问题。首先,我需要回忆一下相关的知识,确保自己理解正确。 用户提到他们想查找与pci_resource_len相关的技术信息或用法,特别是在Linux内核编程中。我记得在Linux内核中,PCI设备资源管理是一个重要的部分,而pci_resource_len应该是用来获取设备资源长度的函数之一。不过具体用法和参数可能不太清楚,需要进一步确认。 首先,我应该确定pci_resource_len函数的定义和作用。根据以往的知识,PCI设备在配置空间中有多个资源区域,如内存区域和I/O端口。驱动程序需要获取这些资源的信息,比如起始地址和长度,以便正确映射和使用。pci_resource_len函数的作用应该是返回特定资源区域的长度。 接下来,我需要检查参数是否正确。通常,这个函数的参数可能包括指向pci_dev结构体的指针,以及资源编号。例如,pci_dev代表PCI设备,而资源编号指定要查询哪个资源。资源编号通常从0开始,对应BAR0、BAR1等。 然后,我需要考虑这个函数的使用场景。当驱动程序需要映射设备的内存区域到内核的虚拟地址空间时,通常会先获取资源的起始地址和长度,然后使用ioremap等函数进行映射。例如,使用pci_resource_start获取起始地址,pci_resource_len获取长度,再调用ioremap。 此外,可能需要注意资源是否已经分配。在访问资源之前,驱动程序应该调用pci_request_regions来请求这些资源,确保没有其他驱动冲突。如果资源已经被占用,请求会失败,驱动程序需要进行错误处理。 还需要确认函数是否在正确的上下文中使用,比如是否在模块的初始化函数中调用,或者是否在probe函数中。另外,资源编号的有效性也很重要,不能超过设备支持的数量,否则可能访问到无效的资源。 可能会有一个例子,比如在驱动程序中获取第一个BAR的长度: struct pci_dev *pdev; resource_size_t length = pci_resource_len(pdev, 0); 然后,这个长度可以用于后续的映射操作。同时,需要检查返回值,确保资源存在且有效,例如长度是否为0,或者起始地址是否有效。 可能还需要参考内核文档或源代码中的实例,以确认正确的用法。例如,在内核的驱动代码中,如网卡驱动或存储驱动,可以找到使用pci_resource_len的实例。 另外,需要注意不同的资源类型,比如I/O资源和内存资源,它们的处理方式可能不同。但pci_resource_len应该适用于所有类型的资源,返回对应的长度。 总结来说,pci_resource_len用于获取PCI设备特定资源区域的长度,参数是pci_dev指针和资源编号,返回长度。使用时需要确保资源已经请求,并在映射前正确获取起始地址和长度。</think>在Linux内核编程中,`pci_resource_len`函数用于获取PCI设备指定资源的长度。该函数与`pci_resource_start`和`pci_resource_flags`配合使用,可完整描述PCI设备资源特性[^2]。 ### 函数原型与参数 ```c unsigned long pci_resource_len(struct pci_dev *dev, int bar); ``` - **dev**: 指向`pci_dev`结构体的指针,表示目标PCI设备 - **bar**: 基地址寄存器(BAR)的索引号(0-5) ### 典型使用场景 1. **资源获取流程**: ```c struct pci_dev *pdev; unsigned long start = pci_resource_start(pdev, 0); // 获取BAR0起始地址 unsigned long length = pci_resource_len(pdev, 0); // 获取BAR0长度 void __iomem *addr = ioremap(start, length); // 内存映射 ``` 2. **资源有效性检查**: ```c if (!pci_resource_len(pdev, bar)) { dev_err(&pdev->dev, "无效的BAR%d资源长度\n", bar); return -EINVAL; } ``` 3. **DMA缓冲区设置**: ```c dma_addr_t dma_handle; void *buffer = dma_alloc_coherent(&pdev->dev, pci_resource_len(pdev, 0), &dma_handle, GFP_KERNEL); ``` ### 注意事项 1. 调用前需先执行`pci_enable_device()`启用设备 2. 应配合`pci_request_regions()`进行资源预留 3. 对于64位地址空间,需要组合BARn和BARn+1的值 4. 通过`pci_resource_flags()`可判断资源类型(I/O空间或内存空间) [^1]: Now we have a dummy or template driver ready. Compile the module and insert it as explained in Table 8 (assuming the kernel source is in /usr/src/linux-2.4.18 ). gcc -c rtl8139.c -D__KERNEL__ -DMODULE -I /usr/src/linux-2.4.18/include insmod rtl8139.o [^2]: 如果芯片设备安装在某个板上,了解如何寻址其I/O共享内存空间是很重要的。还必须知道找到芯片的总线上的物理地址、存储器芯片空间的大小和总线宽度。这可以在map_info结构的第一个条目中找到... 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值