Ubuntu 14.04下无内核模块实现PCIe DMA写入系统内存问题咨询
可行性结论
无需内核模块实现PCIe设备DMA写入主机内存的方案是可行的,你遇到的读写不生效、DMA传输失败问题是配置和操作细节疏漏导致的,具体解决方法如下:
问题排查与修复
确认预留内存可用
你设置mem=2048M限制内核仅使用前2GB物理内存,目标地址0x90000000(对应2304MB)确实不在内核管理范围内,但需先确认该区间未被BIOS、PCIe BAR等系统预留资源占用:
执行命令cat /proc/iomem | grep 90000000,如果该区间标注为reserved或归属其他设备,需要更换未被占用的高位内存区间。修复mmap映射的缓存同步问题
你当前写入不生效的核心原因是mmap默认开启CPU缓存,写入操作仅落到缓存中,没有同步到物理内存:
- 写入操作后调用
msync主动刷缓存到物理内存,同时失效旧的缓存页 - 可以在mmap参数中新增
MAP_LOCKED | MAP_POPULATE提前锁定物理页,避免页表置换
修改后的代码关键片段如下:
#include <stdio.h> #include <stdint.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> int main() { static volatile uint32_t *gpio = NULL; int fd; if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) return -1; gpio = (uint32_t *)mmap(0, 0x8000000, PROT_READ|PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, 0x90000000); if ((int64_t)gpio == -1) return -1; int i; for (i = 0; i < 32; i++) { printf("0x%08x\n", *(gpio+i)); } printf("Press any key when ready to continue...\n"); getchar(); *(gpio + 0) = 0x11223344; *(gpio + 1) = 0x55667788; // 新增缓存同步逻辑 msync((void*)gpio, 0x8000000, MS_SYNC | MS_INVALIDATE); printf("---\n"); for (i = 0; i < 32; i++) { printf("0x%08x\n", *(gpio+i)); } munmap((void*)gpio, 0x8000000); close(fd); return 0; }
- 修复DMA传输拦截问题
PCIe设备发起DMA传输失败大概率是IOMMU拦截导致的:
- 在内核启动参数中新增
iommu=off intel_iommu=off关闭IOMMU(VT-d/AMD-Vi),设备的DMA请求才能直接访问主机物理地址 - 确认PCIe设备的DMA传输配置中,目标地址为主机物理地址0x90000000,传输方向为设备到主机,长度不超过你预留的128MB(0x8000000)缓冲区大小
注意事项
- 先确认内核未开启严格devmem限制:查看
/boot/config-$(uname -r)中CONFIG_STRICT_DEVMEM的值为n,否则内核会禁止用户态映射普通物理内存 - 该方案仅适用于测试场景,生产环境建议使用内核驱动的标准DMA API分配缓冲区,兼容性和安全性更高
内容的提问来源于stack exchange,提问作者jakeh12
相关产品推荐
相关产品推荐

