【问题标题】:get the physical address of a buffer under LinuxLinux下获取缓冲区的物理地址
【发布时间】:2013-06-09 03:02:13
【问题描述】:

我在具有完整 MMU 的 Xilinx 的 Microblaze 上运行 Linux 内核 3.3。我正在做的任务需要我知道以下内容:我需要创建一个文本文件(缓冲区)并定位此缓冲区的物理地址,并且我不希望内核将此文件写入不连续的内存区域。

我需要这个的原因是因为我有一个 DMA 引擎,可以从预设的物理内存地址流式传输数据,所以我需要强制 Linux 在那个确切的内存位置创建缓冲区文件,这样当我将数据写入这个文件时它立即由 DMA 引擎传输到另一个硬件内核

更多细节:

我的系统有一个 512 MB DDR3 RAM 通过“Xilinx 的多端口内存控制器 (MPMC) 连接到系统。这个内存控制器的基地址是 0x90000000,系统中的所有单元都通过这个控制器访问内存,包括 MicroBlaze , 我拥有的 DMA 单元使用了一个特殊的接口,称为 Native Personality Interface (NPI),以非常低的级别与内存进行通信,从而产生非常高的速度性能。

这个 NPI DMA 单元最初设计用于一个名为“xilkernel”的非常基本的嵌入式内核,它不支持虚拟内存,MMU 也不是 MicroBlaze 的一部分,因此程序员可以看到操作系统代码将驻留在哪里并选择一个物理内存地址(例如 0x91800000)作为 DMA 将从其流式传输的源地址,然后程序员可以将文件放置在该确切地址并运行系统

当我们需要迁移项目以使用 Linux 而不是 xilkernel 时,我们遇到了这个问题,我在外部存储设备上有文件,我可以从 Linux 作为块设备访问这些文件,我需要将每个文件移动到主内存( DDR3 RAM)并使 DMA 流式传输文件。 目前 DMA 从固定地址流式传输,但如果需要,我可以将其设为通用。

【问题讨论】:

  • 看一看这个 PDF 的章节,它解释了 Linux 内核如何管理虚拟和物理内存。它提供了如何为各种场景映射和固定物理内存的示例。 lwn.net/images/pdf/LDD3/ch15.pdf
  • @bromanous 这个设备是您自己开发的,还是您无法修改的第 3 方 IP 核?如果可以修改的话,我强烈建议你把DMA地址软件设置成可配置的,这样你就可以使用tian_yufen所说的标准DMA功能了。
  • @BenjaminLeinweber,是的,我有 DMA 核心的源代码,我可以配置 DMA 流的地址并将其设为通用参数。

标签: linux-kernel embedded embedded-linux mmap microblaze


【解决方案1】:

我需要强制 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

某些架构,例如带有 ATAG 的 ARM,还有其他不太明显但更安全的方法来保留物理内存区域。

然后您必须以某种方式向设备驱动程序提供此内存区域的地址和大小。这可以通过解析命令行来获得,或者(大拇指向下)使用#defines 进行硬编码。

驱动程序应该通过调用request_mem_region()来声明它对内存区域的使用。
驱动程序可以通过调用ioreamp()将此内存区域映射到虚拟地址空间。

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

【讨论】:

  • +1 直接回答问题。另一种类似的方法是让设备使用内存顶部并告诉 Linux 内存比使用 mem= 引导参数的实际内存少。例如,如果您有 64 MB 内存,则可以传递 mem=63M 为您的设备保留一个从 0x3F00000 开始的 meg。
  • @sawdust 我不明白如何“您必须配置 MMU 以禁用此内存区域上的缓存”,我查看了 MicroBlaze 可调参数,但该选项不存在。设置memmap还不够吗?
  • @bromanous - 为了完整性,我将其包括在内。您可能不必做任何明确的事情。希望memmap= 规范和对您的arch/board/proc 的ioremap() 的调用将为该内存区域设置正确的属性。
  • @sawdust 非常感谢,我将尝试此解决方案并返回此操作并更新它
【解决方案2】:

要处理与 DMA 控制器接口的缓冲区,有一些特定的函数。这些函数不仅负责地址转换,还负责缓存与内存的一致性,例如缓存刷新(在发送之前将数据写入内存)和缓存无效(在接收之前使缓存无效)。

(1) 分配缓冲区,获取虚拟地址和物理地址:

void *dma_alloc_coherent(struct device *dev, size_t size,
                         dma_addr_t *dma_handle, gfp_t flag)

函数的返回值是分配的缓冲区的虚拟地址,而dma_handle指针保存的是分配的缓冲区的物理地址。

(2) 传递一个分配给设备的缓冲区:

dma_addr_t dma_map_single(struct device *dev, void *ptr,
                          size_t size,
                          enum dma_data_direction dir)

返回值为缓冲区的物理地址,参数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)

参数dir是DMA_FROM_DEVICE。

注意: 要使用与dma相关的三个功能,必须将设备注册到具有dma_map_ops的特定总线上,否则这三个功能不能使用。

【讨论】:

  • 这不能回答 OP 的问题。 OP 希望在内存中的固定位置创建一个 dma 缓冲区。这些 API 都无法做到这一点。
猜你喜欢
  • 1970-01-01
  • 2014-11-11
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 2021-07-02
  • 2017-04-26
  • 2021-06-13
  • 2011-09-09
相关资源
最近更新 更多