從Windows到Bios的橋梁:Windbg跟蹤Win7開啟ACPI

2021-02-23 看雪學院

本文為看雪論壇優秀文章

看雪論壇作者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的情況(驅動程序在夢遊?)。上面有些問題看似很難有答案,另一些問題的答案又回答的很模糊,像是順著設備堆棧的思路,把問題拋給更底層。這種不能釋懷的情緒伴隨了很多年(我簡直是帶著問題工作!),直到我轉做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而言是透明的,這句話讀者自己細品)
  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。
bcdedit /debug onbcdedit /bootdebug on ;不開這個,windows開啟ACPI的瞬間,windbg和目標機的調試會話可能會被重置

根據洩露的windows xp源碼,可以發現Xp時代,開啟ACPI的相關代碼位於ACPIEnableEnterACPIMode 函數中:
//busdrv\acpi\driver\shared\acpienbl.cVOIDACPIEnableEnterACPIMode (    VOID    );/*++Routine Description:    This routine is called to enter ACPI modeArguments:    NoneReturn Value:    None--*/

不過這段代碼來自Xp,我們調試的目標是win7,調試前得先確認下這部分代碼是否存在變化:
kd>x ACPI!ACPIEnableEnterACPIMode ;搜索函數名ACPI!ACPIEnableEnterACPIMode      ;居然函數還在,看來Win7還是用這個函數開啟ACPIkd> bp acpi!ACPIEnableEnterACPIMode ;那就下斷點kd> gBreakpoint 0 hitACPI!ACPIEnableEnterACPIMode:88cdbce0 8bff            mov     edi,edi

當觸發斷點後,看下調用堆棧等信息,可以看出此時OS處於初始化Phase1階段。
kd> gBreakpoint 0 hitACPI!ACPIEnableEnterACPIMode:88cc2ce0 8bff            mov     edi,edikd> kb # ChildEBP RetAddr  Args to Child              00 8078b644 88cc2e25 00000000 8078b66c 88cfde24 ACPI!ACPIEnableEnterACPIMode01 8078b650 88cfde24 00000000 82a0f940 88cf5ae0 ACPI!ACPIEnableInitializeACPI+0x1f02 8078b66c 88cbe556 87d4cc60 87e548a0 00000000 ACPI!ACPIInitialize+0xe203 8078b69c 88d050c2 87d4cc60 886e81a0 88d04f38 ACPI!ACPIInitStartACPI+0x6a04 8078b6c8 88cb927e 87d4cc60 886e8100 87d4cc60 ACPI!ACPIRootIrpStartDevice+0x18a05 8078b6f8 82a7c11a 87d4cc60 87e548a0 886e8258 ACPI!ACPIDispatchIrp+0x13a06 8078b718 82f8f531 00000000 87e535d8 87d4b008 nt!IofCallDriver+0x7e07 8078b734 82a89506 8078b76c 82a892ea 87d4e4a0 nt!PnpAsynchronousCall+0x10908 8078b7a0 82f976be 82a892ea 87d4e4a0 00000000 nt!PnpStartDevice+0x18409 8078b7fc 82f96b27 00000012 00000000 87d4e4a0 nt!PnpStartDeviceNode+0x2a60a 8078b818 82f8b0b8 00000000 00000000 00000000 nt!PipProcessStartPhase1+0x870b 8078ba14 82a87f83 87df5748 00000000 8078ba50 nt!PipProcessDevNodeTree+0x1cc0c 8078ba5c 82a87e4a 00000000 87d4cab8 00000000 nt!PnpDeviceActionWorker+0x1290d 8078ba74 832b06ba 00000000 00000000 00000000 nt!PnpRequestDeviceAction+0x15e0e 8078baec 832ad52b 8080f8c0 00000000 80810e50 nt!IopInitializeBootDrivers+0x4140f 8078bb6c 832a29b7 8080f8c0 87de25e0 87de2270 nt!IoInitSystem+0x59310 8078bc4c 82ecd012 8078bc90 8313d0da 8080f8c0 nt!Phase1InitializationDiscard+0xd6711 8078bc54 8313d0da 8080f8c0 0010209f 00000000 nt!Phase1Initialization+0xd12 8078bc90 82be6555 82ecd005 8080f8c0 00000000 nt!PspSystemThreadStartup+0x17813 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   ObjectName  87d4cc60  \Driver\ACPI       87e548a0  > 87e535d8  \Driver\ACPI_HAL   87e53690  0000006c!DevNode 87d4b008 :  DeviceInst is "ACPI_HAL\PNP0C08\0"  ;<---PNP0C08  ServiceName is "ACPI"

