由于windows设备驱动程序被设计成以设备堆栈(Device stack)的形式,下层驱动向上提供支持,所以我们习惯于不加思考的在设备栈的上层使用来自下层驱动的各种已有功能。当我还在做驱动程序时,经常见到这些句话:
A1."功能驱动(Fdo)收到IRP_MJ_PNP&IRP_MN_START后,会向总线驱动(Pdo)查询并正确配置所需的硬件资源,如IO Port/IRQ/Mem等,然后才开始工作"。这段话我一直感到困惑:功能驱动的硬件资源来自总线驱动,那么总线驱动的资源由谁提供?可能有人会这样释疑:由物理总线仲裁分配。
A2."设备驱动从D0进入D3,保存设备上下文以后,设备(特指硬件上的设备)将进入D3 Sleep状态"。这段话就更困惑了,为什么保存设备上下文(往往是驱动层面上的上下文)后,硬件就能下电了?
A3.OS进入Sleep后,当支持唤醒功能的设备收到中断信号后,唤醒OS。其实,进入Sleep状态后,CPU不会运行任何指令,OS和驱动程序不可能执行;另外,OS作为驱动程序的载体,不可能驱动程序先于OS提前醒来并唤醒OS的情况(驱动程序在梦游?)。那么,所谓的驱动程序Wakeup功能又是指什么?
上面有些问题看似很难有答案,另一些问题的答案又回答的很模糊,像是顺着设备堆栈的思路,把问题抛给更底层。这种不能释怀的情绪伴随了很多年(我简直是带着问题工作!),直到我转做Bios一段时间,并无意中得到xp泄露的源码后,我觉得我可能释疑了。所以打算用几篇文章输出一下自己的观点。本文暂不回答上面3个问题,只用于引入文章的主角----作为沟通OS和Bios的桥梁,ACPI.sys(它是windows对ACPI spec中反复出现的OSPM的平台实现)。windows通过ACPI.sys,借助BIOS的实现上述功能A1/A2;GPIO信号跳变后,触发SCI中断,引起Bios执行平台唤醒功能,并在此过程中重新加载OS,最终实现上述功能A3。ACPI如此重要,值得我们好好研究。本文从两个侧面介绍win7 x86上开启ACPI的过程:Bios侧和windows侧。
根据ACPI spec(Version 6.2),FADT表中的SMI_CMD和ACPI_ENABLE与ACPI开启有关:
如spec所述,Bios在初始化过程中配置完包含SMI_CMD和ACPI_ENABLE(重点强调,这是Bios设置的,相当于提供给OS的接口。对于同一台机器不论装什么OS,这个值是固定的)的整个ACPI table,然后加载到内存。OS在引导过程中会向SMI_CMD指定的端口写入ACPI_ENABLE设定的值,触发SMI中断;SMI中断触发后会进入Bios的SMM Handler,由Bios执行开启ACPI所需的设置。到vm上看下这两项的设置:
SMI_CMD是0xB2,这是一个特殊值,Intel PCH spec上对这个端口有详细说明:当APMC_En bit enable后,往APM_CNT,就是RW中的0xB2写入数值就会引起SMI中断。下面截图选自Intel PCH spec:
UEFI框架中会用下列形式注册SMI Handler,当往0xB2端口写入SwSmiInputValue时,就会触发SMI中断,并执行SMI处理函数EnableAcpiCallback(附注,SMI处理函数的运行对于OS而言是透明的,这句话读者自己细品)
同理,Bios为了支持windows开启ACPI,也会以同样的方式注册SMI处理函数。对于VMware,它注册的值是0xF0,注册后,往ACPI table Fadt->ACPI_Enable域写入0xF0,之后默默的等windows开启ACPI。
debug前的准备工作:
根据泄露的windows xp源码,可以发现Xp时代,开启ACPI的相关代码位于ACPIEnableEnterACPIMode 函数中:
不过这段代码来自Xp,我们调试的目标是win7,调试前得先确认下这部分代码是否存在变化:
当触发断点后,看下调用堆栈等信息,可以看出此时OS处于初始化Phase1阶段。
另外,调用堆栈Frame#06有个熟悉的函数调用:IofCallDriver,参数显示的DeviceInst:ACPI_HAL\PNP0C08,它就是ACPI spec中指定ACPI.sys(OSPM)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-12-29 17:50
被hyjxiaobia编辑
,原因: