reserved-memory驱动开发:memremap()与virt_to_phys()地址偏移问题
问题:reserved-memory驱动中virt_to_phys返回地址与原物理地址偏移异常
我正在为reserved-memory区域实现自定义Linux驱动,遵循Xilinx官方文档开发。内存预留已正常工作,probe函数中代码如下:
paddr = r.start; vaddr = memremap(paddr, 4096, MEMREMAP_WB); printk("Allocated reserved memory, vaddr: 0x%0X, paddr: 0x%0X\n", (u32)vaddr, paddr); printk("Allocated reserved memory, paddr (2): 0x%0x\n", virt_to_phys(vaddr));
实际输出:
[10370.743577] Allocated reserved memory, vaddr: 0xE0847000, paddr: 0x20000000 [10370.750564] Allocated reserved memory, paddr (2): 0x20847000
原物理地址为0x20000000,但virt_to_phys返回的地址偏移了0x847000,请问该偏移来源是什么?如何确保驱动访问正确的物理地址0x20000000?
偏移来源分析
- 函数适用范围错误:
virt_to_phys仅对内核线性直接映射区的虚拟地址有效,而memremap(MEMREMAP_WB)返回的是vmalloc动态映射区的虚拟地址。对vmalloc地址调用virt_to_phys,得到的是vmalloc页表对应的物理页地址,而非你预留的物理内存地址,0x847000是这个错误转换产生的无效偏移。 - memremap映射逻辑:MEMREMAP_WB模式下,内核会将预留物理内存映射到vmalloc区域,而非直接线性映射,因此虚拟地址和物理地址不存在固定的线性偏移关系。
解决方法
方案一:直接保留原始物理地址
驱动中直接保存初始化时获取的paddr(即r.start),后续需要使用物理地址时直接调用该值即可,无需通过虚拟地址转换。这是最简单高效的方式。
方案二:使用正确的地址转换函数(仅特殊场景需要)
如果必须从memremap返回的虚拟地址反推物理地址,需使用适用于vmalloc地址的转换逻辑:
struct page *page = virt_to_page(vaddr); phys_addr_t correct_paddr = page_to_phys(page) + (vaddr - (void *)page_address(page));
但此方法完全没必要,因为你已经持有原始的paddr。
关键注意事项
- 直接使用
memremap返回的vaddr进行内存读写即可,它已经正确映射到你预留的0x20000000物理地址,无需额外转换。 - 区分内核虚拟地址区域:线性直接映射区(virt_to_phys可用)、vmalloc动态映射区(需用page相关函数转换)、ioremap映射区(virt_to_phys可用)。
内容的提问来源于stack exchange,提问作者moudi
相关产品推荐
相关产品推荐