繼續往下調試,會看到OS從全局變量ACPI!AcpiInformation+0x04處獲得信息,感覺ACPI!AcpiInformation是個結構體指針:
kd> pACPI!ACPIEnableEnterACPIMode+0x81:88cdbd61 a194bbd088      mov     eax,dword ptr [ACPI!AcpiInformation (88d0bb94)]  kd> u ACPI!ACPIEnableEnterACPIMode+0x818bf8            mov     edi,eaxa1942bcf88      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 _ACPIInformationACPI!_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是個宏定義,其實現如下。
#define WRITE_ACPI_REGISTER(AcpiReg, Register, Value) ((*AcpiWriteRegisterRoutine)((AcpiReg), (Register), (Value)))  PWRITE_ACPI_REGISTER AcpiWriteRegisterRoutine = DefWriteAcpiRegister;

換言之,Windows通過調用是DefWriteAcpiRegister來寫0xB2寄存器,下面代碼段我只貼出和SMI_CMD相關的代碼:
VOIDDefWriteAcpiRegister(    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 PM1x               registers.      Value - Data to write.  Return Value:      None.  --*/{    switch (AcpiReg) {..        case SMI_CMD:            WRITE_PORT_UCHAR((PUCHAR)AcpiInformation->SMI_CMD, (UCHAR)Value);            break;          default:            break;    }}

看雪ID:hyjxiaobia

https://bbs.pediy.com/user-home-399589.htm

  *本文由看雪論壇 hyjxiaobia 原創,轉載請註明來自看雪社區。

相關焦點

  • Win7系統提示:此Windows副本不是正版的幾種解決方法
    一些win7系統的電腦用戶會遇到,打開電腦之後,電腦桌面壁紙變成黑色,同時,電腦桌面的下角提示:「Windows7 內部版本7600
  • Win7:如何從Windows 7免費升級至Windows 10
    但是,隨著技術的不斷更新迭代,更安全、更高效、更強大的win10面世後,歷經10年歷史的win7在今天終於宣布了正式退役。那這是不是意味著,我們的電腦以後就不能用win7系統了呢。嚴格來說不然,微軟官方給出的公告是:從今天起,你的電腦仍可以使用win7系統,但是後續是沒有軟體更新和安全保障的,電腦遭受病毒和惡意軟體攻擊的風險會加大。
  • 再次提醒:快去免費升級你的windows電腦!
    win7、win8不能直接升級    2.沒有激活的小夥伴請使用這個KMS工具激活,安裝完成之後,聽到語音提示program compeled即可。KMS工具:https://share.weiyun.com/dfc11ba2dc7d5c8023444189b339b9a5    3.win10官方升級地址為:https://www.microsoft.com/zh-cn/accessibility/windows10upgrade    4.打開這個網址,你會下載一個升級器,為英文軟體,但系統升級之後是中文系統。
  • 清除系統毒瘤:新裝win10必做的設置關閉windows安全中心卸載Windows Defender
    同時相信我們大部分的小夥伴都會在電腦安裝360、火絨或者騰訊管家之類的安全軟體,所以確實沒有不要讓windows安全中心來佔用我們的系統資源。方法一:臨時關閉這個方法適用於只是為了激活系統,或者安裝一些特殊的軟體,暫時關閉一下。
  • 一鍵U盤啟動:安裝純淨版win7系統教程
    1、電腦裝機啟動U盤一個點擊查看U盤啟動製作方法2、下載win7鏡像文件win7.GHO (64位)鏡像文件下載:連結:https://pan.baidu.com/s/1c167UDi 密碼:guq3win7.GHO  (32位)鏡像下載:連結:https://pan.baidu.com/s/1gfB1Kxd 密碼:23n1 下載提示:由於百度網盤自身原因,第一次打開可能會提示頁面不存在,再次打開或刷新頁面即可。
  • C型橋梁伸縮縫@C型橋梁伸縮縫簡介@C型橋梁伸縮縫原則
    18330875558 C型橋梁伸縮縫@C型橋梁伸縮縫簡介@C型橋梁伸縮縫原則 GQF-C型橋梁伸縮縫
  • 放棄臺北高薪到臺南,我現在每月休假19天
    圖/biosmonthly.com這兩年他一面調整自己的工作形態,一面和臺南正興街眾人一起創辦「全球視野最窄」雜誌《正興聞》、和日本接力舉辦不容小覷的「辦公椅滑行大賽」、認同無用才能重獲自由的「無用生活節」、不再總是推崇積極改建,而是學會好好和一個地方說再見的「神隱廢墟告別市集」⋯⋯正興幫的主要聯絡人高耀威——不分上下班——
  • [MSDN]最新Win7 SP1簡體中文所有版本下載[親測可下!]
    2008 R2 Service Pack 1 (x86, x64, ia64) - DVD (Chinese-Simplified)文件名 cn_windows_automated_installation_kit_for_windows_7_and_windows_server_2008_r2_sp1_x86_x64_ia64_dvd_619619.iso發布日期 (UTC):
  • iOS 14新功能:限制App廣告跟蹤!
    因此,在iOS 14中,我們讓你對自己分享的數據有更多主控權,也能更清楚地了解到這些數據的用途。例如之前iOS 14加入的剪切板提醒、麥克風、攝像頭開啟提醒、照片讀取範圍權限、模糊定位、App Store標明隱私權限等等。這些新功能可以幫助用戶了解當前有哪些軟體正在讀取自己的信息,這對於注重隱私的用戶來說十分的重要。
  • Windows 10版面大升級,網友:竟然感覺有些好看
    其實在視頻中,不只以預告升級版windows 10為主,微軟還在視頻中帶大家回顧了windows系統的發展史。就如同視頻裡所展示的,windows已經經歷了無數個版本更替。從1985年最初的Windows 1.0,到現在風靡全球的Windows XP、Windows 7、Windows8和當下正在使用的windows10,每個版本都給用戶們留下了深刻的回憶。
  • 【敦樸橋梁資料庫】橋梁災難與事故(1月5日)
    1. 1975年1月5日,澳大利亞霍巴特的塔斯曼大橋(Tasman Bridge)被貨輪撞擊,造成橋梁三跨127m落水,船上12名人員死亡,船體沉沒。
  • SQL Server 2017 正式發布:同時支持 Windows 和 Linux
    來自:開源中國社區連結:https://www.oschina.net/news/89263/sql-server-2017-on-windows-linux-and-docker-ga例如,SQL Server 中的 Adaptive Memory Grants 跟蹤並了解對給定的查詢使用了多少內存,以調整內存的使用。Automatic Plan Correction 通過查找和修正性能的回歸來確保持續的性能。
  • 直接下載:Windows 10正式版官方原版鏡像!
    【64位簡體中文家庭/專業版】文件名:cn_windows_10_multiple_editions_x64_dvd_6848463.iso體積:4.01GBSHA1:C71D49A6144772F352806201EF564951BE55EDD5快科技網盤下載:http://pan.baidu.com/s/1dDGZ59B
  • Windows To Go:隨身攜帶、即插即用的Windows作業系統
    通過這件事我知道Windows To Go有三個功能:安裝、修復系統;別人電腦系統出了問題,插上就可以對損壞系統鏡像修復;可以聯網玩遊戲,和windows安裝系統同樣操作;工作中我們同樣可以使用這個系統,回到家裡插在自己電腦上繼續工作;把U盤查找其他電腦,同樣可以運行,走到哪裡就可以帶到哪裡,相當於私人獨家系統
  • Windows App Studio 更新:新增9種語言及多項功能
    新增功能:Windows Phone 8.1 Sideloading:選擇通用應用,導出時開啟「可安裝包」,可以直接在手機上部署,快速測試應用。9 種新語言:App Studio 官網、幫助文檔、.appx 應用生成已經支持 10 種語言,包括簡繁體中文、英文等。
  • 世界「最恐怖」橋梁:每天上萬人找代駕,老司機嚇到臉色大變!
    近幾年來,我國建造了很多華麗的箭鏃、橋梁,還有的跨海、還有的被人們稱為世界最恐怖的橋梁。在人們的心中,中國的建造的橋梁都是特別偉大的,為人們的出行是便利了不少,近期開通的跨海「港珠澳大橋」就是一個典型的例子,人們可以自己開車,跨個海就可以抵達了,人們前往旅遊也是更加的方便。由於它設立在海中,有很多人還是認為恐怖,擔憂的。不過,大家知道嗎?
  • 高速防眩網,防眩網,公路防眩網,高速公路防眩網,橋梁防眩網,高速橋梁防眩網
    高速防眩網的優點及特性:此種網大多採用鋼板網、異型管、邊耳、圓管焊接成,連接附件與熱鍍管支柱固定,防眩性能極好,多用於公路、高速公路、鐵路、橋梁、建築工地、小區、廠、飛機場、體育場綠地等地區,起到防眩與防護的作用;另有防眩隔離柵、圍欄、網欄等名稱;具有外形美觀,風阻少,經過鍍鋅塗塑雙塗層能延長使用壽命,減少維護費用,且安裝便利,不易損壞,接觸面少,不易沾塵能長久保持整潔、規格多樣等特點