SD Controller DMA Remapping 完整技术报告

SD Controller DMA Remapping 完整技术报告


目录

  1. 问题背景
  2. 原因分析
  3. 系统框架与层级
  4. 尝试的解决方案
  5. 最终方案: SDMA + PIO 混合模式
  6. WDF vs StorPort 驱动框架对比
  7. 技术结论
  8. 参考资料

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
2
3
4
5
设备: Bayhub SD Host Controller
PCI ID: 1217:8621
设备类型: External (SD Card 槽可被用户物理访问)
DmaRemappingPolicy: 2 (Driver capable of using DMA remapping)
系统: Windows 10/11 启用 Kernel DMA Protection

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
2
3
4
5
6
7
8
9
// 比较同一内存使用不同参数调用 StorPortGetPhysicalAddress 的结果
PA_with_NULL = StorPortGetPhysicalAddress(pdx, NULL, buffer, &len);
PA_with_Srb = StorPortGetPhysicalAddress(pdx, Srb, buffer, &len);

if (PA_with_NULL != PA_with_Srb) {
// 地址不同 → 有 IOMMU 重映射
} else {
// 地址相同 → 没有经过 IOMMU
}

测试结果 (DMAR_DIAG 日志):

1
2
3
DMAR_DIAG: Address comparison test:
DataBuffer: PA(NULL)=0x0000000000000000 PA(Srb)=0x0000000000200760 DIFF
SrbExtension: PA(NULL)=0x0000000072EF04A0 PA(Srb)=0x0000000072EF04A0 SAME

分析

内存类型 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.”

文档问题

  1. 文档说 VirtualAddress 可以是 DataBufferSenseInfoBufferSrbExtension
  2. 但文档 没有明确说明 返回的地址是否经过 IOMMU 重映射
  3. 实测发现 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────────┐
│ User Applications │
├─────────────────────────────────────────────────────────────────┤
│ Windows File System │
├─────────────────────────────────────────────────────────────────┤
│ Windows Storage Stack │
├─────────────────────────────────────────────────────────────────┤
│ Storport Driver │
│ - 管理 SRB (SCSI Request Block) │
│ - 提供 DMA API(IOVA 支持不确定) │
│ - 不明确是否为 External 设备正确处理 IOMMU │
├─────────────────────────────────────────────────────────────────┤
│ Bayhub SD Miniport Driver │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 依赖 Storport API 获取 DMA 地址 │ │
│ │ 无法独立解决 IOMMU 映射问题 │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ SD Host Controller Hardware │
├─────────────────────────────────────────────────────────────────┤
│ IOMMU (VT-d / AMD-Vi) │
│ - External 设备使用 Isolated Domain │
│ - 需要 IOVA 而非 PA 进行 DMA │
├─────────────────────────────────────────────────────────────────┤
│ Physical Memory │
└─────────────────────────────────────────────────────────────────┘

3.2 问题定位

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────────────┐
│ 问题定位 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✗ 不是 Bayhub Miniport 驱动 Bug │
│ ✗ 不是 SDHCI 硬件架构问题 │
│ ✗ 不是 Windows Kernel DMA Protection 设计与 ADMA 的冲突 │
│ │
│ ✓ 是 Storport DMA API 对 IOMMU 支持的不确定性 │
│ ✓ 是 Storport 框架没有为 Miniport 提供可靠的 IOVA 获取方式 │
│ ✓ 需要 Microsoft Driver Team 支持以明确 API 行为 │
│ │
└─────────────────────────────────────────────────────────────────┘

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
2
; bhtsddr.inf
HKR,"Parameters\GG8", "test_dma_mode_setting",0x00010001, 0x8000000F

原理: PIO 模式不使用 DMA,CPU 直接通过寄存器读写数据,完全绕过 IOMMU。

测试结果 (DMAR_DIAG):

1
2
DMAR_DIAG: *** PIO MODE ENABLED (mode=0xF) ***
DMAR_DIAG: PIO READ success, Data[0-15]: FF FF FF FF FF FF FF FF...

