在实际应用中,需要保留一段专用内存,给驱动程序或者应用程序,比如给PL 访问的一段内存,用于查表,等等。
本文介绍如何在petalinlux 里实现保留一段内存。在这里我引用原文有3种方式预留内存:普通的,DMA,CMA。我的应用中打算使用普通的,32位方式。但这里3种方式都介绍。
其中重要的参考来自:https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841683/Linux+Reserved+Memory
预留内存
要从系统地址空间保留内存范围,可以在设备树配置中使用保留的内存节点。 每个子节点定义一个特定的内存空间,并且可以根据内核文档中所述可用于保留内存节点的不同参数进行配置。 然后可以通过memory-region参数将保留的存储空间分配给特定的设备驱动程序。
用于64位Cortex-A53 MPSoC的system-top.dts文件中的设备树节点:
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
reserved: buffer@0 {
no-map;
reg = <0x0 0x70000000 0x0 0x10000000>;
};
};
reserved-driver@0 {
compatible = "xlnx,reserved-memory";
memory-region = <&reserved>;
};
或者在基于Yocto的Petalinux project-spec / meta-user / recipes-bsp / device-tree / files / system-user.dtsi 中针对32位Cortex-A9 Zynq进行定制的类似设备树节点:
/include/ "system-conf.dtsi"
/ {
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
reserved: buffer@0x38000000 {
no-map;
reg = <0x38000000 0x08000000>;
};
};
reserved-driver@0 {
compatible = "xlnx,reserved-memory";
memory-region = <&reserved>;
};
};
在设备驱动程序中,可以通过解析设备树节点来处理内存区域的属性,并且一旦知道了物理地址和大小,就可以使用memremap / ioremap调用来映射内存区域。 下面的代码引用了保留的内存分配:
/* Get reserved memory region from Device-tree */
np = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!np) {
dev_err(dev, "No %s specified\n", "memory-region");
goto error1;
}
rc = of_address_to_resource(np, 0, &r);
if (rc) {
dev_err(dev, "No memory address assigned to the region\n");
goto error1;
}
lp->paddr = r.start;
lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB);
dev_info(dev, "Allocated reserved memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
由于保留的内存区域已被内核排除以用于通用,并标记为无映射,因此iomem信息(/ proc / iomem)显示系统RAM小于板上的内存量。
root@plnx_aarch64:~# cat /proc/iomem
00000000-6fffffff : System RAM
00080000-00b37fff : Kernel code
011c9000-012b8fff : Kernel data
Once the device is loaded, the allocation can be confirmed:
[ 126.191774] reserved-memory reserved-driver@0: Device Tree Probing
[ 126.198595] reserved-memory reserved-driver@0: Allocated reserved memory, vaddr: 0xFFFFFF8020000000, paddr: 0x70000000
通过DMA API保留的内存
通常,保留内存空间与DMA引擎一起使用,因此从设备驱动程序的角度来看,集成这两个框架可能很有用。 对于该特定目的,可以将兼容属性设置为shared-dma-pool,从而生成为特定设备驱动程序保留的DMA内存池。
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
reserved: buffer@0 {
compatible = "shared-dma-pool";
no-map;
reg = <0x0 0x70000000 0x0 0x10000000>;
};
};
reserved-driver@0 {
compatible = "xlnx,reserved-memory";
memory-region = <&reserved>;
};
这样,设备驱动程序仅需要以常规方式使用DMA API,但无需使用默认的CMA内存池,而是将为此特定设备使用保留的内存区域。
/* Initialize reserved memory resources */
rc = of_reserved_mem_device_init(dev);
if(rc) {
dev_err(dev, "Could not get reserved memory\n");
goto error1;
}
/* Allocate memory */
dma_set_coherent_mask(dev, 0xFFFFFFFF);
lp->vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &lp->paddr, GFP_KERNEL);
dev_info(dev, "Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
内核日志是这样的:
[ 0.000000] Reserved memory: created DMA memory pool at 0x0000000070000000, size 256 MiB
[ 0.000000] Reserved memory: initialized node buffer@0, compatible id shared-dma-pool
[ 0.000000] cma: Reserved 128 MiB at 0x0000000068000000
在启动驱动程序后,内核日志是这样的:
root@plnx_aarch64:~# insmod /lib/modules/4.6.0-xilinx/extra/reserved-memory.ko
[ 80.745166] reserved-memory reserved-driver@0: Device Tree Probing
[ 80.750183] reserved-memory reserved-driver@0: assigned reserved memory node buffer@0
[ 81.220878] reserved-memory reserved-driver@0: Allocated coherent memory, vaddr: 0xFFFFFF8020000000, paddr: 0x70000000
为CMA保留的内存
有时,不需要将保留的内存区域分配给特定的设备驱动程序,而只打算拥有比默认的更大的CMA内存池。 对于该特定用例,可以使用一个额外的属性来指向内核,以将保留的内存区域用作默认的CMA内存池。
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
reserved: buffer@0 {
compatible = "shared-dma-pool";
reusable;
reg = <0x0 0x70000000 0x0 0x10000000>;
linux,cma-default;
};
};
内核日志是这样的:
[ 0.000000] Reserved memory: created CMA memory pool at 0x0000000070000000, size 256 MiB
[ 0.000000] Reserved memory: initialized node buffer@0, compatible id shared-dma-pool