ax-dma
路径:
os/arceos/modules/axdma类型:库 crate 分层:ArceOS 层 / DMA 内存服务层 版本:0.3.0-preview.3文档依据:Cargo.toml、src/lib.rs、src/dma.rs、os/arceos/api/ax-api/src/imp/mem.rs、drivers/tpu/sg2002-tpu/src/ion/heap.rs、platforms/axplat-dyn/src/drivers/mod.rs、os/axvisor/src/driver/blk/mod.rs
ax-dma 不是驱动聚合层,也不是某类设备驱动。它的真实职责是为 ArceOS 内核提供一套全局一致的 DMA 一致性内存分配服务:从页分配器拿到内存、把页表属性改成 UNCACHED、给设备返回可用的总线地址,并在释放时尽可能恢复映射属性。它位于 ax-alloc / ax-mm / ax-hal 等内存基础设施之上,位于需要软件管理 DMA 缓冲的驱动之下。
架构设计
设计定位
ax-dma 解决的是这样一个问题:
- CPU 侧需要拿到一块可访问的虚拟地址。
- 设备侧需要拿到与之对应的总线地址。
- 这块内存还需要满足 DMA 使用场景下的一致性或缓存属性要求。
因此它承担的是 DMA 内存服务,而不是设备驱动逻辑。它不会告诉系统“有哪些设备”,也不会教某个网卡如何编程发送描述符。
1.2 核心公开对象
| 符号 | 作用 |
|---|---|
BusAddr | 设备侧看到的总线地址包装类型 |
DMAInfo | 同时携带 CPU 虚拟地址与总线地址 |
phys_to_bus() | 把物理地址转换为设备侧总线地址 |
alloc_coherent() | 分配 DMA 一致性内存 |
dealloc_coherent() | 释放 DMA 一致性内存 |
1.3 内部对象与主线
内部实现集中在 src/dma.rs:
ALLOCATOR: SpinNoIrq<DmaAllocator>:全局 DMA 分配器。DmaAllocator { alloc: DefaultByteAllocator }:既支持页级分配,也支持小块字节分配。update_flags():通过ax-mm::kernel_aspace().protect()修改页表属性。
alloc_coherent(layout) 的主线有两条:
- 若
layout.size() >= 4 KiB,走alloc_coherent_pages():- 直接从全局页分配器申请页;
- 把页属性改成
READ | WRITE | UNCACHED; - 通过
virt_to_bus()返回总线地址。
- 若小于 4 KiB,走
alloc_coherent_bytes():- 先尝试从内部字节分配器分配;
- 若空间不够,则从全局页分配器按 4 页扩容一段 DMA 内存;
- 再把这段内存加入字节分配器。
释放时:
- 大块分配会归还页,并尽量把页属性恢复到
READ | WRITE。 - 小块分配则归还给内部字节分配器。
1.4 hv feature 的行为差异
开启 hv 时,内部字节分配器会改用 buddy-slab-allocator 提供的接口,并且小块分配路径不再尝试 add_memory 动态扩容。这意味着:
- 非
hv模式更像“按需从页分配器借内存扩展字节池”。 hv模式更像“在已有 slab 空间里分配,失败就直接报错”。
1.5 地址模型
phys_to_bus() 的实现非常直接:
- 先得到物理地址;
- 当前动态平台路径下按物理地址与总线地址一致处理;
- 最终把该地址包装成
BusAddr。
这说明当前 ax-dma 假设平台满足一种简单的直通总线地址模型,而不是依赖 IOMMU 做复杂映射。需要偏移或 map/unmap 协议的平台应在平台 glue 或驱动能力边界中显式表达。
1.6 与驱动栈的真实接线关系
当前仓库里,ax-dma 的明确直接使用者包括:
os/arceos/api/ax-api/src/imp/mem.rs:通过ax_alloc_coherent()/ax_dealloc_coherent()暴露系统级 DMA 一致性内存 API;drivers/tpu/sg2002-tpu/src/ion/heap.rs:通过ax_dma::alloc_coherent_pages()为 ION 堆分配 DMA buffer。
但要特别注意一个实现事实:
ax-driver/Cargo.toml中的 FXMAC glue 通过dma-api/axklib::dma::op()获取一致性 DMA 内存;- 它并没有直接调用
ax-dma的 coherent allocator。
这说明在当前代码树里,ax-dma 不是所有 DMA 驱动的唯一后端,而是其中一条已被明确使用的 DMA 服务路径。
1.7 与其它 DMA 实现的边界
仓库里至少还有两条独立 DMA 路径:
platforms/axplat-dyn/src/drivers/mod.rs:提供dma_api::DmaOp实现,服务动态平台块设备探测。os/axvisor/src/driver/blk/mod.rs:提供 Axvisor 自己的rdif_block::dma_api::DmaOp实现。
因此不能把 ax-dma 写成“整个仓库唯一的 DMA 抽象层”;它是 ArceOS 主线中的一个内核 DMA 内存服务模块。
1.8 边界澄清
最关键的边界是:ax-dma 负责分配一致性 DMA 内存并给出总线地址,但不负责设备探测、描述符编程、总线枚举或 IOMMU 策略。
核心功能
功能概览
- 分配和释放 DMA 一致性内存。
- 把 CPU 虚拟地址和总线地址绑定成
DMAInfo。 - 通过页表属性修改保证这段内存按 DMA 需要工作。
- 为 ArceOS API 层和部分驱动提供统一的 DMA 内存入口。
2.2 对上层 API 的关系
os/arceos/api/ax-api/src/imp/mem.rs 会在 cfg_dma! 下直接重导出:
ax_alloc_coherent()ax_dealloc_coherent()DMAInfo
这说明 ax-dma 不只是驱动内部工具,也被设计成可向更高层 API 暴露的系统能力。
2.3 与 ax-runtime / ax-feat 的关系
需要区分两个概念:
ax-runtime的dmafeature 只是["paging"],它并不直接依赖ax-dmacrate。ax-feat的dmafeature 表示整机要具备 DMA 所需的内存和分页能力。
也就是说,ax-dma 更像一个“可被 API 或驱动选用的具体实现模块”,而不是 runtime 自动初始化出来的独立子系统。
依赖关系
直接依赖
| 依赖 | 作用 |
|---|---|
ax-alloc | 全局页分配器与 DMA 用页申请 |
axallocator / buddy-slab-allocator | 字节级分配器实现 |
ax-hal | 提供物理地址与页表标志相关能力 |
ax-mm | 修改内核地址空间页表属性 |
ax-kspin | 保护全局分配器 |
memory_addr | 地址与页大小辅助 |
log | 错误与调试日志 |