说实话,第一次在WinDbg里看到ACPI!ACPIBuildProcessDevicePhaseSta这个函数名时,我盯着它看了好一会儿。这个名字长得像把半句话塞进了一个符号:Build、Process、Device、Phase、Sta,一个函数名里同时带了“构建”“处理”“设备”“阶段”“状态”五个动作。等真正把一个ISA设备“莫名其妙消失”的问题排查完之后,我才明白它为什么非要叫这个名字——ACPI.sys在把命名空间里的Device节点变成Windows能识别的设备对象时,中途要过的关卡比想象中多得多。这篇笔记从一个真实排查经历出发,拆解ACPIBuildProcessDevicePhaseSta和ACPIDetectDuplicateHID这两个ACPI驱动内部函数对ISA设备的处理逻辑,顺便给想用WinDbg摸ACPI设备枚举底细的朋友一份可以直接照着操作的排查路线。内容偏向Windows内核驱动与固件联调场景,做驱动开发、BIOS/EC固件、以及设备枚举问题排查的朋友应该都能用得上。
1. 为什么ISA设备在ACPI驱动里是个“特殊钉子户”
1.1 今天仍在ACPI命名空间里出现的ISA设备
可能有人觉得奇怪,现在的主板上连ISA插槽都找不到了,研究ACPI对ISA设备的处理还有什么意义?实际上只要你打开过一台带Super I/O芯片的机器的DSDT表,就会发现ACPI命名空间里到处都是ISA风格的设备节点:串口(PNP0501)、并口(PNP0400)、PS/2键盘鼠标控制器(PNP0303、PNP0F13)、DMA控制器(PNP0200)、8254系统定时器(PNP0100)、PIC中断控制器(PNP0000)、PC喇叭(PNP0800)、实时钟(PNP0B00),全都没有消失。它们物理上挂在LPC总线桥上,在ACPI命名空间里的路径通常是_SB_.PCI0.LPCB.SIO1、_SB_.PCI0.LPCB.FDC0这样的形式。
这些设备在Windows设备管理器里可能被归到“系统设备”或“端口”下面,你平时不会特别留意它们,但只要有一天某个串口不出现在设备管理器里,或者COM口和LPT口对不上号,问题就会一路追到ACPI驱动对ISA设备的枚举逻辑上。ISA设备不像PCI设备有配置空间可以自己报家门,也没有USB那样的描述符机制,操作系统想发现它们,唯一靠谱的途径就是去读ACPI命名空间里的声明。
1.2 没有总线枚举机制,ISA设备靠ACPI静态宣告
在PCI总线上,操作系统可以通过配置空间读取Vendor ID、Device ID、Class Code这些字段,硬件自己就能告诉驱动“我是谁”。USB也类似,设备插上去之后通过枚举请求把描述符交出来。ISA设备完全没有这套机制——ISA总线上没有配置周期,你没法像读PCI寄存器那样去扫一个ISA设备的存在性和身份信息。
所以ACPI规范里对ISA设备的处理思路是:固件在命名空间里用_HID直接声明“这里有这样一个设备”,用_STA告诉操作系统“这个设备现在在不在、能不能用”。ISA设备的地址信息(IO端口、IRQ、DMA)也不是通过总线扫描得到的,而是通过_CRS、_PRS、_SRS这些资源对象静态描述出来的。换句话说,ISA设备在ACPI里是“固件说是啥就是啥”的存在,没有任何动态发现机制兜底。这也直接导致后续的重复HID问题——固件如果多写了一个节点,操作系统只能靠去重逻辑兜着。
1.3 ISA设备在ACPI设备枚举阶段中的位置
ACPI.sys在把命名空间解析成设备树的时候,并不是简单地把所有Device节点都变成DeviceObject,而是要经过一个阶段化的流程。从符号命名和行为推断来看,ACPI驱动内部会对每个设备节点执行类似“解析标识符、评估状态、建立设备关系、上报PnP管理器”的处理,ACPIBuildProcessDevicePhaseSta就处在“解析标识符之后、建立设备关系之前”的状态评估环节。
函数名里Ph
