DMA Remapping调试过程与解决方案 日期 : 2026-03-21项目 : Bayhub SD Host Controller Storport 驱动主题 : DMA Remapping 环境下的调试过程与最终解决方案
概述 本文档整合了 DMA Remapping 调试过程中的所有中间分析,包括 PIO 模式方案、SRB Extension 方案测试、以及 SDMA DMAR 兼容性分析。这些都是《SD Controller DMA Remapping 完整技术报告》的中间调试过程。
目录
问题背景
调试过程:PIO模式DMA Remapping解决方案
调试过程:SRB Extension ADMA2方案分析
调试过程:DMAR与ADMA2架构深度分析
调试过程:SDMA DMAR兼容方案实现
最终解决方案:SDMA + PIO 混合模式
技术结论
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. 调试过程:PIO模式DMA Remapping解决方案 2.1 问题分析 在 DMA Remapping (IOMMU) 环境下:
StorPortGetPhysicalAddress(NULL, va) 返回 物理地址 (PA)
DMA 设备需要 I/O 虚拟地址 (IOVA)
PA ≠ IOVA → DMA 访问错误的物理位置 → 数据传输失败
2.2 PIO模式原理
特性
DMA 模式
PIO 模式
数据传输方式
硬件 DMA 直接访问内存
CPU 通过寄存器读写数据
需要 IOVA?
✅ 是
❌ 否
受 IOMMU 影响?
✅ 是
❌ 否
性能
高 (硬件并行)
较低 (CPU 占用)
1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────────────────────────────────────┐ │ PIO 模式数据流: │ │ │ │ CPU ←──寄存器读写──→ SD Host Controller ←──SD总线──→ SD Card │ │ │ │ - 不涉及 DMA 操作 │ │ - 不需要物理地址或 IOVA │ │ - 绕过 IOMMU 的地址转换 │ └─────────────────────────────────────────────────────────────────┘
2.3 实现细节 INF 配置 1 2 3 4 HKR,"Parameters\GG8", "test_dma_mode_setting",0x00010001, 0x8000000F
代码修改 1: cfgmng.c - 缓冲区映射配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bool cfg_dma_need_sdma_like_buffer (u32 dma_mode) { if ((CFG_TRANS_MODE_ADMA2_SDMA_LIKE == dma_mode) || (CFG_TRANS_MODE_ADMA3_SDMA_LIKE == dma_mode) || (CFG_TRANS_MODE_SDMA == dma_mode) || (CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE == dma_mode) || (CFG_TRANS_MODE_PIO == dma_mode)) return TRUE; else return FALSE; }
代码修改 2: host.c - 传输模式初始化 1 2 3 4 5 6 case CFG_TRANS_MODE_PIO: host_dma_select(host, TRANS_PIO); DbgErr("DMAR_DIAG: *** PIO MODE ENABLED (mode=0xF) ***\n" ); DbgErr("DMAR_DIAG: No DMA transfer - CPU will transfer data directly\n" ); break ;
代码修改 3: reqmng.c - 请求路由 1 2 3 4 5 6 7 8 if (pdx->cfg->host_item.test_dma_mode_setting.dma_mode == 0xF ){ srb_ext->req.gen_req_t .code = GEN_IO_CODE_PIORW; srb_ext->req.gen_req_t .arg1 = sec_addr; srb_ext->req.gen_req_t .arg2 = sec_cnt; result = req_gen_io_add(pdx, srb_ext); }
2.4 测试结果 1 2 3 4 5 BHT-ERROR : DMAR_DIAG: *** PIO MODE ENABLED (mode=0xF) *** BHT-ERROR : DMAR_DIAG: No DMA transfer - CPU will transfer data directly BHT-ERROR : DMAR_DIAG: PIO mode request: READ sec_addr=0x00000000 sec_cnt=1 srb_buff=FFFFA2006451BB80 BHT-ERROR : DMAR_DIAG: PIO READ sec_addr=0x00000000 sec_cnt=1 data_buf=FFFFA2006451BB80 BHT-ERROR : DMAR_DIAG: PIO READ success, Data[0-15]: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
2.5 PIO模式特点 优点 :
优点
说明
✅ DMA Remapping 兼容
不使用 DMA,无 IOVA 问题
✅ 无需复杂代码修改
仅需配置更改和少量代码修改
✅ 立即可用
无需等待 OEM BIOS patch
✅ 系统兼容性好
适用于所有 Windows 版本
缺点 :
缺点
说明
⚠️ 性能较低
CPU 需要参与每次数据传输
⚠️ CPU 占用高
大文件传输时 CPU 使用率增加
⚠️ 吞吐量有限
无法充分利用 SD UHS-I/UHS-II 速度
3. 调试过程:SRB Extension ADMA2方案分析 3.1 测试结果 日志证据 :
1 2 3 4 SG[0].Addr=0x00000000FEC002F0 ← IOVA (高地址,来自 DataBuffer) desc_tbl PA=0x000000007361D5B0 ← PA (低地址,来自 SrbExtension) DMA Error! Status=0x01 State=ST_FDS ← 硬件无法读取描述符表
3.2 地址对比分析
地址类型
地址值
特征
是否 IOVA
DataBuffer
0xFEC002F0
高位地址 (~4GB)
✅ IOVA
SrbExtension
0x7361D5B0
低位地址 (~1.8GB)
❌ PA
3.3 结论 StorPortGetPhysicalAddress(Srb, SrbExtension_member) 不返回 IOVA!
Storport 的 DMA remapping 支持仅针对 DataBuffer ,不针对 SrbExtension 内部成员。
成员
文档预期
实际测试结果
DataBuffer
IOVA
✅ IOVA
SenseInfoBuffer
IOVA
未测试
SrbExtension
IOVA
❌ PA
3.4 可能原因分析 假设 1: SD Host Controller 特殊处理
AHCI 设备(硬盘)通常被认为是 “internal” 设备,可能有完整的 IOMMU 支持。SD 卡读卡器可能被认为是 “external” 设备,Kernel DMA Protection 对其有不同处理。
假设 2: SrbExtension 内存来源不同
Storport 分配的 SrbExtension 内存可能不经过 IOMMU 映射,只有 DataBuffer 经过映射。
假设 3: Storport 版本差异
不同 Windows 版本的 Storport 可能有不同的 DMA remapping 实现。
4. 调试过程:DMAR与ADMA2架构深度分析 4.1 DataBuffer vs SrbExtension 地址测试 1 2 3 DMAR_DIAG: Address comparison test: DataBuffer: PA(NULL)=0x0000000000000000 PA(Srb)=0x0000000000200760 DIFF(IOVA) SrbExtension: PA(NULL)=0x0000000072EF04A0 PA(Srb)=0x0000000072EF04A0 SAME
内存类型
PA(NULL)
PA(Srb)
结论
DataBuffer
0x00000000 (失败)
0x00200760
DIFF → 有 IOMMU 映射 ✅
SrbExtension
0x72EF04A0
0x72EF04A0
SAME → 无 IOMMU 映射 ❌
4.2 ADMA2 架构深度分析 用户的理解
软件驱动负责管理 system memory 的 Descriptor 数据;软件可以使用 DMA 或者非 DMA 方式访问 Descriptor 数据,获取到 DataBuffer 的物理地址,再配置进硬件的 ADMA2 Engine register;之后软件启动 ADMA2 hardware engine,hardware 自动搬运数据。
实际 SDHCI 规范
项目
用户理解
实际 SDHCI 规范
写入 0x58 寄存器的内容
DataBuffer 地址
Descriptor Table 地址
谁解析 Descriptor
软件 (CPU)
硬件 (ADMA2 Engine)
Descriptor 访问方式
软件 PIO 读取
硬件 DMA 读取
代码证据 1 2 3 4 5 6 7 8 data->data_mng.sys_addr = node->phy_node_buffer.head.pa; sdhci_writel(host, SDHCI_ADMA_ADDRESS, os_get_phy_addr32l(sys_addr)); if (host->bit64_enable) sdhci_writel(host, SDHCI_ADMA_ADDRESSH, os_get_phy_addr32h(sys_addr));
ADMA2 实际工作流程 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 27 28 29 30 31 32 33 34 35 36 37 ┌─────────────────────────────────────────────────────────────────┐ │ 步骤 1: 软件在内存中创建 Descriptor Table │ │ │ │ System Memory: │ │ ┌─────────────────────────────────────────────┐ │ │ │ Descriptor Table @ 0x72EF0000 (物理地址) │ │ │ │ ┌────────┬────────┬──────┬──────┐ │ │ │ │ │Addr │Length │Type │Attr │ │ │ │ │ │0x200760│4096 │Tran │Val │ -> Page1 │ │ │ │ │0x201760│4096 │Tran │Val │ -> Page2 │ │ │ │ │0x202760│512 │Tran │End │ -> Page3 │ │ │ │ └────────┴────────┴──────┴──────┘ │ │ │ └─────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ │ 软件 PIO 写入 v ┌─────────────────────────────────────────────────────────────────┐ │ 步骤 2: 软件写 Descriptor Table 地址到 0x58 寄存器 │ │ │ │ sdhci_writel(host, 0x58, 0x72EF0000); // Descriptor地址! │ └─────────────────────────────────────────────────────────────────┘ │ │ 软件发送 SD 命令 v ┌─────────────────────────────────────────────────────────────────┐ │ 步骤 3: 硬件自动通过 DMA 读取 Descriptor Table │ │ │ │ SD Host Controller: │ │ ┌─────────────────────────────────────────────┐ │ │ │ ADMA2 Engine (硬件状态机): │ │ │ │ 1. 从 0x58 寄存器读取 Descriptor 地址 │ │ │ │ 2. 通过 PCI 总线 DMA 读取 0x72EF0000 的内容 │ <-- 这里! │ │ │ 3. 解析得到 DataBuffer 地址: 0x200760 │ │ │ │ 4. 通过 PCI 总线 DMA 读写 DataBuffer │ │ │ └─────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
4.3 DMAR 与 ADMA2 的根本冲突 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌─────────────────────────────────────────────────────────────────┐ │ IOMMU (DMA Remapping) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ SD Host Controller System Memory │ │ (External Device) │ │ │ │ │ │ DMA 请求: 读取 0x72EF0000 │ │ │ (Descriptor Table PA) │ │ v │ │ ┌──────────┐ "0x72EF0000 不在 │ │ │ IOMMU │ ───> 该设备的映射表中" │ │ └──────────┘ → 拒绝访问! │ │ → ST_FDS 错误 │ │ │ └─────────────────────────────────────────────────────────────────┘
根本问题 :Windows Kernel DMA Protection 设计与 ADMA 硬件架构的冲突,不是驱动 bug。
4.4 SDHCI 传输模式对比
模式
Descriptor
Data Access
地址要求
DMAR 兼容
PIO
无
CPU 逐字读写
无需 IOVA
✅ 兼容
SDMA
无
硬件 DMA
需要 IOVA
⚠️ 有条件兼容
ADMA2
有
硬件 DMA
Descriptor + Data 都需要 IOVA
❌ 不兼容
ADMA3
有
硬件 DMA
Descriptor + Data 都需要 IOVA
❌ 不兼容
5. 调试过程:SDMA DMAR兼容方案实现 5.1 SDMA vs ADMA2 架构对比 1 2 3 4 5 6 7 8 9 10 11 12 13 ADMA2: ┌─────────────────┐ ┌───────────────────┐ ┌──────────────┐ │ sys_addr (0x58) │────>│ Descriptor Table │────>│ Data Buffer │ │ │ │ (需要 IOVA) │ │ (需要 IOVA) │ └─────────────────┘ └───────────────────┘ └──────────────┘ 写入 Desc 地址 硬件 DMA 读取 硬件 DMA 读写 SDMA: ┌─────────────────┐ ┌──────────────────────────────────────────┐ │ sys_addr (0x00) │────>│ Data Buffer (直接指向数据,无 Descriptor) │ │ │ │ (需要 IOVA) │ └─────────────────┘ └──────────────────────────────────────────┘ 写入 Data 地址 硬件 DMA 直接读写
关键区别 :SDMA 没有 Descriptor Table,sys_addr 直接指向数据缓冲区!
5.2 SDMA DMAR 兼容方案 核心思路 :直接使用 Srb->DataBuffer 的 IOVA,跳过内部缓冲区。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 bool tq_sdma_build_io_dmar_compatible (void * p, node_t * node) { srb_ext_t * pext = node_2_srb_ext(node); request_t * req = &pext->req; if (req->srb_sg_len == 1 ) { phy_addr_t iova; ULONG len; iova = StorPortGetPhysicalAddress(pdx, pext->psrb, req->srb_buff, &len); if (len >= req->tag_req_t .sec_cnt * 512 ) { data->data_mng.sys_addr = iova; data->data_mng.driver_buff = req->srb_buff; return TRUE; } } return FALSE; }
5.3 关键代码修改 tqsdma.c - 直接 IOVA 路径选择1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 if (pext->psrb != NULL ) { sg_list_t temp_sg[MAX_SGL_RANGE]; u32 sg_len = os_get_sg_list(pdx, pext->psrb, temp_sg); if (sg_len == 1 ) { u64 data_iova = temp_sg[0 ].Address; if ((data_iova & (sdma_bd_len - 1 )) == 0 || mgr->total_bytess <= sdma_bd_len) { dma.pa.QuadPart = data_iova; dma.va = req->srb_buff; data->data_mng.sys_addr = dma.pa; data->data_mng.sdma_direct_iova = 1 ; node->sdma_direct_iova = 1 ; DbgErr("DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x%08X%08X\n" , ...); } } }
tagqueue.c - 跳过 CPU copy1 2 3 4 5 6 7 8 9 if (pnode->sdma_like) { if (pnode->sdma_direct_iova) { DbgErr("DMAR_DIAG: SDMA completion - direct IOVA path, skip CPU copy\n" ); } else if (DATA_DIR_IN == psrb_ext->req.data_dir) { os_memcpy(psrb_ext->req.srb_buff, pnode->data_tbl.va, ...); } }
5.4 诊断日志 1 2 3 4 5 6 7 8 9 10 // ✅ 直接 IOVA 路径成功 DMAR_DIAG: SDMA DIRECT IOVA path! sys_addr=0x0000000000200760 len=4096 (no CPU copy) DMAR_DIAG: SDMA completion - direct IOVA path, skip CPU copy // ⚠️ Fallback 到内部缓冲区 DMAR_DIAG: SDMA srb_sg_len=3 (need 1 for direct IOVA), using internal buffer DMAR_DIAG: WARNING - Internal buffer uses PA, may fail under DMAR for external devices! // ⚠️ DataBuffer 未对齐 DMAR_DIAG: SDMA DataBuffer not aligned for direct path (addr=0x00200123, boundary=4096)
5.5 SDMA DMAR 兼容性结论
场景
SDMA 可行性
srb_sg_len == 1 (连续)
✅ 可行 - 直接使用 DataBuffer IOVA
srb_sg_len > 1 (分散)
❌ 不可行 - SDMA 不支持 Scatter-Gather
任意场景 (PIO fallback)
✅ 可行 - 使用 PIO 模式
6. 最终解决方案:SDMA + PIO 混合模式 6.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 寄存器传输 │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
6.2 INF 配置 1 2 3 HKR,"Parameters\GG8", "test_dma_mode_setting",0x00010001, 0x80000000
6.3 性能对比
传输模式
典型速度
CPU 占用
DMAR 兼容性
ADMA3 (正常)
80-100 MB/s
低
❌ 不兼容
SDMA (直接路径)
40-60 MB/s
中
✅ 条件兼容
SDMA + PIO
5-60 MB/s
中-高
✅ 完全兼容
PIO
5-15 MB/s
高
✅ 完全兼容
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)
DataBuffer 有 IOVA,SrbExtension 无
测试验证
问题在 Storport API 而非 Miniport
WDF vs StorPort 对比分析
7.2 解决方案汇总
方案
可行性
性能
说明
PIO 模式
✅ 已验证
低 (5-15 MB/s)
完全绕过 DMA
SDMA + PIO Fallback
✅ 已验证
中 (5-60 MB/s)
最佳工程妥协方案
SRB Extension 方案
❌ 失败
-
SrbExtension 不返回 IOVA
StorPortAllocateDmaMemory
❌ 失败
-
返回 PA,无 IOMMU 映射
7.3 长期解决方案路线图
阶段
方案
说明
当前
SDMA + PIO 混合模式
✅ 工程妥协方案
短期
联系 Microsoft
明确 Storport API 的 IOMMU 支持
中期
OEM SSDT Patch
将设备标记为 Internal
长期
考虑 WDF 框架
如需完整 DMAR 兼容
参考资料
StorPortGetPhysicalAddress
StorPortGetScatterGatherList
StorPortBuildScatterGatherList
Enabling DMA Remapping for Device Drivers
WDF Using DMA Enablers
StorAHCI Sample Driver
文档生成日期: 2026-03-21 整合版本: v1.0