点击上方蓝色“终端研发部”,选择“设为星标”
学最好的别人,做最好的我们

本文为看雪论坛优秀文章

看雪论坛作者ID:hyjxiaobia

由于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侧。

Part 1. Bios侧对开启ACPI的支持

根据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而言是透明的,这句话读者自己细品)

  SwContext.SwSmiInputValue = (UINTN) PcdGet8 (PcdAcpiEnableSwSmi);Status = SwDispatch->Register (SwDispatch,EnableAcpiCallback,&SwContext,&SwHandle);

同理,Bios为了支持windows开启ACPI,也会以同样的方式注册SMI处理函数。对于VMware,它注册的值是0xF0,注册后,往ACPI table Fadt->ACPI_Enable域写入0xF0,之后默默的等windows开启ACPI。

新Part 2. Windows侧开启ACPI

debug前的准备工作:

bcdedit /debug on
bcdedit /bootdebug on ;不开这个,windows开启ACPI的瞬间,windbg和目标机的调试会话可能会被重置

根据泄露的windows xp源码,可以发现Xp时代,开启ACPI的相关代码位于ACPIEnableEnterACPIMode 函数中:

//busdrv\acpi\driver\shared\acpienbl.c
VOID
ACPIEnableEnterACPIMode (VOID);
/*++
Routine Description:This routine is called to enter ACPI mode
Arguments:None
Return Value:None
--*/

不过这段代码来自Xp,我们调试的目标是win7,调试前得先确认下这部分代码是否存在变化:

kd>x ACPI!ACPIEnableEnterACPIMode ;搜索函数名
ACPI!ACPIEnableEnterACPIMode      ;居然函数还在,看来Win7还是用这个函数开启ACPI
kd> bp acpi!ACPIEnableEnterACPIMode ;那就下断点
kd> g
Breakpoint 0 hit
ACPI!ACPIEnableEnterACPIMode:
88cdbce0 8bff            mov     edi,edi

当触发断点后,看下调用堆栈等信息,可以看出此时OS处于初始化Phase1阶段。

kd> g
Breakpoint 0 hit
ACPI!ACPIEnableEnterACPIMode:
88cc2ce0 8bff            mov     edi,edi
kd> kb# ChildEBP RetAddr  Args to Child
00 8078b644 88cc2e25 00000000 8078b66c 88cfde24 ACPI!ACPIEnableEnterACPIMode
01 8078b650 88cfde24 00000000 82a0f940 88cf5ae0 ACPI!ACPIEnableInitializeACPI+0x1f
02 8078b66c 88cbe556 87d4cc60 87e548a0 00000000 ACPI!ACPIInitialize+0xe2
03 8078b69c 88d050c2 87d4cc60 886e81a0 88d04f38 ACPI!ACPIInitStartACPI+0x6a
04 8078b6c8 88cb927e 87d4cc60 886e8100 87d4cc60 ACPI!ACPIRootIrpStartDevice+0x18a
05 8078b6f8 82a7c11a 87d4cc60 87e548a0 886e8258 ACPI!ACPIDispatchIrp+0x13a
06 8078b718 82f8f531 00000000 87e535d8 87d4b008 nt!IofCallDriver+0x7e
07 8078b734 82a89506 8078b76c 82a892ea 87d4e4a0 nt!PnpAsynchronousCall+0x109
08 8078b7a0 82f976be 82a892ea 87d4e4a0 00000000 nt!PnpStartDevice+0x184
09 8078b7fc 82f96b27 00000012 00000000 87d4e4a0 nt!PnpStartDeviceNode+0x2a6
0a 8078b818 82f8b0b8 00000000 00000000 00000000 nt!PipProcessStartPhase1+0x87
0b 8078ba14 82a87f83 87df5748 00000000 8078ba50 nt!PipProcessDevNodeTree+0x1cc
0c 8078ba5c 82a87e4a 00000000 87d4cab8 00000000 nt!PnpDeviceActionWorker+0x129
0d 8078ba74 832b06ba 00000000 00000000 00000000 nt!PnpRequestDeviceAction+0x15e
0e 8078baec 832ad52b 8080f8c0 00000000 80810e50 nt!IopInitializeBootDrivers+0x414
0f 8078bb6c 832a29b7 8080f8c0 87de25e0 87de2270 nt!IoInitSystem+0x593
10 8078bc4c 82ecd012 8078bc90 8313d0da 8080f8c0 nt!Phase1InitializationDiscard+0xd67
11 8078bc54 8313d0da 8080f8c0 0010209f 00000000 nt!Phase1Initialization+0xd
12 8078bc90 82be6555 82ecd005 8080f8c0 00000000 nt!PspSystemThreadStartup+0x178
13 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x19

