Linux kernel 获取Linux下缓冲区的物理地址

Linux kernel 获取Linux下缓冲区的物理地址,linux-kernel,embedded,embedded-linux,mmap,microblaze,Linux Kernel,Embedded,Embedded Linux,Mmap,Microblaze,我在Xilinx的Microblaze上使用完整的MMU运行Linux内核3.3。我正在做的任务要求我知道以下几点:我需要创建一个文本文件(缓冲区)并找到这个缓冲区的物理地址,我不希望内核将这个文件写入不连续的内存区域 我之所以需要它,是因为我有一个DMA引擎,它可以从预设的物理内存地址流式传输数据,所以我需要强制Linux在那个确切的内存位置创建缓冲文件,这样当我将数据写入这个文件时,DMA引擎会立即将数据传输到另一个硬件核心 更多详情: 我的系统有一个512 MB DDR3 RAM,通过“X

我在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而不是我们遇到的这个问题时,我在一个外部存储设备上有文件,我可以从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)
函数的返回值是分配的缓冲区的虚拟地址,而dma_句柄指针保存分配的缓冲区的物理地址

(2) 要传递分配给设备的一个缓冲区,请执行以下操作:

dma_addr_t dma_map_single(struct device *dev, void *ptr,
                          size_t size,
                          enum dma_data_direction dir)
返回值为缓冲区的物理地址,参数dir为DMA_到_设备,ptr为缓冲区的虚拟地址

(3) 要从设备接收一个缓冲区,请执行以下操作:

void dma_unmap_single(struct device *dev, dma_addr_t addr,
                            size_t size,
                            enum dma_data_direction dir)
参数dir是来自设备的DMA_

注: 要使用与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
一些体系结构,如ARM及其ATAG,具有其他不太明显且更安全的保留物理内存区域的方法

然后,您必须向设备驱动程序提供该内存区域的地址和大小。这可以通过解析命令行或(拇指朝下)使用
#define
s硬编码来实现

驱动程序应该通过调用
request\u mem\u region()

驱动程序可以通过调用
ioreamp()
将此内存区域映射到虚拟地址空间


由于提供了驱动程序,或者驱动程序已经知道物理地址,所以就完成了。由于分配了物理内存,因此内存是连续的。您必须将MMU配置为禁用此内存区域上的缓存。然后,内存区域将是“DMAable”。

请看这一章的PDF,该章解释Linux内核如何管理虚拟内存和物理内存。它给出了如何为各种场景映射和固定物理内存的示例@bromanous该设备是您自己开发的还是您无法修改的第三方IP核心?如果您可以修改,那么我强烈建议您对DMA地址软件进行配置,这样您就可以使用田玉芬所说的标准DMA功能。@BenjaminLeinweber,是的,我有DMA核心的源代码,我可以配置DMA传输的地址,并将其作为一个通用参数。这并没有回答OP的问题。OP希望在内存中的固定位置创建dma缓冲区。这些API中没有一个能够做到这一点。对于问题的直接答案为+1。另一种类似的方法是让设备使用内存的顶部,并告诉Linux内存比使用mem=boot参数时实际内存少。例如,如果您有64 MB的内存,您可以通过mem=63M为您的设备从0x3F00000开始保留一个meg。@sawdust我不知道如何“您必须配置MMU以禁用此内存区域上的缓存”,我查看了MicroBlaze可调参数,但没有该选项。设置memmap还不够吗?@bromanous-我把它作为完整性的补充。你可能不需要做任何明确的事情。希望
memmap=
指定