SD Controller DMA Remapping 完整技术报告
目录
1. 问题背景
1.1 问题现象
当 Windows 系统启用 Kernel DMA Protection(DMA Remapping / IOMMU)时,Bayhub SD Controller 驱动出现以下问题:
| 错误类型 | 错误码 | 描述 |
|---|---|---|
| ADMA Error | 0x0200 | ADMA Error State: ST_FDS (Fetch Descriptor State) |
| ADMA Error | 0x0100 | ADMA Error State: ST_TFR (Transfer Data State) |
| 数据全零 | N/A | SDMA 模式下读取数据全为 0x00 |
结果: SD 卡无法识别(无盘符)或无法正常读写数据。
1.2 环境信息
1 | 设备: Bayhub SD Host Controller |
1.3 问题触发条件
- 系统 BIOS 启用 Intel VT-d / AMD-Vi
- Windows Kernel DMA Protection 功能开启
- SD Controller 被系统识别为 External 设备
- 驱动使用 ADMA2/ADMA3/SDMA 模式进行 DMA 传输
2. 原因分析
2.1 核心问题:Storport DMA API 对 IOMMU 支持的不确定性
问题本质:Storport 框架提供的 DMA 相关 API 没有明确文档说明是否支持 IOMMU 重映射 (DMA Remapping)。
| Storport API | 官方文档说明 | IOMMU/IOVA 支持 |
|---|---|---|
StorPortGetPhysicalAddress |
返回 physical address | 未明确说明 |
StorPortGetScatterGatherList |
返回 scatter/gather list | 未明确说明 |
StorPortGetUncachedExtension |
返回 uncached memory | 未明确说明 |
StorPortAllocateDmaMemory |
返回 DMA memory | 未明确说明 |
这是问题的核心:Microsoft 官方文档中,这些 API 都没有明确说明在 IOMMU 启用时是否返回 IOVA (I/O Virtual Address) 还是 PA (Physical Address)。
2.2 测试验证:Storport API 实际行为
通过 DMAR_DIAG 诊断代码测试 Storport API 的实际行为:
测试方法:
1 | // 比较同一内存使用不同参数调用 StorPortGetPhysicalAddress 的结果 |
测试结果 (DMAR_DIAG 日志):
1 | DMAR_DIAG: Address comparison test: |
分析:
| 内存类型 | PA(NULL) | PA(Srb) | 结论 |
|---|---|---|---|
| DataBuffer | 0x00000000 (失败) | 0x00200760 | DIFF → 可能经过 IOMMU |
| SrbExtension | 0x72EF04A0 | 0x72EF04A0 | SAME → 没有经过 IOMMU |
重要说明:
- DataBuffer 返回
PA(NULL)=0表示使用 NULL 参数时 API 调用失败 - SrbExtension 两次调用返回相同地址,说明 SrbExtension 可以被转换,但实测 PA == IOVA,没有经过 IOMMU 重映射
- 这与 Microsoft 文档描述不完全一致,说明 Storport API 行为存在不确定性
2.3 Microsoft 文档的局限性
根据 StorPortGetPhysicalAddress 文档:
“If Srb is not NULL, the VirtualAddress parameter must be an address within the range of memory described by the Srb’s DataBuffer or SenseInfoBuffer.”
文档问题:
- 文档说
VirtualAddress可以是DataBuffer、SenseInfoBuffer或SrbExtension - 但文档 没有明确说明 返回的地址是否经过 IOMMU 重映射
- 实测发现 SrbExtension 虽然可以转换,但 没有经过 IOMMU
2.4 StorAHCI 参考的局限性
StorAHCI 示例代码 (Windows-driver-samples/storahci) 存在以下限制:
| 限制 | 说明 |
|---|---|
| 设备类型 | AHCI 控制器是 Internal 设备 |
| IOMMU 域 | Internal 设备使用 Identity Domain,PA == IOVA |
| 不具参考性 | 对于 External 设备(如 SD Controller),无法验证 IOVA 支持 |
结论:StorAHCI 示例代码在 Internal 设备上可以工作,但 不能作为 External 设备 DMAR 兼容性的参考。
3. 系统框架与层级
3.1 软件架构
1 | ┌─────────────────────────────────────────────────────────────────┐ |
3.2 问题定位
1 | ┌─────────────────────────────────────────────────────────────────┐ |
3.3 Storport DMA API 汇总
| API | 用途 | IOVA 支持 | 备注 |
|---|---|---|---|
StorPortGetPhysicalAddress(Srb, DataBuffer) |
DataBuffer 地址 | 未确认 | 实测返回不同地址,但未确认是 IOVA |
StorPortGetScatterGatherList(Srb) |
DataBuffer SGL | 未确认 | 官方文档未说明 |
StorPortGetPhysicalAddress(Srb, SrbExtension) |
SrbExtension 地址 | 实测无 | PA(NULL) == PA(Srb) |
StorPortGetPhysicalAddress(NULL, any) |
任意地址 | 无 | 返回 PA |
StorPortGetUncachedExtension |
内部 DMA buffer | 无 | 返回 PA |
StorPortAllocateDmaMemory |
DMA 内存分配 | 无 | 实测返回 PA |
关键问题:所有 Storport DMA API 都 没有明确的 IOMMU/IOVA 支持文档。
4. 尝试的解决方案
4.1 方案 1: PIO 模式 ✅ 有效
实施内容:
1 | ; bhtsddr.inf |
原理: PIO 模式不使用 DMA,CPU 直接通过寄存器读写数据,完全绕过 IOMMU。
测试结果 (DMAR_DIAG):
1 | DMAR_DIAG: *** PIO MODE ENABLED (mode=0xF) *** |
结论: ✅ 功能正常,但性能较低 (5-15 MB/s vs ADMA 80-100 MB/s)
4.2 方案 2: SRB Extension 存储 ADMA Descriptor ❌ 无效
实施内容: 将 ADMA2/ADMA3 Descriptor Table 放入 SRB Extension。
测试结果 (DMAR_DIAG):
1 | SrbExtension: PA(NULL)=0x72EF04A0 PA(Srb)=0x72EF04A0 SAME |
结论: ❌ SrbExtension 可以被转换,但实测 PA == IOVA,没有经过 IOMMU 重映射。
4.3 方案 3: StorPortAllocateDmaMemory ❌ 无效
实施内容: 使用 StorPortAllocateDmaMemory API 分配 DMA 内存。
测试结果 (DMAR_DIAG):
1 | StorPortAllocateDmaMemory: VA=FFFFA20076005000 PhysicalAddress=0x73090000 |
结论: ❌ 此 API 返回的 PhysicalAddress 没有经过 IOMMU 重映射。
4.4 方案 4: SDMA 直接使用 DataBuffer ✅ 有效 (有条件)
原理: SDMA 没有 Descriptor Table,sys_addr 直接指向 Data Buffer。如果 DataBuffer 地址正确,SDMA 可以工作。
测试结果 (DMAR_DIAG):
1 | DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x0000000000200760 len=4096 |
结论: ✅ 当 srb_sg_len == 1 且满足 boundary 条件时有效。
4.5 方案 5: SDMA + PIO Fallback ✅ 有效
实施内容: SDMA 不兼容时自动使用 PIO。
测试结果:
1 | DMAR_DIAG: SDMA req sg_len=3 (need 1), using PIO fallback |
结论: ✅ 格式化等大块操作自动使用 PIO,小块读写使用 SDMA。
5. 最终方案: SDMA + PIO 混合模式
5.1 方案架构
1 | ┌─────────────────────────────────────────────────────────────────┐ |
5.2 INF 配置
1 | ; bhtsddr.inf - SDMA 模式 |
6. WDF vs StorPort 驱动框架对比
6.1 背景
Realtek SD Host Controller 使用 WDF (Windows Driver Framework) 的 MTD (Mass Transfer Device) 驱动,能够在 DMAR 启用环境下正常工作。
这引出一个重要问题:WDF DMA API 是否确定性支持 IOMMU/DMAR?
6.2 WDF DMA API 分析
WDF 提供了不同于 StorPort 的 DMA 抽象:
| WDF DMA API | 用途 | IOVA 支持 |
|---|---|---|
WdfDmaEnablerCreate |
创建 DMA 使能器 | 框架管理 |
WdfCommonBufferCreate |
分配公共缓冲区 | 框架管理 |
WdfDmaTransactionCreate |
创建 DMA 事务 | 框架管理 |
WdfDmaTransactionGetRequest |
获取请求信息 | 框架管理 |
关键区别:
1 | StorPort 驱动: |
6.3 WDF 框架的 IOVA 处理
根据 WDF DMA 文档 和 WDF Common Buffers:
- WdfDmaEnablerCreate 在设备初始化时获取 IOMMU 能力信息
- WdfCommonBufferCreate 分配的内存由 WDF 框架负责 IOMMU 映射
- WdfDmaTransactionExecute 执行 DMA 事务时,框架自动处理地址转换
注意:WDF 文档也 没有明确说明 IOVA 支持,但:
- WDF 框架是更高层的抽象
- 地址转换由框架而非驱动负责
- Realtek MTD 驱动在 DMAR 环境下工作证明了 WDF 的正确处理
6.4 问题归因
基于 WDF vs StorPort 的对比分析:
| 对比项 | StorPort (Bayhub) | WDF (Realtek) |
|---|---|---|
| DMA 地址获取 | 驱动调用 API 获取 | 框架管理 |
| IOMMU 处理 | 不确定 | 框架负责 |
| DMAR 兼容性 | ❌ 失败 | ✅ 成功 |
| 驱动复杂度 | 高(需处理地址) | 低(框架处理) |
结论:
- ADMA2 问题属于 Storport DMA API 本身的局限性
- 不是 Bayhub Miniport 驱动的实现问题
- WDF 框架提供了更可靠的 DMA 抽象
6.5 建议
| 建议 | 说明 |
|---|---|
| 联系 Microsoft Driver Team | 请求明确 Storport DMA API 的 IOMMU 支持 |
| 考虑 WDF 框架 | 如果需要完整 DMAR 兼容,考虑使用 WDF 重写驱动 |
| 当前方案 | SDMA + PIO 混合模式是 Storport 框架下的最佳工程方案 |
7. 技术结论
7.1 核心发现
| 发现 | 依据 |
|---|---|
| Storport DMA API 对 IOMMU 支持不确定 | Microsoft 文档未明确说明;实测 SrbExtension PA==IOVA |
| 所有 Storport DMA 分配 API 返回 PA | DMAR_DIAG 测试:StorPortAllocateDmaMemory 返回高地址 PA |
| SrbExtension 可转换但未经 IOMMU | DMAR_DIAG 测试:PA(NULL) == PA(Srb) |
| StorAHCI 参考有局限性 | AHCI 是 Internal 设备,使用 Identity Domain |
| WDF 框架在 DMAR 下工作 | Realtek MTD 驱动实证 |
| 问题在 Storport API 而非 Miniport | WDF vs StorPort 对比分析 |
7.2 需要 Microsoft 支持
| 问题 | 需要明确 |
|---|---|
StorPortGetScatterGatherList |
是否返回 IOVA?对 External 设备如何处理? |
StorPortAllocateDmaMemory |
是否支持 IOMMU 重映射?如何为 External 设备获取 IOVA? |
| Storport DMAR 支持 | 是否有计划为 Storport Miniport 提供可靠的 IOVA API? |
7.3 长期解决方案路线图
| 阶段 | 方案 | 说明 |
|---|---|---|
| 当前 | SDMA + PIO 混合模式 | ✅ 工程妥协方案 |
| 短期 | 联系 Microsoft | 明确 Storport API 的 IOMMU 支持 |
| 中期 | OEM SSDT Patch | 将设备标记为 Internal |
| 长期 | 考虑 WDF 框架 | 如需完整 DMAR 兼容 |
8. 参考资料
8.1 Microsoft 官方文档
- StorPortGetPhysicalAddress - 未明确 IOVA 支持
- StorPortGetScatterGatherList - 未明确 IOVA 支持
- StorPortAllocateDmaMemory - 未明确 IOVA 支持
- Enabling DMA Remapping for Device Drivers
- WDF Using DMA Enablers
- WDF Using Common Buffers
8.2 示例代码
- StorAHCI Sample Driver - Internal 设备,不太具备 External 设备参考性
8.3 本项目技术文档
| 文档 | 内容 |
|---|---|
20260306_DMAR_Final_Analysis_Report.md |
最终分析报告 |
20260306_SRB_Extension_Analysis_Result.md |
SRB Extension 方案分析 |
20260306_DMAR_API_Analysis_Report.md |
Storport API 分析 |
20260305_SDMA_DMAR_Compatibility_Analysis.md |
SDMA DMAR 兼容性分析 |
20260305_PIO_Mode_DMAR_Solution.md |
PIO 模式解决方案 |
附录: DMAR_DIAG 关键日志
A.1 SrbExtension 地址测试
1 | BHT-ERROR : DMAR_DIAG: Address comparison test: |
解读: SrbExtension 可以被转换,但 PA == IOVA,没有经过 IOMMU 重映射。
A.2 StorPortAllocateDmaMemory 测试
1 | BHT-ERROR : BhtAllocateDmaBuffer: VA=FFFFA20076005000 IOVA=0x0000000073090000 |
解读: StorPortAllocateDmaMemory 返回的地址无法被硬件访问,说明不是 IOVA。
A.3 SDMA 直接路径成功
1 | BHT-ERROR : DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x0000000000200760 len=4096 |
解读: 使用 DataBuffer 地址的 SDMA 传输成功。
文档生成日期: 2026-03-06
最后更新: 2026-03-06
版本: 2.0 (修订版)