另外,调用堆栈Frame#06有个熟悉的函数调用:IofCallDriver,参数显示的DeviceInst:ACPI_HAL\PNP0C08,它就是ACPI spec中指定ACPI.sys(OSPM)。

kd> !devstack 87e535d8 !DevObj   !DrvObj            !DevExt   ObjectName87d4cc60  \Driver\ACPI       87e548a0
> 87e535d8  \Driver\ACPI_HAL   87e53690  0000006c
!DevNode 87d4b008 :DeviceInst is "ACPI_HAL\PNP0C08\0"  ;<---PNP0C08ServiceName is "ACPI"

继续往下调试,会看到OS从全局变量ACPI!AcpiInformation+0x04处获得信息,感觉ACPI!AcpiInformation是个结构体指针:

kd> p
ACPI!ACPIEnableEnterACPIMode+0x81:
88cdbd61 a194bbd088      mov     eax,dword ptr [ACPI!AcpiInformation (88d0bb94)]kd> u ACPI!ACPIEnableEnterACPIMode+0x81
8bf8            mov     edi,eax
a1942bcf88      mov     eax,dword ptr [ACPI!AcpiInformation (88cf2b94)]
8b4004          mov     eax,dword ptr [eax+4] ;<---从结构体偏移取值
660fb64034      movzx   ax,byte ptr [eax+34h] ;<---ACPI!AcpiInformation+4处取得的值仍然是结构体指针
0fb7c0          movzx   eax,ax

ACPI!AcpiInformation结构体长成什么样,dump出来看一下?

顺带一提,dt的输出结果中,从ACPI!_ACPIInformation!PM1a_BLK开始到ACPI!_ACPIInformation!GpeSize都是ACPI spec中定义的Fixed Hardware Register。

kd> dt _ACPIInformation
ACPI!_ACPIInformation+0x000 RootSystemDescTable : Ptr32 _RSDT_32+0x004 FixedACPIDescTable : Ptr32 _FADT+0x008 FirmwareACPIControlStructure : Ptr32 _FACS+0x00c DiffSystemDescTable : Ptr32 _DSDT+0x010 MultipleApicTable : Ptr32 _MAPIC+0x014 GlobalLock       : Ptr32 Uint4B+0x018 GlobalLockQueue  : _LIST_ENTRY+0x020 GlobalLockQueueLock : Uint4B+0x024 GlobalLockOwnerContext : Ptr32 Void+0x028 GlobalLockOwnerDepth : Uint4B+0x02c ACPIOnly         : UChar+0x030 PM1a_BLK         : Uint4B+0x034 PM1b_BLK         : Uint4B+0x038 PM1a_CTRL_BLK    : Uint4B+0x03c PM1b_CTRL_BLK    : Uint4B+0x040 PM2_CTRL_BLK     : Uint4B+0x044 PM_TMR           : Uint4B+0x048 GP0_BLK          : Uint4B+0x04c GP0_ENABLE       : Uint4B+0x050 GP0_LEN          : UChar+0x052 Gpe0Size         : Uint2B+0x054 GP1_BLK          : Uint4B+0x058 GP1_ENABLE       : Uint4B+0x05c GP1_LEN          : UChar+0x05e Gpe1Size         : Uint2B+0x060 GP1_Base_Index   : Uint2B+0x062 GpeSize          : Uint2B+0x064 SMI_CMD          : Uint4B+0x068 pm1_en_bits      : Uint2B+0x06a pm1_wake_mask    : Uint2B+0x06c pm1_wake_status  : Uint2B+0x06e c2_latency       : Uint2B+0x070 c3_latency       : Uint2B+0x074 ACPI_Flags       : Uint4B+0x078 ACPI_Capabilities : Uint4B+0x07c Dockable         : UChar

