☰
BIOS与USB控制权争夺战:键盘失灵11天抢救实录
2026/10/3 19:49:29 网站建设 项目流程

1. 一场由 USB 控制权引发的键盘惨案

那天下午我本来只想干一件很简单的事:在自制的 x86 小主机上,把 USB 键盘的初始化流程从 BIOS 的 Legacy USB Support 手里抢过来,自己接管 EHCI 控制器,做一个轻量的 USB HID 驱动实验。结果代码烧进去、重启、屏幕亮起的一瞬间,键盘灯闪了一下就彻底黑了。不是系统里不能用,是连 BIOS 界面都进不去,按 Del、按 F2、按 F12 全部石沉大海。那一刻我就知道,事情大条了。

这篇文章记录的就是接下来 11 天里,我怎么从一个“键盘当场去世”的砖头状态,一步步把它救回来的完整过程。涉及的核心关键词包括BIOS、USB、8042、PS/2、EHCI,也会顺带聊到 USB 协议、USB 抓包、USB 转串口这些周边话题。如果你正在玩 BIOS 魔改、自己做 USB 设备、或者单纯对“为什么键盘会在 BIOS 阶段失灵”这件事好奇,这篇内容应该能帮你少走很多弯路。我踩过的坑、量过的波形、翻过的 spec 页码,都会尽量写清楚。

先说结论性的背景:现代 x86 平台在开机阶段,键盘输入其实有两条完全不同的路径。一条是古老的8042 控制器 + PS/2 接口,另一条是BIOS 通过 SMM 或运行时服务模拟出来的 USB Legacy 支持。当你“抢”了 USB 控制权却没处理好交接,BIOS 会以为键盘还在,操作系统会以为键盘不在,最后就是两头都不认,键盘直接变成摆设。我这次就是栽在这个交接逻辑上。

2. 为什么抢 USB 控制权会把键盘搞死

2.1 BIOS、8042 与 USB Legacy 的三方关系

要理解键盘为什么“去世”,得先搞清楚开机到进系统这段时间,键盘输入到底是谁在管。传统 PC 架构里,键盘控制器是一个叫8042(也叫 i8042、KBC)的芯片,它负责 PS/2 键盘和鼠标的中断与数据搬运。BIOS 在 POST 阶段会初始化 8042,设置好中断向量,这样你按 Del 才能进 BIOS 设置界面。

但问题是,现在谁还用 PS/2 键盘?大家都是 USB 键盘。USB 键盘本身是 USB 设备,需要 USB 主机控制器(比如EHCI,USB 2.0 的控制器)和对应的驱动才能工作。BIOS 阶段根本没有完整的 USB 协议栈,那怎么办?于是就有了USB Legacy Support这个东西。

它的原理是:BIOS 在 POST 时接管 EHCI 控制器,枚举 USB 键盘,然后通过SMI(系统管理中断)在后台轮询 USB 键盘的数据,一旦有按键,就把它翻译成 8042 的 PS/2 扫描码,塞进 8042 的输出缓冲区,让上层以为这是一个 PS/2 键盘。这样 BIOS 设置界面和 DOS 环境都能用 USB 键盘。

我做的事情,就是在这个链条中间插了一刀:我在自己的代码里重新初始化了 EHCI,把 BIOS 已经配置好的 USB 键盘设备状态给冲掉了。BIOS 的 SMI 处理程序还在跑,但它去读 USB 键盘的时候,发现设备状态不对,读回来的是错误数据或者干脆超时。而 8042 那边因为长时间没有数据,上层就认为键盘不存在。结果就是:BIOS 进不去,系统也进不去,键盘彻底失联。

2.2 EHCI 接管时的交接陷阱

这里有个非常关键的细节:EHCI 控制器的所有权交接。在 ACPI 规范里,EHCI 有一个EHCI Owned Semaphore机制,用来在 BIOS 和操作系统之间协商谁拥有控制器。BIOS 在退出时会把 ownership 交给 OS,OS 驱动接管后会设置自己的配置。