结论: ✅ 功能正常,但性能较低 (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
2
SrbExtension: PA(NULL)=0x72EF04A0 PA(Srb)=0x72EF04A0 SAME
ADMA Error! Status=0x01 (ST_FDS)

结论: ❌ SrbExtension 可以被转换,但实测 PA == IOVA,没有经过 IOMMU 重映射。


4.3 方案 3: StorPortAllocateDmaMemory ❌ 无效

实施内容: 使用 StorPortAllocateDmaMemory API 分配 DMA 内存。

测试结果 (DMAR_DIAG):

1
2
StorPortAllocateDmaMemory: VA=FFFFA20076005000 PhysicalAddress=0x73090000
ADMA Error! Status=0x01

结论: ❌ 此 API 返回的 PhysicalAddress 没有经过 IOMMU 重映射


4.4 方案 4: SDMA 直接使用 DataBuffer ✅ 有效 (有条件)

原理: SDMA 没有 Descriptor Table,sys_addr 直接指向 Data Buffer。如果 DataBuffer 地址正确,SDMA 可以工作。

测试结果 (DMAR_DIAG):

1
2
DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x0000000000200760 len=4096
DMAR_DIAG: Transfer completed successfully!

结论: ✅ 当 srb_sg_len == 1 且满足 boundary 条件时有效。


4.5 方案 5: SDMA + PIO Fallback ✅ 有效

实施内容: SDMA 不兼容时自动使用 PIO。

测试结果:

1
2
3
DMAR_DIAG: SDMA req sg_len=3 (need 1), using PIO fallback
DMAR_DIAG: PIO operation completed successfully
格式化完成!

结论: ✅ 格式化等大块操作自动使用 PIO,小块读写使用 SDMA。


5. 最终方案: SDMA + PIO 混合模式

5.1 方案架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────────┐
│ 请求入口 (req_tag_io_add) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ sg_len == 1 && (boundary 对齐 || transfer_size <= boundary) │ │
│ └─────────────────────┬────────────────────┬───────────────┘ │
│ │ │ │
│ ✅ 条件满足 ❌ 条件不满足 │
│ │ │ │
│ v v │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SDMA 直接路径 │ │ PIO Fallback │ │
│ │ 使用 DataBuffer │ │ CPU 寄存器传输 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

5.2 INF 配置

1
2
; bhtsddr.inf - SDMA 模式
HKR,"Parameters\GG8", "test_dma_mode_setting",0x00010001, 0x80000000

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
2
3
4
5
6
7
8
9
StorPort 驱动:
Miniport → StorPort API → (IOVA 支持不确定) → IOMMU

驱动依赖不确定的 API

WDF 驱动:
WDF Driver → WDF DMA Framework → HAL/IOMMU Manager → IOMMU

框架负责地址转换

6.3 WDF 框架的 IOVA 处理

根据 WDF DMA 文档WDF Common Buffers

  1. WdfDmaEnablerCreate 在设备初始化时获取 IOMMU 能力信息
  2. WdfCommonBufferCreate 分配的内存由 WDF 框架负责 IOMMU 映射
  3. 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 官方文档

  1. StorPortGetPhysicalAddress - 未明确 IOVA 支持
  2. StorPortGetScatterGatherList - 未明确 IOVA 支持
  3. StorPortAllocateDmaMemory - 未明确 IOVA 支持
  4. Enabling DMA Remapping for Device Drivers
  5. WDF Using DMA Enablers
  6. WDF Using Common Buffers

8.2 示例代码

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
2
3
BHT-ERROR  : DMAR_DIAG: Address comparison test:
BHT-ERROR : DataBuffer: PA(NULL)=0x0000000000000000 PA(Srb)=0x0000000000200760 DIFF
BHT-ERROR : SrbExtension: PA(NULL)=0x0000000072EF04A0 PA(Srb)=0x0000000072EF04A0 SAME

解读: SrbExtension 可以被转换,但 PA == IOVA,没有经过 IOMMU 重映射。

A.2 StorPortAllocateDmaMemory 测试

1
2
3
BHT-ERROR  : BhtAllocateDmaBuffer: VA=FFFFA20076005000 IOVA=0x0000000073090000
BHT-ERROR : ADMA2 using DMA pool desc_tbl IOVA=0x0000000073090000
BHT-ERROR : DMA Error! Status=0x01 Addr=0x0000000073090000 State=ST_FDS

解读: StorPortAllocateDmaMemory 返回的地址无法被硬件访问,说明不是 IOVA。

A.3 SDMA 直接路径成功

1
2
BHT-ERROR  : DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x0000000000200760 len=4096
BHT-ERROR : Transfer completed successfully!

解读: 使用 DataBuffer 地址的 SDMA 传输成功。


文档生成日期: 2026-03-06
最后更新: 2026-03-06
版本: 2.0 (修订版)