再dump出ACPI!AcpiInformation->FixedACPIDescTable的内存,可以发现,其中smi_cmd_io_port/acpi_on_value的值和前面RW截图中显示的值一致。

kd> dx -id 0,0,ffffffff87de2548 -r1 ((ACPI!_FADT *)0xffd07010)
((ACPI!_FADT *)0xffd07010)                 : 0xffd07010 [Type: _FADT *][+0x000] Header           [Type: _DESCRIPTION_HEADER][+0x024] facs             : 0x3fefffc0 [Type: unsigned long][+0x028] dsdt             : 0x3fee1652 [Type: unsigned long][+0x02c] int_model        : 0x0 [Type: unsigned char][+0x02d] pm_profile       : 0x0 [Type: unsigned char][+0x02e] sci_int_vector   : 0x9 [Type: unsigned short][+0x030] smi_cmd_io_port  : 0xb2 [Type: unsigned long]   ;<---SMI_CMD[+0x034] acpi_on_value    : 0xf0 [Type: unsigned char]   ;<---ACPI_ENABLE[+0x035] acpi_off_value   : 0xf1 [Type: unsigned char]

之后,继续跟踪代码运行,Windows通过调用WRITE_ACPI_REGISTER,往0xB2写入0xf0,触发SMI中断,调用前面提到的Bios Smi Handler,在硬件上开启ACPI。

注意WRITE_ACPI_REGISTER是个宏定义,其实现如下。

//busdrv\acpi\driver\shared\acpiio.h
#define WRITE_ACPI_REGISTER(AcpiReg, Register, Value) ((*AcpiWriteRegisterRoutine)((AcpiReg), (Register), (Value)))//busdrv\acpi\driver\shared\acpiio.c:
PWRITE_ACPI_REGISTER AcpiWriteRegisterRoutine = DefWriteAcpiRegister;

换言之,Windows通过调用是DefWriteAcpiRegister来写0xB2寄存器,下面代码段我只贴出和SMI_CMD相关的代码:

VOID
DefWriteAcpiRegister(ACPI_REG_TYPE AcpiReg,ULONG Register,USHORT Value)
/*++Routine Description:Write to the specified ACPI fixed register.Arguments:AcpiReg - Specifies which ACPI fixed register to write to.Register - Specifies which GP register to write to. Not used for PM1xregisters.Value - Data to write.Return Value:None.--*/
{switch (AcpiReg) {
..case SMI_CMD:WRITE_PORT_UCHAR((PUCHAR)AcpiInformation->SMI_CMD, (UCHAR)Value);break;default:break;}
}

至此,我们已经开启了ACPI的大门。

BAT等大厂Java面试经验总结 想获取 Java大厂面试题学习资料扫下方二维码回复「BAT」就好了回复 【加群】获取github掘金交流群回复 【电子书】获取2020电子书教程回复 【C】获取全套C语言学习知识手册回复 【Java】获取java相关的视频教程和资料回复 【爬虫】获取SpringCloud相关多的学习资料回复 【Python】即可获得Python基础到进阶的学习教程回复 【idea破解】即可获得intellij idea相关的破解教程回复 【BAT】即可获得intellij idea相关的破解教程关注我gitHub掘金,每天发掘一篇好项目,学习技术不迷路!

回复 【idea激活】即可获得idea的激活方式

回复 【Java】获取java相关的视频教程和资料

回复 【SpringCloud】获取SpringCloud相关多的学习资料

回复 【python】获取全套0基础Python知识手册

回复 【2020】获取2020java相关面试题教程

回复 【加群】即可加入终端研发部相关的技术交流群

为什么HTTPS是安全的

因为BitMap,白白搭进去8台服务器...

《某厂内部SQL大全 》.PDF

字节跳动一面:i++ 是线程安全的吗?

大家好,欢迎加我微信,很高兴认识你!

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!

喜欢就给个“在看

