bro*_*ous 7 embedded mmap linux-kernel embedded-linux microblaze
我正在Xilinx的Microblaze上运行带有完整MMU的Linux内核3.3.我正在做的任务要求我知道以下内容:我需要创建一个文本文件(缓冲区)并找到该缓冲区的物理地址,我不希望内核将此文件写入不连续的内存区域.
我需要这个的原因是因为我有一个DMA引擎从预设的物理内存地址流式传输数据,所以我需要强制Linux在那个确切的内存位置创建缓冲区文件,以便当我将数据写入此文件时立即传输由DMA引擎到另一个硬件核心
更多细节:
我的系统有一个512 MB DDR3 RAM通过"Xilinx"多端口内存控制器(MPMC)连接到系统.该内存控制器的基地址为0x90000000,系统中的所有单元通过该控制器访问内存,包括MicroBlaze,DMA我所使用的单元使用称为本机个性化接口(NPI)的特殊接口,以非常低的级别与存储器通信,从而产生非常高的速度性能.
这个NPI DMA单元最初设计用于一个名为"xilkernel"的非常基本的嵌入式内核,它不支持虚拟内存,MMU也不是MicroBlaze的一部分,因此程序员可以看到操作系统代码所在的位置并选择物理内存地址如0x91800000作为DMA将从中流出的源地址,然后程序员可以将文件放在该确切的地址中并运行系统
当我们需要迁移项目以使用Linux而不是xilkernel时我们遇到了这个问题,我在外部存储设备上有文件,我可以从Linux访问作为块设备,我需要将每个文件移动到主内存(DDR3 RAM)并使DMA流成为文件.目前来自固定地址的DMA流,但如果需要,我可以使它成为通用的.
要处理与DMA控制器接口的缓冲区,有特定的功能.这些函数不仅处理地址转换,还处理与存储器的高速缓存一致性,例如缓存刷新(在发送之前将数据写入存储器)和高速缓存无效(在接收之前使高速缓存无效).
(1)要分配缓冲区,请同时获取虚拟地址和物理地址:
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
Run Code Online (Sandbox Code Playgroud)
函数的返回值是分配的缓冲区的虚拟地址,而dma_handle指针保存分配的缓冲区的物理地址.
(2)传递分配给设备的一个缓冲区:
dma_addr_t dma_map_single(struct device *dev, void *ptr,
size_t size,
enum dma_data_direction dir)
Run Code Online (Sandbox Code Playgroud)
返回值是缓冲区的物理地址,参数dir是DMA_TO_DEVICE,ptr是缓冲区的虚拟地址;
(3)从设备接收一个缓冲区:
void dma_unmap_single(struct device *dev, dma_addr_t addr,
size_t size,
enum dma_data_direction dir)
Run Code Online (Sandbox Code Playgroud)
参数dir是DMA_FROM_DEVICE.
注意:要使用与dma相关的三个函数,应该将设备注册到具有dma_map_ops的一个特定总线,否则不能使用这三个函数.
我需要强制Linux在那个确切的内存位置创建缓冲区文件
这是不可能的.(实际上你已经创建了一个XY问题.)
由于您的硬件"从预设的物理内存地址流式传输数据",因此您必须确保Linux内核不将此内存区域用作其内存池的一部分.您需要在引导时通知内核不使用此内存区域.一旦它成为内核控制的内存空间的一部分,您将无法"回收"或在此特定物理内存区域中分配缓冲区.
排除内存区域的最通用方法是使用memmap=内核命令行上的参数.
memmap=nn[KMG]$ss[KMG]
[KNL,ACPI] Mark specific memory as reserved.
Region of memory to be used, from ss to ss+nn.
Example: Exclude memory from 0x18690000-0x1869ffff
memmap=64K$0x18690000
or
memmap=0x10000$0x18690000
Run Code Online (Sandbox Code Playgroud)
某些体系结构(例如ARM及其ATAG)具有其他不太可见且更安全的方法来保留物理内存区域.
不知何故,您必须向设备驱动程序提供此内存区域的地址和大小.这可以通过解析命令行来获得,或者(使用拇指向下)使用#defines进行硬编码.
驱动程序应通过调用声明其对内存区域的使用request_mem_region().
驱动程序可以通过调用将此内存区域映射到虚拟地址空间ioreamp().
由于已经提供了驱动程序或者已经知道了物理地址,所以已经完成了.由于分配了物理内存,因此内存是连续的.您必须配置MMU以禁用此内存区域上的缓存.然后存储区域将是"可DMA化的".