入口函式原型 (PeiApi.h @ EDK):
typedef
EFI_STATUS
(EFIAPI *EFI_PEIM_ENTRY_POINT)(
IN EFI_FFS_FILE_HEADER * FfsHeader,
IN EFI_PEI_SERVICES **PeiServices
);
比較常用到的幾個函數:
(**PeiServices).LocatePpi
這個函數要注意的就是 PEI Phase 下 PPI 的存放和 DXE 下存放 PROTOCOL 略有不同,因為 PEI FOUNDATION 會用來存放 PPI DB,所以很有可能一個 DB 裡 N 個 INSTANCE,第三個參數就是讓你選第幾個 INSTANCE 用的,第一個為 0,以此類推。
Status = (**PeiServices).CreateHob (
PeiServices,
EFI_HOB_TYPE_XXX,
(UINT16) (sizeof (EFI_HOB_GUID_TYPE) + PrivateDataSize),
(VOID**) &HobBuffer
);
這個函數要注意的地方就是 CreateHOB 他只幫你弄個 HEADER 出來,要塞 HOB 資料,要自己拉空間出來,算好 OFFSET 是要件。
函數成功的話,HobBuffer 就會指一塊空間出來給你。
(**PeiServices).CopyMem (
(VOID*)(((UINT8*) HobBuffer) + sizeof (EFI_HOB_GUID_TYPE)),
(VOID*) PrivateData,
(UINTN) PrivateDataSize
);
再來也是算 OFFSET 把資料 COPY 進去,就行了。
DXE Phase 下:
EFI_HOB_HANDOFF_INFORMATION_TABLE *HobList;
LibGetSystemConfigurationTable (&gEfiHobListGuid, (VOID**) &HobList);
藉此取得 HOBLIST,然後再用想對應的函數把你要的 HOB 拉出來就行了,如果我上面 HOB 的 TYPE 是 EFI_HOB_TYPE_GUID_EXTENSION,同時這個 HOB.Name 有加以指定一個 GUID,那我在 DXE Phase 下就能使用下列函數把 HOB 倒出來:
GetNextGuidHob ((VOID**) &HobList, &HobNameGuid, (VOID**) &HobContent, &BufferSize);
關於為什麼 PEI Service Table 要做成雙指標,
在 BIOSREN 論壇 srcore 兄做了很漂亮的解釋,這邊借轉載並改成台灣用語:
『這個問題細究起來有點意思。這裡頭有歷史原因,其實依現在看來,定義成直接指標就可以了。』
『PEI 階段中,在 Main Memory 可用之前,PEI Core 和 PEIM 都必須在 flash 上 XIP。XIP 就意味著全域變數只能讀,不能寫。所以PEI Core在 memory 可用並且把自己 Shadow 到 memory 之前,是不可以用全域變數保存自己在 Stack 中分配的 PrivateData 的位址的。』
『這就是說我們必須有一種方法能讓 PEI CORE 在 PEIM 調用 PEI Service 的時候,能從傳進來的指向 PEI Serivces Table 的間接指標得到 PEI CORE 自己私有資料的位址。』
『當初,規範定義 EFI_PEI_SERVICES ** 是期望如下的 PEI CORE 的實現:舉例說明』
typedef struct { ...
EFI_PEI_SERVICES *PS,
...
} PEI_CORE_DATA;
『PEI_CORE_DATA 在 Stack 上分配,PS 域被初始化為 PEI Core 資料段中 PEI Service Table 的指標。』
『&PEI_CORE_DATA.PS 不就是EFI_PEI_SERVICES ** 嗎?通過 CR 就可能得到 Stack 上 PEI_CORE_DATA 的地址。』
『注意,這裡有個前提,就是 PEI Service Table 中所有域的值都是固定的,作為一個 PEI Core 的全域變數存放在資料段中。』
『對,這就是定義 EFI_PEI_SERVICES ** 的由來。』
『有人會問,如果PEI CORE按照如下實現:』
typedef struct {
...
EFI_PEI_SERVICES PSTable,
...
} PEI_CORE_DATA;
『就是說在 PEI Core 的 PrivateData 中定義一份 PEI Service Table 而不是指向它的指標,PEI Core 在初始化的時候將自己資料段中 PEI Service Table 作為範本拷到 PEI_CORE_DATA.PSTable 中。』
『這樣,就只用定義 EFI_PEI_SERVICES * (即 &PEI_CORE_DATA.PSTable)就可以了。確實如此,但規範仍然定義 EFI_PEI_SERVICES **,是考慮到 PEI Service Table 是固定的,可以直接放在 flash 上 PEI Core image 的資料段裡,PEI CORE 私有資料裡有一個指標指向它就可以了,這樣可以省掉一些對 temporary memory 的佔用,畢竟 temporary memory 的資源還是比較寶貴的。』
『瞭解了它的最初的設計意圖,那為什麼說回過頭來看,其實定義成直接指標就可以了?』
『因為 PEI CIS 規範的修訂使得 PEI Service Table 裡面的值不再是 build time 決定之後就不變的了。PEI CIS 0.91 在 PEI Service Table 加入了 CPU IO PPI 和 PCI CFG PPI 的指標。這兩個指標必須在執行時填入,所以 PEI Service Table 不能再放在 flash 上了,它必須被放到 memory 裡,這樣它才能被修改。』
『所以現在PEI CORE的實現一般是這樣:』
typedef struct {
...
EFI_PEI_SERVICES *PS,
...
...
EFI_PEI_SERVICES PSTable,
...
} PEI_CORE_DATA;
『PEI Core 在初始化的時候將自己資料段中 PEI Service Table 作為模組拷到 PEI_CORE_DATA.PSTable 中,然後 PEI_CORE_DATA.PS = &PEI_CORE_DATA.PSTable。』