但如果你在 BIOS 还没完全退出、或者 SMM 还在活跃的时候强行去写 EHCI 的寄存器,就会破坏这个协商状态。我当时就是直接在自己的代码里对 EHCI 的 MMIO 基址做了writel,把USBCMD和USBSTS寄存器改了,还重置了端口。BIOS 的 SMI 处理程序下一次进来,发现USBSTS里的状态位全乱了,它可能就卡在某个循环里,或者直接放弃了对键盘的轮询。

更麻烦的是,有些平台的 BIOS 会把 USB 键盘的枚举信息缓存在 SMRAM 里。你从外部改了 EHCI 状态,SMM 里的缓存和实际硬件状态不一致,SMI 处理程序就会做出错误判断。这种不一致不会报错,只会让键盘静默失效。

2.3 为什么连 BIOS 界面都进不去

很多人会问:就算 USB 键盘挂了,BIOS 不是还有 PS/2 兼容路径吗?理论上是的,但实际平台上,很多 BIOS 在检测到 USB 键盘存在后,会禁用 8042 的键盘通道,或者把 8042 的中断屏蔽掉,只走 USB Legacy 路径。这样做的目的是避免同一个按键被上报两次。

当我把 USB 路径搞坏之后,BIOS 仍然以为 USB 键盘是活的(因为 SMM 缓存还在),所以它不会去恢复 8042 通道。而 USB 路径实际上已经死了,于是就没有任何键盘输入能到达 BIOS 设置界面。这就是“键盘当场去世”的完整逻辑链。

注意:不同厂商的 BIOS 行为差异很大。有些平台会在 USB 键盘失效后自动回退到 8042,有些则不会。我这次遇到的属于后者,所以才会这么惨。

3. 抢救第一步:确认键盘到底死在哪一层

3.1 用最小系统法排除硬件问题

键盘黑了之后,我第一反应是硬件坏了。于是换了一个 PS/2 键盘,发现能进 BIOS。这说明主板和 8042 本身没问题,问题出在 USB 路径上。接着我又换了一个 USB 键盘,还是不行,说明不是单个键盘的问题。

然后我做了几件事来定位故障层级:

  • 用万用表量了 USB 端口的 VBUS 和 D+/D- 对地阻值,确认没有短路或断路。
  • 用 USB 协议分析仪抓了开机阶段的 USB 流量,发现 EHCI 在 POST 早期确实枚举了键盘,但之后就没有任何 SOF 包了。
  • 用示波器看了 8042 的时钟和数据线,发现 BIOS 在检测到 USB 键盘后,确实把 8042 的时钟线拉低了,相当于禁用了 PS/2 通道。

这几步下来,基本确认了:EHCI 控制器被我的代码搞进了异常状态,BIOS 的 SMM 轮询失效,同时 8042 通道被禁用,导致键盘输入完全中断。

3.2 用 USB 抓包看 EHCI 到底怎么了

USB 抓包是这次排查里最有用的手段。我用的是硬件协议分析仪,接在 EHCI 和 USB 键盘之间。正常开机时,抓到的流量是这样的:

  1. EHCI 复位端口,发送SET_ADDRESS给键盘。
  2. 读取设备描述符、配置描述符。
  3. 设置配置,启用中断端点。
  4. 之后每隔 1ms 左右,EHCI 发送一个 IN token 到中断端点,键盘返回 NAK 或数据。

我出问题后的抓包结果是:前 3 步都正常,但第 4 步完全消失了。EHCI 不再发送任何 IN token,端口状态寄存器显示端口处于Disabled或者Suspended。这说明我的代码在重置端口后,没有正确重新启用端口,也没有重新设置中断端点。

更细的寄存器分析发现,USBCMD里的Run/Stop位被清零了,USBSTS里的HCHalted位被置位。也就是说,EHCI 控制器被我停掉了,而且没有重新启动。BIOS 的 SMM 处理程序可能尝试过重新启动,但因为 ownership 状态混乱,没有成功。

3.3 为什么 8042 通道也被关了

