Qcom ARM平台Windows驱动(五):SD Host PoFx电源状态问题Debug实录
1. 问题概述
BH202 SD Host Controller 在高通 ARM Windows 平台上出现一系列电源管理问题,涉及 StorPort PoFx 框架的 Activate/Idle 引用计数机制。问题在 cold boot(含 Windows Fast Startup 的 Hybrid S4)、显式 S4 hibernate、带卡/不带卡等多种组合下表现不同。
1.1 问题矩阵
| 场景 | 现象 | 根因关键词 |
|---|---|---|
| Cold boot 后 SD host 持续 D0 | idle timeout 不触发 D3 | PoFx 初始状态 mismatch |
| 带卡 cold boot 拔卡 → BSOD 0xA0 | INTERNAL_POWER_ERROR | 双重 IdleComponent |
| S4 hibernate 期间拔卡 → resume 后 BSOD | 10秒后系统重启 | S4 恢复 per-caller 状态 |
| 带卡 cold boot 后插卡不识别 | SD host 一直 D3 | host_check_lost + StorPort 未 prime |
1.2 环境信息
- 平台:Qualcomm Snapdragon 8380 (ARM64)
- OS:Windows 10/11 ARM64
- 驱动模型:StorPort miniport + PoFx (Runtime D3)
- 关键配置:
STOR_POFX_DEVICE_FLAG_IDLE_TIMEOUT = 10ms
2. 背景知识:StorPort PoFx 模型
StorPort miniport 通过 StorPortInitializePoFxPower 注册 PoFx,之后 StorPort 框架管理 adapter 的 Active/Idle 状态转换。
1 | ┌─────────────────────────────────────────────────────────┐ |
关键约束:
IdleComponent必须有前置的ActivateComponent(否则 BSOD 0xA0 arg1=0x608)- PoFx 注册后初始状态为 Active(count=1),但 miniport 的
pofx_active计数器为 0 - D3 callback(
ScsiAdapterPower)运行在 DISPATCH_LEVEL,不能 busy-wait
2.1 驱动架构中的 PoFx 调用点
1 | // 插卡:arm_vdd1_power_cycle → pofx_request_d0 → ActivateComponent |
3. Debug 过程
3.1 WinDbg 分析 BSOD Dump
第一个 dump 揭示了 StorPort unit removal 路径的 BSOD:
1 | INTERNAL_POWER_ERROR (a0) |
关键信息:StorPort 在 unit removal 时自行调用 IdleComponent。如果 miniport 在 thread_remove_card 中也调用了 IdleComponent,就形成双重 Idle → count 下溢 → BSOD。
3.2 DebugLogger 分析 D0 问题
带卡 cold boot 后 SD host 持续 D0 的 log:
1 | 06-01 04:45:21.408 --> arm_vdd1_power_cycle: |
关键发现:
arm_vdd1_power_cycle的pofx_request_d3因pofx_active=0直接返回(不调 IdleComponent)pofx_request_d0成功调用ActivateComponent(pofx_active → 1)- 拔卡时
pofx_request_d3调了IdleComponent但 StorPort 不响应 D3 callback(1秒 timeout)
这说明 S4 resume 后 StorPort 的 PoFx 状态机与 miniport 不同步。
3.3 Windows Fast Startup 的影响
Windows 10/11 默认”关机”实际是 Hybrid S4(内核会话 hibernate,用户会话关闭)。这意味着:
1 | 用户操作 "Shutdown" → 驱动收到 StorPowerActionHibernate (S4) |
驱动中的 s4_happened_flag 在 S4 D3 callback 中设为 TRUE,永不清除。因此”cold boot”后 s4_happened_flag=TRUE。
4. 根因分析
4.1 问题一:Cold Boot D0
1 | PoFx 注册后:StorPort 认为 adapter active (count=1) |
修复:在 pofx_request_d3 中实现 force-idle(检测 mismatch 并强制设 pofx_active=1 后调 IdleComponent)。
4.2 问题二:带卡 Cold Boot 拔卡 BSOD
1 | 带卡启动 → card_init → pofx_request_d0 → pofx_active=1 |
修复:S4 resume 后(s4_happened_flag=TRUE)跳过 pofx_request_d3,让 StorPort unit removal 独占 IdleComponent。
4.3 问题三:S4 Hibernate 期间拔卡 BSOD
1 | S4 前:卡工作中,曾调过 IdleComponent(进入 D3 前) |
修复:force-idle 增加 !s4_happened_flag 条件。S4 resume 后不触发 force-idle(PoFx 恢复状态可能已 idle)。
5. 修复方案总结
5.1 force-idle 条件(cardinterface.c pofx_request_d3 内)
1 | if (pdx->pm_state.rtd3_en && |
每个条件的作用:
| 条件 | 防止的问题 |
|---|---|
!card_present |
卡在位时不 idle |
!inited_card.card_present |
unit 存在时不 idle(避免双重 Idle) |
last_present == 0 |
bus change 已完成 |
!rtd3_entered |
不在 RTD3 D3 callback 内 |
!s3s4_entered |
不在 S3/S4 D3 callback 内 |
adapter_powerstate == D0 |
设备确实在 D0 |
s3s4_resume_for_card_init |
只在 init/resume 路径触发(非正常拔卡) |
!s4_happened_flag |
S4 resume 后不触发(PoFx 状态已恢复) |
5.2 card_power_off guard(cardinterface.c)
1 | if (!pdx->pm_state.s3s4_entered |
所有 D3 callback 内都不调用 pofx_request_d3,防止 busy-wait at DISPATCH_LEVEL。
5.3 arm_vdd1_power_cycle(cardinterface.c)
1 | void arm_vdd1_power_cycle(bht_dev_ext_t *pdx){ |
S4 resume 后跳过 D3(设备已在 D0),但保留 D0(PMIC 上电 + 确保 pofx_active=1)。
5.4 thread_remove_card 的 pofx_request_d3
S4 resume 后由 StorPort unit removal 独占 IdleComponent,miniport 不调用。
6. 关键设计约束
- PoFx Activate/Idle 必须配对:每次
ActivateComponent必须有对应的IdleComponent,反之亦然 - D3 callback 不能 busy-wait:运行在 DISPATCH_LEVEL,busy-wait 超过几秒触发 DPC_WATCHDOG
- StorPort unit removal 会调 IdleComponent:miniport 不能再调,否则双重 Idle
- S4 resume 恢复 PoFx per-caller 状态:不能假设 S4 resume 后状态是 fresh
- Windows Fast Startup = Hybrid S4:
s4_happened_flag在”关机”后为 TRUE s4_happened_flag永不清除:S4 后 GPIO 中断失效,依赖 BusRelations 作为卡检测替代
7. 验证矩阵
| 测试场景 | 预期结果 | 验证点 |
|---|---|---|
| 不带卡 cold boot | D3 | force-idle 触发 |
| 带卡 cold boot → 拔卡 | 无 BSOD | StorPort unit removal 独占 Idle |
| S4 hibernate → 休眠拔卡 → resume | 无 BSOD | force-idle 不触发 |
| 不带卡 S4 resume | D3 | StorPort idle timer |
| Warm boot → 插卡拔卡 | D3 | 正常 Activate/Idle 流程 |
| 带卡 cold boot → 拔卡 → 再插卡 | 识别 | host_check_lost goto retry |
8. 教训与总结
8.1 StorPort PoFx 的隐含行为
StorPort 文档未明确说明 unit removal 时会调用 IdleComponent。这个行为通过 WinDbg dump 的 call stack 才发现。StorPort miniport 开发时需要假设:StorPort 会在 unit 生命周期管理中自行管理 PoFx 引用。
8.2 Hybrid S4 的陷阱
Windows Fast Startup 使得”关机→开机”在驱动层面等价于 S4 hibernate→resume。驱动开发时不能假设 cold boot 是 fresh start——必须处理 S4 恢复的 stale 状态。
8.3 Debug 方法论
- WinDbg dump 分析:确定 BSOD 的 call stack 和触发条件
- DebugLogger 实时日志:追踪 PoFx 状态转换时序
- 对比分析:Good case(不带卡/warm boot)vs Bad case(带卡/S4),定位差异点
- Git bisect:缩小引入问题的 commit 范围
- 逐条件分析:列出 force-idle 的每个条件,分析每个场景下哪个条件阻止/允许触发