天天看點

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

    2010年畢業那會,我首次接觸到51單片機,當時發現51單片機如此簡單卻功能完備:盡管它沒有搭載OS,卻能接收GPIO Port上的裝置事件并反過來控制Port上的裝置。反觀基于x86體系的PC,雖然功能強大,但終端使用者卻因為下列限制,很難控制主機闆上的裝置(小到自己往主機闆上的裝一個按鍵):

a.主機闆廠商本着能省則省的原則,未必會在主機闆上保留白閑的GPIO Port;退一步,即使廠商出于某種原因,保留了空閑的GPIO Port,終端使用者沒有主機闆回路圖,也無法找到這些GPIO Port;

b.即使使用者僥幸找到了GPIO Port,但是GPIO Port是由主機闆廠商在Bios中設定的,普通使用者無法修改這些設定;另外,Intel僅向IBV/OEM等它的合作廠商提供PCH spec,除非發生了類似2020年7月Intel洩露了30G相關資料,否則終端使用者無法對外設進行準确的設定;

c.OS足夠強大,以至于抽象了底層硬體實作(OS的透明性),這是積極的一面。但是反過來,正是因為OS的透明性,位于Ring3層的使用者或者App很難通路硬體。

以上種種,無不是PC行業整體給初學者添加的重重壁壘。而我,由于機緣巧合,有機會在這些壁壘上自底向上的(Bios--Acpi.sys/OS--驅動--App)打了一串小孔,進而能在Ring3層接受來自硬體層的GPIO中斷。本文基于Intel Whiskey Lake平台,總結了這種自上而下的(非标準)通路方式,也算是我對畢業這麼多年的一個回顧~

0.神秘的P2SB接口

PCH EDS Vol2中規定,Intel PCH通過P2SB(Primary to Sideband Bridge)接口,以下列公式通路GPIO Pad:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖1.

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖2

而P2SB接口是PCH上的PCIe裝置,在Whisky Lake平台上,它位于B:0/D:31/F:1。看到這,你的第一反應一定是計算P2SB的配置空間位址,然後通過​​MMIO方式​​獲得SBREG_BAR。但是P2SB和PMC Controller(B:0/D:31/F:2)一樣神秘:即使已知Bus\Device\Function Number,也無法通路該PCIe裝置的配置空間:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖3.MMIO方式讀取3種PCIe裝置的配置空間的結果(Address值0xE0000000,0xE00F9000,0xE00FA000分别對應PCIe裝置B:0/D:0/F:0 B:0/D:31/F:1和B:0/D:31/F:2 )。無法獲得P2SB的配置空間,進而無法獲得P2SB Base Address(B:0/D:31/F:1/Reg 0x10),是以本文結束!

當然不會結束,我開玩笑的。可以從Intel提供給IBV\OEM的Intel\WhiskLakeSiliconPkg目錄下找到(讀者可以在Intel洩露的30G資料裡找到KabyLakeSiliconePkg~ )SBREG_BAR和通過P2SB接口通路各類寄存器的宏定義:

/**
  Definition for PCR address
  The PCR address is used to the PCR MMIO programming
**/
#define PCH_PCR_ADDRESS(Pid, Offset)    (PCH_PCR_BASE_ADDRESS | ((UINT8)(Pid) << 16) | (UINT16)(Offset))

#define PCH_PCR_BASE_ADDRESS              0xFD000000     ///< SBREG MMIO base address      

這個宏定義可以視為對圖1中紅線部分的補充說明。

PortID

不同于51單片機通路GPIO Port,Intel PCH視GPIO為GPIO Pad(可以将GPIO Pad了解為主機闆上的焊點,當然Pad本身也有焊盤的意思。焊點GPIO Pad到真正的GPIO Port之間還有不為人知的回路)。同時,Intel PCH将若幹實體記憶體上相近的GPIO Pad劃到同一個GPIO Community(GPIO組),以便于通路,并賦予各個GPIO Community平台唯一的Port ID。各個平台定義的Port ID不同,需要查詢EDS Vol1,下圖為Whisky Lake定義的Port ID:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖4.本文用到的是GPIO community 4 GPP_E,Port ID:6A

至此,鋪墊已經完成,開始正題:配置Bios使GPIO在OS下觸發SCI Event需要3步(把大象關進冰箱要幾步?也需要三步。):

1.配置GPIO,使其能觸發GPIO SCI event;

2.配置PMC;

3.ACPI響應SCI event;

1.配置GPIO,使其能觸發GPIO SCI event

這一步分3小步:

1.1.選擇GPIO pin

如果讀者能獲得主機闆的GPIO table(其實讀者幾乎沒可能獲得OEM廠商的GPIO table這類内部資料,這就是我在文章開頭說的障礙之一),那麼從中挑選一個空閑的GPIO pin并不是難事。這篇文章使用的主機闆,OEM廠商設定GPP_E22是一個弱上拉信号,輔助Bios判斷主機闆上喇叭類型(品牌喇叭/雜牌喇叭),我琢磨着這類引腳應該能随意折騰,于是外接了一根引線作為GPP_E22的輸入線,另外又引出一根地線:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖5.