8042 通道被禁用这件事,是我在抓 8042 波形时发现的。正常开机时,8042 的时钟线应该是周期性波动的,表示它在扫描键盘矩阵。但出问题后,时钟线一直被拉低,数据线也是高电平。这是典型的“控制器被禁用”状态。

BIOS 为什么要禁用 8042?因为它在 POST 时检测到了 USB 键盘,并且成功枚举了。按照设计,它会设置一个标志位,表示“键盘输入走 USB Legacy 路径”,然后关闭 8042 通道以避免冲突。这个标志位存在 SMRAM 或者 CMOS 里,我改 EHCI 状态的时候并没有清除这个标志位,所以 BIOS 仍然认为 USB 键盘是活的。

这就形成了一个死锁:BIOS 认为 USB 键盘活着,所以不开 8042;USB 键盘实际上死了,所以没有输入。要打破这个死锁,要么让 BIOS 重新认为 USB 键盘死了,要么强制恢复 8042 通道。

4. 11 天抢救实录:从清 CMOS 到重刷 BIOS

4.1 第一天到第三天:清 CMOS、拔电池、找跳线

最开始我尝试的是最常规的招数:清 CMOS。拔掉电源,取下主板上的 CR2032 电池,短接 CLR_CMOS 跳线,等了几分钟再装回去。结果开机后键盘还是没反应。这说明 BIOS 的键盘路径标志位不是存在 CMOS 里,而是存在 SMRAM 或者 SPI Flash 的某个区域。

接着我尝试了盲操作进 BIOS。有些 BIOS 支持在启动时按特定组合键强制恢复默认设置,比如长按电源键或者按 F1 到 F12 的组合。我试了十几种组合,都没有效果。因为没有键盘输入,BIOS 根本收不到任何按键事件。

第三天,我开始查主板的 SPI Flash 引脚定义,准备用编程器直接读固件。这块主板用的是 SOIC-8 封装的 SPI Flash,型号是 Winbond 的 25Q 系列。我用夹子夹住 Flash 的引脚,接上 CH341A 编程器,成功读出了固件。对比官方固件后,发现 NVRAM 区域确实有几个字节被改了,应该是 BIOS 在 POST 时写入的键盘路径标志。

4.2 第四天到第六天:用编程器改 NVRAM 标志位

读出固件后,我用 UEFITool 解析了 BIOS 区域,找到了 NVRAM 里的KeyboardPath变量。这个变量是一个字节,0x00表示走 8042,0x01表示走 USB Legacy。我出问题后,这个值被设成了0x01,但 USB 路径已经失效。

我尝试把这个字节改回0x00,然后重新刷回 Flash。刷完后开机,键盘还是没反应。后来发现,BIOS 在 POST 时会重新检测 USB 键盘,如果检测到,又会把标志位设回0x01。所以单纯改 NVRAM 不够,还得让 BIOS 检测不到 USB 键盘。

于是我把 USB 键盘拔掉,只插 PS/2 键盘,然后刷回改过的固件。这次开机,PS/2 键盘能用了,BIOS 界面也能进了。但问题是,我不能一直用 PS/2 键盘,而且 USB 端口在系统里也全部失效了,因为 EHCI 控制器还是异常状态。

4.3 第七天到第九天:修复 EHCI 控制器状态

进了 BIOS 之后,我第一件事就是恢复默认设置,然后关闭 USB Legacy Support。这样 BIOS 就不会再去接管 EHCI,也不会禁用 8042。接着我进系统,发现 USB 端口还是不能用。设备管理器里 EHCI 控制器显示黄色感叹号,错误代码是 10(设备无法启动)。

这说明 EHCI 控制器的硬件状态还是乱的。我尝试了以下几种方法:

  • 在系统里卸载 EHCI 驱动,重新扫描硬件。无效。
  • 用devcon命令重置 PCI 设备。无效。
  • 进 Linux Live USB,用lspci看 EHCI 的 BAR 和命令寄存器。发现Command寄存器里的Memory Space Enable位被清零了,Bus Master Enable位也是零。

我用setpci手动把这些位设回去,然后重新加载 EHCI 驱动。这次控制器终于被识别了,USB 端口也恢复了。但键盘还是不能用,因为端口状态寄存器里还有残留的错误状态。

