序
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:
圖1.
圖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裝置的配置空間:
圖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:
圖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的輸入線,另外又引出一根地線:
圖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寄存器:
圖6.
圖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檢視配置結果:
圖8. GPI_GPE_EN_GPP_E_22的配置
圖9. PAD_CFG_DW0_GPPC_E_22的配置
這種驗證方式并不是很直覺,如果讀者有"Intel GPIO Configuration"工具,可以以報表的形式,獲得更直覺的結果:
圖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):
圖11.
對于Whisky Lake平台,預設的映射關系是GPP_E-->GPE_DW2/None-->GPE_DW1/GPP_B-->GPE_DW0(這種映射關系不能随意改變,改變映射關系可能不會生效)
圖12.MISCCFG配置
2.配置PMC
理論上應該配置被GPPC_E22映射的用于觸發PMC GPE_EN的寄存器:GPE_EN_95_64,但實際測試過程中發現不去置位GPE_EN_95_64:22也能觸發SCI中斷。
圖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:
圖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引腳,不會有這條語句輸出:
圖14.GPP_E22處于高電平時,Bios輸出
圖15.GPP_E22拉低電後,Bios輸出
總結:
Tiger Lake Platform Controller Hub (PCH) BIOS Specification概括了GPIO SCI的步驟:
參考:
聲明:雖然我基于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;