1.2.将GPIO Pin配置為Edge/Level觸發&Enable GPIO SCI

Bios通過Pad Configuration DW0 寄存器設定來控制GPIO pin的觸發方式以及中斷觸發類型(SCI/SMI/NMI/IOxACPI)等屬性,如果将中斷觸發類型設定為SCI,還需要配置GPI General Purpose Events Enable寄存器:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖6.

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖7.

我已經提前在主機闆上按如下方式配置這2個寄存器,

{GPIO_CNL_LP_GPP_E22, {GpioPadModeGpio,     GpioHostOwnAcpi,   GpioDirIn,       GpioOutDefault,       GpioIntSci | GpioIntLevel,             GpioPlatformReset,  GpioTermNone,     GpioPadConfigUnlock                           }},      

為了驗證配置結果,可以将GPI_GPE_EN_GPP_E_22的偏移(Offset:0x164)和PAD_CFG_DW0_GPPC_E_22的偏移(Offset:0x8E0)作為宏PCH_PCR_ADDRESS的參數分别計算出它們的位址,然後用RW檢視配置結果:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖8. GPI_GPE_EN_GPP_E_22的配置

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖9. PAD_CFG_DW0_GPPC_E_22的配置

這種驗證方式并不是很直覺,如果讀者有"Intel GPIO Configuration"工具,可以以報表的形式,獲得更直覺的結果:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇
ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖10.Intel GPIO Configuration輸出結果

1.3.将GPIO Community映射到PMC GPE event

根據PCH spec可知,GPE頂層中斷源共有128個,PCH外部中斷将中斷信号映射到GPE頂層中斷源後,才有機會觸發中斷。僅僅完成上面的配置還不足以觸發GPE SCI中斷,還需要建立從GPIO到GPE的映射。建立這種映射依賴GPIO Community的Miscellaneous Configuration寄存器(MISCCFG:Offset 10):

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖11.

對于Whisky Lake平台,預設的映射關系是GPP_E-->GPE_DW2/None-->GPE_DW1/GPP_B-->GPE_DW0(這種映射關系不能随意改變,改變映射關系可能不會生效)

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖12.MISCCFG配置

2.配置PMC

理論上應該配置被GPPC_E22映射的用于觸發PMC GPE_EN的寄存器:GPE_EN_95_64,但實際測試過程中發現不去置位GPE_EN_95_64:22也能觸發SCI中斷。

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖12. 

 前面也說過我們無法通過MMIO獲得PMC Controller PCIe配置空間的值,更無法獲得ACPI Power managerment IO register的位址,是以最終還是要到Intel代碼中獲得:

TOKEN
    Name  = "ACPI_BASE_ADDRESS"
    Value  = "0x1800"
    Help  = "ACPI Base Address(ABASE), D31F2, Offset 40h"
    TokenType = Integer
    TargetH = Yes
End      

 由此可知,ACPI Power managerment IO register(ABASE)的偏移位址為0x1800:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖13.ACPI Power managerment IO register實體位址輸出.

3.ACPI響應SCI event

ACPI命名空間\_GPE下_Lxx\_Exx Control Method用于接收和處理SCI event,_L/_E分别指電平觸發和邊緣觸發,而xx(16 進制)是128個GPE中斷源中的編号。本文中GPPC_E Port映射到GPE_96_64(GPPC_E22映射到了GPE_86),是以我們需要在\_GPE下添加_L56 Control Method:

Intel\CometLakePlatSamplePkg\Acpi\AcpiTables\Dsdt\Gpe.asl

  Scope(\_GPE)
  {
    Method (_L56, 0, Serialized){
        DSTR("l-event _L56"); //列印日志
    }
  }      

重新編譯和燒錄Bios,看下Bios輸出結果

當GPIO pin處于高電平時(pin在上拉狀态下),有大量的"l-event _L56"輸出;當GPIO pin搭上Gnd引腳,不會有這條語句輸出:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖14.GPP_E22處于高電平時,Bios輸出

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

圖15.GPP_E22拉低電後,Bios輸出 

總結:

Tiger Lake Platform Controller Hub (PCH) BIOS Specification概括了GPIO SCI的步驟:

ACPI.sys,從Windows到Bios的橋梁(2):Windows應用程式響應GPIO(SCI)裝置中斷 Bios篇

參考:

聲明:雖然我基于Whiskey Lake平台實作了對硬體的通路,但是該平台的PCH spec以及Bios writer guide不能公開,讀者隻能用已流落在民間的Skylake Lake平台相關文檔替代。盡管Sky Lake和Whiskey Lake兩個平台存在差異,但并不影響整體思路。

a.<Intel® 100 Series Chipset Family PCH Datasheet, Vol. 1 > (簡稱PCH EDS Vol1)/<Intel® 100 Series Chipset Family PCH Datasheet, Vol. 2> (簡稱PCH EDS Vol2), 這兩份是Intel Skylake PCH spec,規定了PCH的寄存器偏移/預設值等;

b.<Tiger Lake Platform Controller Hub (PCH) BIOS Specification>, 這一份是Intel Tiger Lake bios writer guide,規定了如何在Bios中配置GPIO;