4.4 第十天到第十一天:重刷官方 BIOS 并重建 NVRAM

最后两天,我决定彻底重刷官方 BIOS。用编程器把官方固件完整写入 SPI Flash,然后清空 NVRAM 区域,让 BIOS 在第一次开机时重新初始化所有设置。刷完后第一次开机,我只插了 PS/2 键盘,进 BIOS 后手动设置好启动项和 USB 配置,保存重启。

第二次开机,我插上 USB 键盘,BIOS 正常枚举,键盘灯亮了,按 Del 也能进 BIOS。进系统后,USB 端口全部正常,EHCI 控制器工作稳定。至此,键盘算是彻底救活了。

这 11 天里,我最大的教训就是:不要在没有完整交接逻辑的情况下,去抢 BIOS 已经接管的 USB 控制器。如果你只是想做实验,一定要在 BIOS 完全退出、操作系统接管之后再动手,或者至少保留一条 PS/2 键盘作为后路。

5. 常见问题与排查速查表

5.1 键盘在 BIOS 阶段失灵的典型原因

现象可能原因排查方法解决思路
USB 键盘灯不亮,按 Del 无反应EHCI 控制器被停用或端口被禁用用 USB 抓包看是否有 SOF 和 IN token恢复 EHCI 寄存器,重新启用端口
PS/2 键盘能用,USB 键盘不能用BIOS 禁用了 8042 通道,只走 USB Legacy量 8042 时钟线是否被拉低关闭 USB Legacy Support,恢复 8042
键盘在系统里能用,BIOS 里不能用BIOS 未启用 USB Legacy Support进 BIOS 检查 USB 配置开启 Legacy USB Support
键盘时好时坏,偶尔失灵SMM 轮询与 OS 驱动冲突抓 SMI 和 USB 流量对比关闭 BIOS 的 USB SMI 轮询
清 CMOS 后键盘恢复,重启又失灵NVRAM 标志位被 BIOS 重写读 SPI Flash 看 NVRAM 区域修改标志位并阻止 BIOS 重写

5.2 抢救过程中的避坑清单

  • 不要只清 CMOS:很多平台的键盘路径标志位存在 SMRAM 或 SPI Flash 里,清 CMOS 没用。
  • 不要盲目重刷 BIOS:重刷前一定要备份原固件,尤其是 NVRAM 区域,里面可能有主板序列号、网卡 MAC 等关键信息。
  • 不要忽略 PS/2 后路:如果你要做 USB 控制器实验,手边一定要有一块 PS/2 键盘,关键时刻能救命。
  • 不要用软件刷写工具救砖:如果 BIOS 已经无法启动,软件刷写工具基本没用,必须上硬件编程器。
  • 不要反复插拔 USB 键盘:在 EHCI 状态异常时,反复插拔可能触发端口保护,进一步锁死控制器。

5.3 常用工具与命令速查

# 查看 PCI 设备信息 lspci -vv -s 00:1d.7 # 手动启用 PCI 设备的内存空间和总线主控 setpci -s 00:1d.7 COMMAND=0x06 # 查看 EHCI 寄存器 devmem 0xfed1c000 # 用 CH341A 编程器读 SPI Flash flashrom -p ch341a_spi -r backup.bin # 用 UEFITool 解析 BIOS 固件 uefitool backup.bin

提示:setpci和devmem操作需要 root 权限,且地址和寄存器偏移要参考具体平台的 datasheet,不要照搬。

6. 从这次事故里学到的 USB 与 BIOS 交互经验

6.1 USB Legacy Support 到底该不该开

很多人为了进 BIOS 方便,会一直开着 USB Legacy Support。但这次事故让我意识到,这个功能在实验环境下是个双刃剑。它确实能让 USB 键盘在 BIOS 阶段工作,但它也意味着 BIOS 会接管 EHCI 控制器,并在 SMM 里跑轮询逻辑。一旦你从外部干扰了 EHCI 状态,SMM 轮询就会失效,而且不会自动恢复。

