读取内存映射 IO 寄存器如何来自数据表并在 mmap 中使用它们

问题描述

我有 Ethernet controller: Intel Corporation 82579LM Gigabit Network Connection (Lewisville) (rev 04) 的英特尔系统。我已经下载了数据表,如 Intel ® 82579 Gigabit Ethernet PHY

的数据表

现在我正在读取 pci 以太网设备的资源 0,例如

    if((fd = open("/sys/bus/pci/devices/0000:00:19.0/resource1",O_RDWR | O_SYNC)) == -1) {
        perror("Error: open error");
    }

    int map_size = 4096UL;
    ...
    map_base = mmap(0,4096UL,PROT_READ,MAP_SHARED,fd,Register_Values_From_Datasheet);

我有 x86-64 位系统。卡利 Linux 5.7。 所以我将上面的 Register_Values_From_Datasheet 替换为来自数据表的寄存器偏移值,例如 0x00008 代替 STATUS:Device Status Register=from datasheet

但是 mmap() 的 errno 在 mmap 调用后具有值 22:EINVAL。意味着无效的论点。也许我从数据表中读取的偏移值可能需要以其他方式解释。

我在以太网设备的sys/bus/pci/devices/0000:00:19.0中的资源文件是这样的

        0x00000000fe400000 0x00000000fe41ffff 0x0000000000040200
        0x00000000fe427000 0x00000000fe427fff 0x0000000000040200
        0x000000000000f060 0x000000000000f07f 0x0000000000040101
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000

从上面的资源文本文件 (resource0) 转储显示,我的内存映射 IO 从 0x00000000fe400000 开始。所以我认为应该是 mmap 的返回地址 (return (void*)=&0x00000000fe400000)= 但我得到了类似 0xffffffff 的东西,而 errno 是 22。任何人都可以指导我正确的方向如何偏移量需要从数据表中解释。此外,通常需要访问哪些寄存器以获取 pci 以太网设备的数据包。因为我是设备编程的新手。

解决方法

Resource0 是资源列表,剩下的 Resource1 .. ResourceN 是寄存器所在的实际内存区域。 (参考:https://techpubs.jurassic.nl/manuals/linux/developer/REACTLINUX_PG/sgi_html/ch07.html

偏移量是一个区域内的偏移量,而不是该区域的偏移量。

因此,您通常会映射 region1 的整个空间 - 在您的情况下为 128KB,因为:0x00000000fe41ffff - 0x00000000fe400000 。 (您也可以检查 /proc/iomem 进行确认)

map_base = mmap(0,32*4096UL,PROT_READ,MAP_SHARED,fd,0); 

编辑: mmap 的签名是:

void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);

如果您指定的长度或偏移量超过文件大小,则会得到 EINVAL: 来自 Linux 手册页:

EINVAL We don't like addr,length,or offset (e.g.,they are too large,or not aligned on a page boundary).

您可能最终将 map_base 转换为 uint32_t 数组(如果所有寄存器都是 32 位)并使用:map_base_as_int[8/4] 索引到寄存器空间。

第一个障碍是获取只读数据(例如 MAC 地址等)。

一旦你真的想要发送和接收数据包,你就需要物理地址——DPDK代码处理这个(以一种相当复杂的方式),但你可以用GRUB参数切出物理内存并安全地使用它......


另一种方法是使用上面的偏移量在“/dev/mem”上使用 mmap fe400000) 无论如何,稍后您将需要它来访问物理内存(并且您需要确保编译内核以供访问 - 一些锁定的内核不是)