4. DMA映射概念:物理地址与总线地址、IOMMU/SMMU的作用、映射表与描述符

在QNX BSP移植中,DMA(直接内存访问)映射是连接外设与系统内存的核心桥梁。理解物理地址、总线地址以及IOMMU/SMMU的作用,是定位DMA相关故障的前提。本节将系统阐述这些概念及其在BSP中的实现机制。

4.1 物理地址与总线地址的差异

在嵌入式系统中,物理地址(Physical Address, PA)是CPU和内存控制器看到的地址空间,而总线地址(Bus Address, BA)是外设(如网卡、存储控制器)在DMA操作中使用的地址。两者在无IOMMU的系统中可能相同,但在现代SoC中往往不同。

地址类型 定义 使用者 典型范围
物理地址(PA) CPU通过内存总线访问的实际硬件地址 CPU、MMU、Cache 0x80000000 – 0xFFFFFFFF(32位)
总线地址(BA) 外设通过DMA引擎访问的地址 DMA控制器、外设设备 0x40000000 – 0x7FFFFFFF(可能不同)
虚拟地址(VA) 进程看到的地址,需MMU转换 应用程序、内核线程 0x00010000 – 0xBFFFFFFF
关键提示:在QNX BSP中,mmap_device_memory()返回的是物理地址映射后的虚拟地址,而DMA描述符中必须填写总线地址。混淆这两者将导致DMA传输目标错误或数据损坏。
物理地址与总线地址关系图 CPU / MMU 虚拟地址 → 物理地址 系统内存 物理地址空间 IOMMU / SMMU 地址转换与保护 DMA外设 总线地址空间 DMA描述符 含总线地址 外设DMA描述符中必须填写总线地址(BA),而非物理地址(PA)或虚拟地址(VA)

4.2 IOMMU/SMMU的作用

IOMMU(I/O Memory Management Unit)或SMMU(System Memory Management Unit)是连接外设与系统内存的硬件单元,其核心功能包括:

  • 地址转换:将外设发出的总线地址转换为物理地址,使外设可以访问非连续的物理内存。
  • 访问权限控制:限制外设只能访问被授权的内存区域,防止恶意或错误的DMA操作破坏系统。
  • 隔离与虚拟化:在虚拟化环境中,为不同虚拟机的外设提供独立的内存映射。
特性 IOMMU(x86/PCIe) SMMU(ARM)
架构标准 Intel VT-d / AMD-Vi ARM SMMUv2/v3
转换粒度 4KB页 / 2MB大页 4KB页 / 2MB / 1GB块
中断重映射 支持(Interrupt Remapping) 支持(MSI转换)
QNX支持 io-pgtbl 驱动 arm-smmu 驱动
BSP移植要点:在QNX中,IOMMU/SMMU的初始化通常在启动代码(startup)中完成,包括:
  1. 解析设备树(DTB)中的IOMMU节点。
  2. 配置页表基地址和转换规则。
  3. 注册IOMMU回调到DMA框架。
IOMMU/SMMU地址转换流程 外设DMA请求 总线地址 BA IOMMU / SMMU 页表查找 权限检查 物理地址 PA 系统内存 映射表(页表) BA → PA 转换规则

4.3 映射表与描述符

映射表(Mapping Table)是IOMMU/SMMU内部维护的页表结构,定义了总线地址到物理地址的转换规则。DMA描述符(Descriptor)是外设与DMA控制器之间传递传输信息的控制结构。

4.3.1 映射表结构

在QNX BSP中,映射表通常以多级页表形式存在。以ARM SMMUv2为例,典型的二级页表结构如下:

/* 一级页表(L1)条目:指向二级页表 */
struct smmu_l1_entry {
    uint32_t    base_addr : 20;  // 二级页表基地址(高位)
    uint32_t    reserved  : 10;
    uint32_t    valid     : 1;   // 有效位
    uint32_t    type      : 1;   // 0=页表, 1=块
};

/* 二级页表(L2)条目:指向物理页 */
struct smmu_l2_entry {
    uint32_t    pfn       : 20;  // 物理页帧号
    uint32_t    attr      : 10;  // 内存属性(缓存、权限等)
    uint32_t    valid     : 1;
    uint32_t    type      : 1;
};
常见故障:映射表未正确初始化或页表地址对齐错误,会导致IOMMU缺页异常(Translation Fault)。在QNX中,此类故障通常表现为SYSPAGE_IOMMU_FAULT中断,需检查smmu_fault_handler的日志输出。

4.3.2 DMA描述符格式

DMA描述符的格式因外设而异,但通常包含以下关键字段:

字段 偏移(字节) 描述
源地址 0x00 DMA读取数据的起始总线地址
目的地址 0x04 DMA写入数据的目标总线地址
传输长度 0x08 本次传输的字节数(通常为4的倍数)
控制标志 0x0C 中断使能、链式传输、方向等
下一个描述符指针 0x10 链式DMA中下一个描述符的总线地址
DMA描述符与映射表协作关系 描述符 1 (BA=0x1000) 描述符 2 (BA=0x2000) 描述符 3 (BA=0x3000) IOMMU 映射表 BA 0x1000 → PA 0xA000 BA 0x2000 → PA 0xB000 BA 0x3000 → PA 0xC000 ... PA 0xA000 PA 0xB000

4.4 QNX中的DMA映射API

QNX提供了一套标准的DMA映射接口,BSP开发者需在io-pkt或驱动中正确调用:

/* 分配DMA缓冲区并获取总线地址 */
dma_addr_t dma_alloc_coherent(size_t size, uint32_t flags);

/* 将虚拟地址映射为DMA总线地址 */
dma_addr_t dma_map_single(void *cpu_addr, size_t size, int direction);

/* 同步DMA缓冲区(Cache一致性维护) */
void dma_sync_single(dma_addr_t bus_addr, size_t size, int direction);

/* 释放DMA映射 */
void dma_unmap_single(dma_addr_t bus_addr, size_t size);
调试建议:当DMA传输数据异常时,首先检查:
  • 描述符中的总线地址是否在IOMMU映射范围内。
  • 映射表条目是否标记为valid且权限正确。
  • Cache一致性操作是否在正确时机调用(dma_sync_single)。

4.5 故障定位要点总结

故障现象 可能原因 检查方法
DMA传输无响应 描述符中总线地址错误或IOMMU缺页 查看smmu_fault_status寄存器
数据内容错乱 Cache未同步或映射表属性错误 检查dma_sync_single调用顺序
系统崩溃/异常 IOMMU权限不足或描述符越界 启用QNX内核的dma_debug选项
性能低下 映射表粒度过小或TLB未命中 使用2MB大页映射连续内存
核心原则:DMA映射的本质是建立外设与内存之间的信任通道。物理地址是CPU的视角,总线地址是外设的视角,IOMMU是翻译官,描述符是合同。BSP移植者的任务就是确保这份“合同”准确无误。