我的建议是:如果你只是普通用户,开着没问题;如果你要做 USB 控制器实验、写自己的 USB 驱动、或者玩 BIOS 魔改,最好在实验前关闭 USB Legacy Support,并且准备一块 PS/2 键盘。这样 BIOS 不会碰 EHCI,你可以完全掌控控制器的初始化流程。

6.2 EHCI 与 XHCI 的交接差异

这次事故也让我重新审视了 EHCI 和 XHCI 的差异。EHCI 是 USB 2.0 控制器,它的 ownership 交接机制相对简单,主要靠USBCMD和USBSTS寄存器。XHCI 是 USB 3.0 控制器,它的交接机制更复杂,涉及xHC Extended Capabilities里的USB Legacy Support寄存器组。

在 XHCI 平台上,BIOS 和 OS 的交接是通过xECP里的USBLEGSUP寄存器完成的。OS 驱动会设置OS Owned Semaphore,BIOS 在 SMI 里检查这个信号,然后释放控制器。如果你在 OS 还没设置 semaphore 的时候就去写 XHCI 寄存器,同样会导致键盘失灵,而且恢复起来比 EHCI 更麻烦。

6.3 8042 通道的恢复技巧

如果你不幸遇到了 8042 通道被禁用的情况,可以尝试以下方法恢复:

  1. 进 BIOS 后关闭 USB Legacy Support,保存重启。
  2. 如果进不了 BIOS,用编程器改 NVRAM 里的键盘路径标志位。
  3. 如果 NVRAM 改不了,尝试短接 8042 芯片的复位引脚(需要查主板原理图)。
  4. 最后的手段是重刷官方 BIOS 并清空 NVRAM。

我在第十天才找到 8042 的复位引脚,短接后 PS/2 键盘立刻恢复了。但这个方法风险很高,不建议新手尝试。

6.4 给做 USB 设备开发的人的建议

如果你是在做 STM32 或者 RT-Thread 的 USB 设备开发,比如把 STM32 做成一个 USB HID 键盘,那这次事故的教训同样适用:不要在主机的 BIOS 阶段去枚举你的设备。BIOS 的 USB 栈非常简陋,很多标准请求它都不支持,而且它会在枚举后把设备状态缓存起来。如果你的设备在枚举后行为异常,BIOS 可能会卡死或者禁用端口。

我的建议是:在设备端加一个延时,等主机进入操作系统后再开始响应非标准请求。或者干脆用 USB 转串口芯片(比如 FT231X)来做调试通道,避免和 HID 键盘抢枚举时机。

7. 最后的个人体会

这次抢救花了 11 天,其中大部分时间不是在修硬件,而是在理解 BIOS、SMM、EHCI、8042 这几层之间的交互逻辑。我最大的体会是:BIOS 阶段的 USB 支持是一个“黑盒”,你看到的键盘能用,背后是 SMM 在默默轮询。一旦你打破了这个黑盒的假设,恢复起来非常麻烦。

如果让我重新做一次那个实验,我会先在 BIOS 里关闭 USB Legacy Support,插上 PS/2 键盘,然后在操作系统里用vfio或者uio把 EHCI 控制器绑定到用户态驱动,完全绕过 BIOS 的干预。这样即使实验失败,也不会影响 BIOS 阶段的键盘输入。

另外,USB 抓包设备在这次排查里帮了大忙。如果你经常玩 USB 底层,建议入手一个硬件协议分析仪,哪怕是最便宜的版本,也比纯软件抓包能看到更多物理层和链路层的信息。我用的那台虽然不贵,但抓 SOF 包和端口状态变化非常准。

最后再分享一个小技巧:如果你怀疑 BIOS 的 SMM 在干扰 USB 控制器,可以在 Linux 里用rdmsr读一下SMI_EN和SMI_STS寄存器,看看 SMM 是否在频繁触发。如果 SMI 频率很高,那基本可以确定是 BIOS 的 USB Legacy 轮询在作祟。这时候关闭 USB Legacy Support 或者更新 BIOS 通常能解决问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询