如何在Petalinux/Yocto中创建DMA访问的预留内存区域?
解决MicroBlaze平台DMA预留内存不生效及替代方案
先排查当前设备树配置的问题
- 内核驱动编译检查:你使用的
xlnx,reserved-memory驱动需要编译进内核(或作为模块加载),请确认内核配置中开启了CONFIG_XILINX_RESERVED_MEM和CONFIG_RESERVED_MEM。如果驱动未加载,设备树中的配置不会生效,自然不会在/proc/iomem或dmesg中看到对应设备。 - MicroBlaze内存属性适配:MicroBlaze架构下,
no-map属性会让内核不创建虚拟地址映射,但如果你的驱动需要访问这段内存,可能需要手动在驱动中做ioremap操作。同时要确保reg中的地址范围是MicroBlaze可访问的物理地址,且未被其他硬件或内核占用。
替代的DMA预留内存方法
方法1:通过内核启动参数直接预留内存
使用memmap参数强制内核保留指定物理内存区域,无需依赖设备树驱动:
- 在U-Boot的启动参数中添加:
memmap=0x10000000$0xa0000000- 解释:
0x10000000是要预留的内存大小,0xa0000000是起始物理地址
- 解释:
- 启动后可以通过
cat /proc/iomem看到这段内存被标记为reserved - 驱动中访问时,使用
request_mem_region申请区域权限,再通过ioremap_nocache映射为虚拟地址(MicroBlaze必须用非缓存映射避免DMA缓存一致性问题):
#define RESERVED_BASE 0xa0000000 #define RESERVED_SIZE 0x10000000 void __init my_dma_init(void) { if (!request_mem_region(RESERVED_BASE, RESERVED_SIZE, "dma_buffer")) { pr_err("Failed to request reserved memory region\n"); return; } void *virt_addr = ioremap_nocache(RESERVED_BASE, RESERVED_SIZE); if (!virt_addr) { pr_err("Failed to ioremap reserved memory\n"); release_mem_region(RESERVED_BASE, RESERVED_SIZE); return; } // 后续使用virt_addr进行DMA操作 }
方法2:修复CMA访问崩溃问题
你之前使用CMA崩溃,大概率是MicroBlaze的缓存一致性未处理正确,调整后可正常使用:
- 设备树中定义专属CMA区域:
reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; cma_dma: cma@0xa0000000 { compatible = "shared-dma-pool"; reusable; reg = <0x0 0xa0000000 0x0 0x10000000>; linux,cma-default; }; };
- 驱动中使用正确的DMA分配接口:
不要直接访问CMA物理地址,而是用dma_alloc_coherent申请缓存一致的内存,或者用dma_alloc_from_contiguous从指定CMA区域申请:
struct device *dev = &my_dev->dev; dma_addr_t dma_addr; void *virt_addr = dma_alloc_coherent(dev, 0x1000, &dma_addr, GFP_KERNEL); if (!virt_addr) { pr_err("DMA alloc failed\n"); return -ENOMEM; } // 使用virt_addr(虚拟地址)和dma_addr(DMA可访问的物理地址)进行操作
- 缓存操作兜底:如果必须直接访问物理地址,在DMA传输前后执行缓存刷新/失效:
// 写入内存后,刷新缓存让DMA可见 flush_dcache_range((unsigned long)virt_addr, (unsigned long)virt_addr + size); // DMA读取完成后,失效缓存让CPU可见 invalidate_dcache_range((unsigned long)virt_addr, (unsigned long)virt_addr + size);
方法3:驱动静态申请预留内存
如果不需要设备树标准化的预留,可直接在驱动中配合内核启动参数的memmap使用:
- 用
memmap参数保留内存(同方法1) - 驱动中通过
request_mem_region申请权限,再ioremap映射,确保操作前这段内存未被内核占用。
内容的提问来源于stack exchange,提问作者Makuta Arguilleres
相关产品推荐
相关产品推荐

