在Windows内核的ACPI子系统实现中,设备扩展(Device Extension)是一个关键数据结构,它承载着设备驱动与ACPI总线之间的交互信息。今天我们就来深入探讨ACPIBuildDeviceExtension函数如何为ISA总线设备构建这些扩展结构。
_DEVICE_EXTENSION是ACPI驱动中用于存储设备状态的核心数据结构,其内存布局如下:
c复制typedef struct _DEVICE_EXTENSION {
ULONG Flags; // 设备状态标志
ULONG Signature; // 结构标识('GPS_')
PVOID DispatchTable; // 分发函数表
union {
WORK_QUEUE_CONTEXT WorkContext;
_FDO_DEVICE_EXTENSION Fdo;
_FILTER_DEVICE_EXTENSION Filter;
_PDO_DEVICE_EXTENSION Pdo;
};
union {
EXTENSION_WORKER WorkQueue;
BUTTON_EXTENSION Button;
THERMAL_EXTENSION Thermal;
LINK_NODE_EXTENSION LinkNode;
DOCK_EXTENSION Dock;
_PROCESSOR_DEVICE_EXTENSION Processor;
};
DEVICE_POWER_STATE DeviceState; // 当前电源状态
DEVICE_POWER_STATE PreviousState;// 前一个电源状态
_ACPI_POWER_INFO PowerInfo; // 电源管理信息
// ... 其他字段
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
这个结构通过联合体实现了对不同类型设备的支持,包括功能设备对象(FDO)、物理设备对象(PDO)和过滤驱动等。在调试输出中我们可以看到,每个扩展结构的签名都是0x5f534750(ASCII码对应'_SGP')。
关键提示:设备扩展的内存管理采用非分页池,因为它在中断上下文和DPC中都会被访问。这也是为什么我们在调试器中能看到这些结构的高地址(如0x89db4e80)。
从提供的DSDT(Differentiated System Description Table)反汇编代码可以看出,系统中有三个层级的ISA设备:
根级设备(位于_SB作用域):
asl复制Scope (_SB) {
Device (PCI0) // PCI根桥
Device (AGP) // AGP桥
Device (ISA) // ISA桥
Device (MBRD) // 主板设备
Device (DMAC) // DMA控制器
Device (PIC) // 可编程中断控制器
// ... 共12个设备
}
次级ISA设备(位于_SB.PCI0.ISA作用域):
asl复制Scope (\_SB.PCI0.ISA) {
Device (HPET) // 高精度事件定时器
Device (SMC) // 系统管理控制器
Device (COM3) // 串口3
// ... 共36个设备
}
底层IO设备(位于_SB.PCI0.ISA.SIO作用域):
asl复制Scope (\_SB.PCI0.ISA) {
Device (SIO) // 超级IO芯片
Device (LPT) // 并口
Device (COMA) // 串口A
// ... 共1个主设备+5个子设备
}
这种层级结构反映了典型的PC架构:PCI根桥下挂接ISA桥,ISA桥再连接各种传统设备。ACPI驱动会为每个设备创建对应的设备扩展。
ACPIBuildDeviceExtension函数的执行流程可以概括为:
ExAllocatePoolWithTag从非分页池分配扩展结构内存ChildDeviceList和SiblingDeviceList将设备挂接到总线在调试输出中,我们看到典型的设备扩展初始化结果:
code复制+0x000 Flags : 0xa
+0x008 Signature : 0x5f534750
+0x088 DeviceState : 0 (Stopped)
+0x140 ChildDeviceList : _LIST_ENTRY [ 0x89db4d18 - 0x89deb878 ]
ACPI驱动通过双向链表管理设备层级关系,关键字段包括:
ChildDeviceList:指向子设备链表的头部SiblingDeviceList:链接到兄弟设备ParentExtension:指向父设备扩展调试器中的链表遍历显示:
code复制0: kd> dx -id 0,0,89dd5240 -r1 ((ACPI!_LIST_ENTRY *)0x89db4e78)
[+0x000] Flink : 0x89db4d18
[+0x004] Blink : 0x89deb878
0: kd> dx -id 0,0,89dd5240 -r1 ((ACPI!_LIST_ENTRY *)0x89db4d18)
[+0x000] Flink : 0x89db4bb0
[+0x004] Blink : 0x89db4e78
这表明设备扩展形成了一个环形双向链表,总共包含49个节点(12+36+1)。
设备扩展中的关键资源字段:
c复制+0x10c Address : 0 // 设备地址
+0x114 ResourceList : (null) // 资源列表
+0x118 PnpResourceList : (null) // PnP资源列表
对于ISA设备,资源通常通过以下方式指定:
asl复制Device (COM1) {
Name (_HID, EISAID("PNP0501")) // 标准串口ID
Name (_CRS, ResourceTemplate() {
IO(Decode16, 0x3F8, 0x3F8, 0, 8) // IO端口3F8-3FF
IRQ(Edge, ActiveHigh, Shared, ) {3} // IRQ3
})
}
定位设备扩展:
windbg复制// 从设备对象找到扩展
dt nt!_DEVICE_OBJECT 0x89da6278 DeviceExtension
// 直接解析扩展结构
dt ACPI!_DEVICE_EXTENSION 0x89db4e80-148
遍历设备树:
windbg复制// 从父设备开始遍历
dx -id 0,0,89dd5240 -r1 (*((ACPI!_LIST_ENTRY *)0x89db4e78))
// 递归遍历子设备
.foreach (addr {dd 0x89db4e80+0x140 L1}) { .if (@addr != 0) { !list -x "dt ACPI!_DEVICE_EXTENSION @$extret" addr } }
检查电源状态:
windbg复制dt ACPI!_DEVICE_EXTENSION 0x89db4e80-148 DeviceState PreviousState
问题现象:设备无法进入D3状态
排查步骤:
windbg复制dt ACPI!_ACPI_POWER_INFO 0x89db4e80-148+0x90
windbg复制!amli find _PSC 89da6278
windbg复制!acpiinfopolicy
问题现象:设备枚举不完整
排查步骤:
windbg复制!list -t _LIST_ENTRY.Flink -x "dt ACPI!_DEVICE_EXTENSION @$extret" 0x89db4e78
windbg复制!amli dns \_SB.PCI0.ISA
由于每个设备都会创建扩展结构,在嵌入式系统中可以考虑:
定制扩展大小:通过修改ACPI驱动,为简单设备使用精简扩展
c复制typedef struct _SIMPLE_DEVICE_EXTENSION {
DEVICE_EXTENSION_COMMON;
ULONG DeviceSpecificData;
} SIMPLE_DEVICE_EXTENSION;
池标签分类:使用不同内存池标签区分设备类型
c复制ExAllocatePoolWithTag(NonPagedPool, size, 'ISAE'); // ISA设备
ExAllocatePoolWithTag(NonPagedPool, size, 'PCIE'); // PCI设备
对于ISA设备的电源管理:
状态缓存:在扩展中维护最近的状态转换时间
c复制typedef struct _ISA_POWER_EXTENSION {
DEVICE_EXTENSION_COMMON;
LARGE_INTEGER LastStateChangeTime;
ULONG StateChangeCount;
} ISA_POWER_EXTENSION;
延迟状态切换:对频繁切换的设备实现状态缓冲
c复制if (NewState != Ext->DeviceState) {
if (KeGetCurrentTime() - Ext->LastStateChangeTime < 100000) {
Ext->PendingState = NewState;
StartTimer();
return STATUS_PENDING;
}
}
扩展签名验证:在关键函数开始处添加检查
c复制ASSERT(Ext->Signature == 'GPS_');
链表完整性检查:定期验证设备链表
c复制VOID ValidateDeviceList(PLIST_ENTRY ListHead) {
PLIST_ENTRY Entry = ListHead->Flink;
while (Entry != ListHead) {
PDEVICE_EXTENSION Ext = CONTAINING_RECORD(Entry, DEVICE_EXTENSION, SiblingDeviceList);
ASSERT(Ext->Signature == 'GPS_');
Entry = Entry->Flink;
}
}
通过深入理解ACPI设备扩展的创建和管理机制,我们可以更好地调试电源管理问题、优化设备枚举过程,并构建更可靠的ACPI驱动程序。在实际项目中,建议结合WDK文档和ACPI规范,针对特定硬件平台定制扩展结构,以平衡功能需求和资源消耗。