部分AArch64平台调用vm_mmap映射PCI BAR触发崩溃问题咨询
问题根因
vm_mmap()本身仅负责分配用户态虚拟地址区间、创建VMA结构,不会自动将传入的offset参数识别为物理地址/PCIe BAR地址建立映射,实际的硬件地址到用户虚拟地址的页表映射逻辑,必须由传入的file对应的struct file_operations中的.mmap回调实现。- x86_64平台运行正常完全属于巧合:要么传入的file是
/dev/mem,x86_64下/dev/mem的mmap实现默认将offset作为物理地址处理,且该平台地址位布局下传入的0x1f40000000没有触碰到内核保留的标记位;要么是未实现自定义mmap回调时,x86_64的默认内存映射路径歪打正着建立了正确的页表,不代表代码写法符合规范。 - AArch64平台崩溃的直接原因:
- 直接将BAR物理地址作为offset传入,但没有实现对应的mmap回调处理该偏移,内核将其识别为普通文件偏移,在地址计算时错误地将物理地址的高位与虚拟地址的标记位混淆。报错地址中多出来的
0x800000高位,本质是部分AArch64平台内核配置用户虚拟地址位宽为42/44位时,高于用户地址位宽的位会被内核用作VMA类型标记,传入的40位BAR地址刚好触碰了该标记位,最终生成的0x8000001f40000000属于AArch64规范下的非法非规范地址——该地址第47位为1,但高16位没有按要求做符号扩展全置1,MMU会直接拒绝访问触发崩溃。 - 部分AArch64平台默认开启MTE内存标签、地址标记校验特性时,未正确标记为设备内存的映射区域会被CPU识别为非法访问,进一步触发异常。
- 直接将BAR物理地址作为offset传入,但没有实现对应的mmap回调处理该偏移,内核将其识别为普通文件偏移,在地址计算时错误地将物理地址的高位与虚拟地址的标记位混淆。报错地址中多出来的
修正方案
禁止直接在vm_mmap()调用时传入BAR物理地址,按内核标准流程实现PCIe BAR到用户态的映射:
- 为PCI设备驱动实现专属的
file_operations.mmap回调,不要依赖/dev/mem(多数发行版默认限制/dev/mem访问,且存在安全隐患)。 - 在自定义的mmap回调中完成以下操作:
- 校验映射长度
vma->vm_end - vma->vm_start,确保不超出BAR空间的实际大小,防止越界。 - 调用
pgprot_noncached()或pgprot_writecombine()修改VMA的内存属性为设备内存属性,关闭CPU对该区域的缓存机制,适配PCIe MMIO空间的访问要求。 - 为VMA设置
VM_IO、VM_PFNMAP、VM_DONTEXPAND标记,告知内核该区域是IO映射区域,不要执行页回写、页换出等普通内存操作。 - 调用
io_remap_pfn_range()将BAR空间对应的PFN(即BAR物理地址右移PAGE_SHIFT位)映射到VMA对应的用户虚拟地址区间。
- 校验映射长度
- 调用
vm_mmap()时,offset参数直接传0即可,所有物理地址相关的处理逻辑全部放到驱动的mmap回调中,不要让物理地址进入mmap通用路径的地址计算逻辑。修正后的调用示例:// 物理地址映射逻辑全部在驱动mmap回调中实现,此处offset传0 addr = vm_mmap(pci_dev_file, 0, bar_len, PROT_READ | PROT_WRITE, MAP_SHARED, 0); - 若特殊场景必须复用
/dev/mem做映射,需先明确校验BAR地址位宽不超过当前AArch64平台配置的物理地址位宽,映射前通过arch_phys_wc_add()类架构相关接口处理地址保留位,避免触碰内核标记位。
内容的提问来源于stack exchange,提问作者jreing
相关产品推荐
相关产品推荐

