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