如何用mmap将/proc/iomem中的PCIe MMCONFIG物理地址映射到用户态
PCIe MMCONFIG内存映射到用户态的实现方法
核心问题解答
mmap的fd参数需要填写**/dev/mem**的文件描述符,该设备文件是用户态访问系统物理内存的标准入口。
具体实现步骤
打开/dev/mem设备文件
需要以读写权限打开,代码示例:int fd = open("/dev/mem", O_RDWR); if (fd == -1) { perror("open /dev/mem failed"); exit(EXIT_FAILURE); }注意:必须以root权限运行程序,普通用户无访问/dev/mem的权限。
确定映射参数
从/proc/iomem获取的MMCONFIG范围是0xe0000000-0xefffffff:- 物理起始地址:
0xe0000000 - 映射长度:
0x10000000(256MB) - 页对齐检查:x86_64系统页大小通常为4KB,
0xe0000000是4KB对齐的,满足mmap对offset参数的页对齐要求。
- 物理起始地址:
调用mmap完成映射
参数配置说明:addr:传NULL让内核自动分配映射地址prot:PROT_READ | PROT_WRITE,允许读写映射区域flags:MAP_SHARED,确保对映射区域的修改同步到物理内存fd:前面打开的/dev/mem文件描述符offset:物理起始地址0xe0000000
完整代码示例:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <stdint.h> #define MMCONFIG_BASE 0xe0000000 #define MMCONFIG_SIZE 0x10000000 int main() { int fd = open("/dev/mem", O_RDWR); if (fd == -1) { perror("open /dev/mem"); return 1; } void *mmconfig_ptr = mmap(NULL, MMCONFIG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMCONFIG_BASE); if (mmconfig_ptr == MAP_FAILED) { perror("mmap failed"); close(fd); return 1; } // 示例:访问Bus 0, Device 0, Function 0的Vendor ID寄存器(偏移0x0) uint32_t vendor_id = *(uint32_t *)(mmconfig_ptr + (0 << 20) + (0 << 15) + (0 << 12) + 0x0); printf("Bus 0, Device 0, Function 0 Vendor ID: 0x%x\n", vendor_id); // 解除映射并关闭文件 munmap(mmconfig_ptr, MMCONFIG_SIZE); close(fd); return 0; }
关键注意事项
- 权限要求:必须以root权限执行程序,或给/dev/mem添加特殊权限(不推荐,存在安全风险)。
- 内核配置:确保内核开启了
CONFIG_DEVMEM选项,RHEL默认已开启,若关闭需重新编译内核。 - 地址合法性:映射后仅能访问MMCONFIG范围内的地址,非法访问会导致系统崩溃。
- 性能优势:内存映射方式相比端口IO(0xcfc/0xcf8),避免了频繁的用户态-内核态切换,效率更高。
内容的提问来源于stack exchange,提问作者Usr1
相关产品推荐
相关产品推荐