从Windows到Bios的桥梁:Windbg跟踪Win7开启ACPI相关推荐

  1. [原创]ACPI.sys,从Windows到Bios的桥梁(2):Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

    引言 在上一篇<ACPI.sys,从Windows到Bios的桥梁(2):Windows应用程序响应主板上GPIO(SCI)设备中断 硬件篇>只完成了2件事: 1.Bios接收GPIO S ...

  2. 如何使用WinDBG跟踪调试ASL/ACPI?

    如何使用WinDBG跟踪调试ASL/ACPI? 去年在研究ACPI的时候我有用windbg来分析一下ACPI methods,当时配置windbg的时候参考了几位前辈的文章,今天也转其中一篇过来以做备 ...

  3. 【windows内核编程】vs2013+WDK8.1+winDbg+vmware win7虚拟机联调

    [我的]vs2013+WDK8.1+winDbg+vmwarewin7虚拟机联调 作者:zcr214 时间:2016/4/8 内核驱动开发,首先要配置开发环境,目前微软已经出到了vs2015+WDK1 ...

  4. 猎豹浏览器禁止跟踪怎么开启 禁止跟踪功能开启方法

    为了防止第三方网站跟踪用户活动,包括猎豹浏览器在内的多款浏览器产品都上线了"禁止追踪"功能.不过,需要注意的是,"禁止追踪"功能是需要手动开启的!那么,该怎么操 ...

  5. win10 修复打印机服务器,Windows Update修复了打印机错误(win10/win7)

    Windows Update修复了打印机错误(win10/win7) Microsoft已于2019年10月3日发布了Windows 7至Windows 10及其服务器对应版本的大量更新.这些旨在解决 ...

  6. 装XP系统时蓝屏,出现“The BIOS in this system is not fully ACPI compliant..

    HP CQ V3700 自带的是 Vista 系统,现在要换成 XP 系统,安装 XP 时蓝屏,出现以下信息: "The BIOS in this system is not fully A ...

  7. Windows系统下,tracert路由跟踪指令详解

    在Windows系统下是执行Tracert的命令: Tracert hostname 参数说明: tracert [-d] [-h maximum_hops] [-j computer-list] [ ...

  8. Windows域策略 设置关闭事件跟踪器【全域策略生效】

    目录 windows事件跟踪器 如何通过域策略关闭呢? 开始--管理工具--组策略管理 进入组策略界面

  9. Windows从Bios启动的两种方式

    Windows有两种启动引导模式: UEFI → 对应硬盘的分区格式为 GPT(也叫GUID)格式 Legacy → 对应硬盘分区格式为 MBR Legacy模式启动BIOS的流程: 开机 → BIO ...

最新文章

  1. 团队任务4-每日例会(2018-12-6)
  2. Mars 是什么、能做什么、如何做的——记 Mars 在 PyCon China 2018 上的分享
  3. 华为发布智简全光网战略,携手上下游重新定义光产业
  4. qsort函数使用手册
  5. 访问权限修饰符( public,protected,default,private )的使用对象和作用域
  6. Mysql学习总结(53)——使用MySql开发的Java开发者规范
  7. Linux网卡配置出错无法联网-联网报错解决方法
  8. electron快捷键
  9. 线性回归中一次性实现所有自变量的单因素分析
  10. python代码图片头像_Python爬取堆糖网优美古风头像(附源码)
  11. python画聚类树状图_聚类分析python画树状图--Plotly(dendrogram)用法解析
  12. HDMI RGB_TO_DVI模块
  13. 1.2 Python环境搭建
  14. vectorvn1610报价_VECTORVN1610
  15. EasyNVS摄像机公网全终端无插件网页摄像机直播管理服务运行出现“请求服务不存在或已停止”
  16. 开发效率提升300%,Vue3新特性已成气候!
  17. 轰动众多国际电影节的著名电影短片《车四十四》
  18. 北风吹战鼓雷(实验平台的搭建)1
  19. 看了个电影《葫芦兄弟》
  20. VBA操作TXT文档

热门文章

  1. vue3实现鼠标左键拖拽画矩形框框选功能
  2. Jeecgboot报错Failed to configure a DataSource: ‘unl‘ attribute is not specified and no embedded dataso
  3. linux 内存映射-ioremap和mmap函数
  4. 如何在阿里云免费 SSL 证书到期后更新证书操作步骤
  5. [转]在线生成条形码(39码、EAN-13)
  6. 什么是远程桌面连接?如何操作远程桌面?
  7. Failed to ignore SIGHUP: No error
  8. ITASCA PFC 2D3D DISCERETE ELEMENT MODELING
  9. GO语言(golang)官方网站!
  10. 视频号|常见违规限流情况及解除方法