阅读内存映射 IO 寄存器如何(来自数据表)并在 mmap 中使用它们
reading Memory-Mapped IO registers How to (from datasheet) and using them in 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 地址 (return (void*)=&0x00000000fe400000)= 但我得到类似 0xffffffff
的内容,errno 是 22。任何人都可以指导我关于如何从数据表中解释偏移量的正确方向。此外,通常需要访问哪些寄存器以获取 pci 以太网设备的数据包。由于我是设备编程新手。
Resource0是资源列表,剩余的Resource1 .. ResourceN是寄存器所在的实际内存区域。
(参考:https://techpubs.jurassic.nl/manuals/linux/developer/REACTLINUX_PG/sgi_html/ch07.html)
偏移量是进入某个区域的偏移量,而不是该区域的偏移量。
所以您通常会映射 region1 的整个 space - 在您的例子中是 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]
索引到寄存器 space.
第一个障碍是获取只读数据(例如,MAC 地址等)。
一旦你真正想要发送和接收数据包,你将需要物理地址 - DPDK 代码处理这个(以一种相当复杂的方式),但你可以使用 GRUB 参数切出物理内存并安全地使用它.. ..
另一种方法是在“/dev/mem”上使用 mmap,使用上面的偏移量 fe400000
)
无论如何,您以后都需要它来进行物理内存访问(并且您需要确保编译内核以进行访问-某些锁定的内核不是)
在内核函数iomem_is_exclusive中检查IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)或IORESOURCE_EXCLUSIVE 标志已设置:
if (IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)
|| p->flags & IORESOURCE_EXCLUSIVE) {
err = true;
break;
}
我们可以简单地使用stap来通过检查以进行测试:
stap -g -e 'probe kernel.function("iomem_is_exclusive").return { $return = 0 }'
我有 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 地址 (return (void*)=&0x00000000fe400000)= 但我得到类似 0xffffffff
的内容,errno 是 22。任何人都可以指导我关于如何从数据表中解释偏移量的正确方向。此外,通常需要访问哪些寄存器以获取 pci 以太网设备的数据包。由于我是设备编程新手。
Resource0是资源列表,剩余的Resource1 .. ResourceN是寄存器所在的实际内存区域。 (参考:https://techpubs.jurassic.nl/manuals/linux/developer/REACTLINUX_PG/sgi_html/ch07.html)
偏移量是进入某个区域的偏移量,而不是该区域的偏移量。
所以您通常会映射 region1 的整个 space - 在您的例子中是 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]
索引到寄存器 space.
第一个障碍是获取只读数据(例如,MAC 地址等)。
一旦你真正想要发送和接收数据包,你将需要物理地址 - DPDK 代码处理这个(以一种相当复杂的方式),但你可以使用 GRUB 参数切出物理内存并安全地使用它.. ..
另一种方法是在“/dev/mem”上使用 mmap,使用上面的偏移量 fe400000
)
无论如何,您以后都需要它来进行物理内存访问(并且您需要确保编译内核以进行访问-某些锁定的内核不是)
在内核函数iomem_is_exclusive中检查IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)或IORESOURCE_EXCLUSIVE 标志已设置:
if (IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)
|| p->flags & IORESOURCE_EXCLUSIVE) {
err = true;
break;
}
我们可以简单地使用stap来通过检查以进行测试:
stap -g -e 'probe kernel.function("iomem_is_exclusive").return { $return = 0 }'