Windows驱动调试(一):BSOD 0xA IRQL_NOT_LESS_OR_EQUAL 问题分析报告

Windows 驱动调试(一):BSOD 0xA IRQL_NOT_LESS_OR_EQUAL 问题分析报告

日期: 2026-03-21
问题编号: BSOD 0xA0 (BH202)
影响产品: Bayhub SD Host Controller (BH202)
复现概率: 1/50 machines × 1/3000 S4 cycles (WP# 开启时 <20%)
状态: ✅ 已解决


1. 问题概述

1.1 问题现象

Qualcomm + BH202 SD chip 平台上进行大量老化测试时,系统出现蓝屏死机 (BSOD),错误码为 0xA (IRQL_NOT_LESS_OR_EQUAL)

1.2 复现环境

环境配置 描述 复现概率
环境 1 BH202 V10039 驱动 + BIOS 开启 SD Write Protection Pin (WP#) < 20%
环境 2 BH202 V10039 驱动 + BIOS 关闭 SD Write Protection Pin (WP#) < 0.1%

1.3 核心结论

根本原因: BH202 driver V10039 代码中的 notify 线程创建时间早于 notify 事件初始化时间,导致有概率发生空指针访问,进而触发 BSOD 0xA。


2. 错误码分析

2.1 BSOD 0xA 错误信息

1
2
3
4
IRQL_NOT_LESS_OR_EQUAL (a)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is usually caused by
drivers using improper addresses.

2.2 参数解析

参数 含义
Arg1 0x0000000000000000 访问的内存地址 (NULL)
Arg2 0x0000000000000002 发生访问时的 IRQL 级别
Arg3 0x0000000000000000 bit0=0 表示读操作,bit3=0 表示非执行操作
Arg4 fffff803a0459c94 触发访问的代码地址

2.3 错误解读

根据 Microsoft 官方文档 (Bug Check 0xA):

  • Arg1 = 0 < 0x1000,符合 “the issue is likely a NULL pointer dereference“ 的描述
  • 访问的地址为 0 (NULL),确认是空指针访问导致的 BSOD

3. 内核堆栈分析

3.1 完整调用堆栈

1
2
3
4
5
6
7
8
9
10
11
12
STACK_TEXT:
fffff684`a8609fb0 fffff803`a04b18f4 : nt!KeBugCheck2+0x2e8
fffff684`a860a5c0 fffff803`a082af24 : nt!KiAbortException+0x1d4
fffff684`a860a600 fffff803`a0803c5c : nt!KisSynchronousException+0x3791a4
fffff684`a860a6f0 fffff803`a080285c : nt!KzSynchronousException+0x24
fffff684`a860a750 fffff803`a0459c94 : nt!KeWaitForSingleObject+0x214
fffff684`a860ab70 fffff803`9e296108 : storport!StorPortExtendedFunction+0x2ab0
fffff684`a860acb0 fffff803`a0865f9c : bhtsdhubdr!thread_polling_notify+0xa8
[thread.c @ 768]
fffff684`a860ad00 fffff803`a04e9c5c : nt!IopThreadStart+0x3c
fffff684`a860ad30 fffff803`a0800bfc : nt!PspSystemThreadStartup+0x7c
fffff684`a860ad90 00000000`00000000 : nt!KiStartSystemThread+0x24

3.2 堆栈解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
KiStartSystemThread

PspSystemThreadStartup

IopThreadStart

bhtsdhubdr!thread_polling_notify ← BSOD 发生点

storport!StorPortExtendedFunction

nt!KeWaitForSingleObject ← 等待未初始化的事件对象

nt!KzSynchronousException ← 同步异常

nt!KiAbortException

nt!KeBugCheck2 ← 系统蓝屏

3.3 故障点定位

项目
故障文件 F:\AE\bh202-arm-win\SDSTORDriver\sdstor\main\thread.c
故障行号 768
故障函数 bhtsdhubdr!thread_polling_notify+0xa8
故障原因 KeWaitForSingleObject 中访问 NULL 指针

4. 根因分析

4.1 问题本质

Driver 代码执行顺序错误

  1. Notify 线程 先被创建
  2. Notify Event 后才初始化
  3. 如果 Notify 线程在 Event 初始化前执行,就会访问 NULL 指针

4.2 触发条件分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────────┐
│ Notify 线程调度时机问题 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 场景: 主线程执行时间过长 │
│ │ │
│ ├── BIOS 开启 WP# → 主线程等待 IOCTL → CPU 占用时间长 │
│ │ │
│ └── BIOS 关闭 WP# → 主线程快速释放 → CPU 占用时间短 │
│ │ │
│ ▼ │
│ CPU 调度策略 │
│ │ │
│ ├── 主线程占用 CPU 很久 → Notify 线程被调度概率高 │
│ │ │
│ └── 主线程占用 CPU 很短 → Notify 线程被调度概率低 │
│ │ │
│ ▼ │
│ Notify 线程创建后立即执行 → Event 未初始化 → NULL 访问 → BSOD │
│ │
└─────────────────────────────────────────────────────────────────────┘

4.3 概率分析

因素 影响
WP# 开启 主线程需要等待 IOCTL 完成才释放 CPU,占用时间长,Notify 线程被立即调度概率高
WP# 关闭 主线程快速释放 CPU,占用时间短,Notify 线程被调度概率低
CPU 调度状态 取决于 Notify 线程创建时,CPU 是否处于”待调度”状态

4.4 OS 调度原理

根据操作系统原理,同级别 (IRQL) 的多个线程是分时复用 CPU 的。如果某线程已经占用 CPU 很长时间,CPU 就即将执行其他同级别线程。


5. 代码问题定位

5.1 问题代码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
DriverEntry / StorHwPassiveInitialize

├── (1) os_create_thread() ← 线程在此创建
│ ↓
│ CPU 可能立即调度线程
│ ↓
│ thread_polling_notify() 执行
│ ↓
│ KeWaitForSingleObject(p->evt) ← evt 未初始化 = NULL!

└── (2) win_init_event_mgr() ← 事件在后续才初始化

└── win_init_event() → StorPortInitializeEvent()

5.2 关键代码位置

文件 函数 行号 说明
winscsientry.c StorHwPassiveInitialize 348 os_create_thread() 调用
winscsientry.c scsi_HwInitialize 386 初始化入口
winapi.c win_init_event_mgr 477+ Event 初始化
winapi.c win_init_event 436 StorPortInitializeEvent
thread.c thread_polling_notify 768 Notify 线程函数

5.3 时序图

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
┌─────────────────────────────────────────────────────────────────────┐
│ V10039 驱动初始化时序 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [主线程] [Notify 线程] │
│ │ │ │
│ │ ① DriverEntry │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ② scsi_HwInitialize │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ③ StorHwPassiveInitialize │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ④ os_create_thread() │ │
│ │──────────────────────────────► │
│ │ │ │ │
│ │ │ CPU 调度 │ │
│ │ ▼ ▼ │
│ │ ⑤ thread 运行 │
│ │ ⚠ KeWaitForSingleObject(NULL) │
│ │ 💥 BSOD! │
│ │ │ │
│ │ ⑥ win_init_event_mgr() │ │
│ │──────────────────────────────► (如果还没崩溃) │
│ │ │ │
│ │ ⑦ win_init_event() │ │
│ │──────────────────────────────► │
│ │ │ │
│ ▼ ▼ │
│ │
└─────────────────────────────────────────────────────────────────────┘

6. 解决方案

6.1 推荐方案:代码修复 (V10040)

核心修改: 保证 Notify Event 初始化早于 Notify 线程创建

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
┌─────────────────────────────────────────────────────────────────────┐
│ V10040 驱动初始化时序 (已修复) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [主线程] [Notify 线程] │
│ │ │ │
│ │ ① DriverEntry │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ② scsi_HwInitialize │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ③ StorHwPassiveInitialize │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ④ win_init_event_mgr() │ ✅ 先初始化所有事件 │
│ │──────────────────────────────► │
│ │ │ │
│ │ ⑤ win_init_event() │ │
│ │──────────────────────────────► │
│ │ │ │
│ │ ⑥ os_create_thread() │ ✅ 然后才创建线程 │
│ │──────────────────────────────► │
│ │ │ │
│ │ │ │ │
│ │ │ CPU 调度 │ │
│ │ ▼ ▼ │
│ │ ⑦ thread 运行 │
│ │ │ │
│ │ ✅ KeWaitForSingleObject(valid_evt) │
│ │ │ │
│ │ ▼ │
│ │ 正常工作 │
│ │ │ │
│ ▼ ▼ │
│ │
└─────────────────────────────────────────────────────────────────────┘

6.2 代码修改

修改位置: winscsientry.c - StorHwPassiveInitialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// V10039 (有问题)
BOOLEAN StorHwPassiveInitialize(IN PVOID DeviceExtension)
{
// ...
// 创建线程太早!
os_create_thread(pdx, &pdx->os.thread[0], pdx, thread_main);

return TRUE;
}

// V10040 (已修复)
BOOLEAN StorHwPassiveInitialize(IN PVOID DeviceExtension)
{
// ...
// ✅ 先初始化所有事件
win_init_event_mgr(pdx, &pdx->os.evt_mgr);

// ✅ 再创建线程
os_create_thread(pdx, &pdx->os.thread[0], pdx, thread_main);

return TRUE;
}

6.3 Workaround 方案

如果不修改驱动代码,可通过 BIOS 设置 降低复现概率:

设置 效果
关闭 SD Write Protection Pin (WP#) 减少主线程占用 CPU 时间,降低 Notify 线程被立即调度的概率

注意: 此方案仅能降低概率,无法根本解决问题


7. 影响范围评估

7.1 风险场景

场景 风险等级 说明
系统冷启动 驱动加载时可能触发
设备管理器 Disable/Enable 驱动重新初始化
S4 休眠唤醒 系统从休眠状态恢复
运行时热插拔 驱动已完全初始化

7.2 平台差异

平台 复现概率 原因
BIOS 开启 WP# < 20% 主线程等待时间长
BIOS 关闭 WP# < 0.1% 主线程等待时间短
AMD + Qualcomm CPU 调度策略差异

8. 测试验证

8.1 验证计划

测试项 验证方法 预期结果
冷启动测试 100 次冷启动 0 次 BSOD
S4 循环测试 3000 次 S4 0 次 BSOD
Disable/Enable 50 次操作 0 次 BSOD
热插拔测试 100 次插拔 0 次 BSOD

8.2 回归测试

修复后需要验证:

  • ✅ 正常读写功能不受影响
  • ✅ 电源管理功能正常
  • ✅ 性能无明显下降

9. 总结

9.1 问题回顾

项目 说明
问题现象 系统蓝屏 BSOD 0xA
根本原因 线程创建早于事件初始化,导致空指针访问
触发条件 CPU 调度策略 + 主线程执行时间
复现概率 1/50 machines × 1/3000 S4 cycles (WP# 开)

9.2 解决方案

方案 效果 推荐度
V10040 驱动修复 ✅ 根本解决 ⭐⭐⭐⭐⭐
关闭 BIOS WP# ⚠️ 降低概率 ⭐⭐

9.3 经验教训

  1. 初始化顺序至关重要: 驱动初始化时,必须保证资源在被使用前已完成初始化
  2. 线程创建风险: 创建线程后应立即检查是否有竞态条件
  3. 系统调度不确定性: 不能假设线程创建后不会立即执行

参考资料

  1. Bug Check 0xA: IRQL_NOT_LESS_OR_EQUAL
  2. KeWaitForSingleObject function
  3. StorPortInitializeEvent
  4. Thread Creation in Kernel Mode

文档生成日期: 2026-03-21
问题编号: BH202 BSOD 0xA0
驱动版本: V10039 → V10040