====== UEFI 學習 ======
[[https://uefi.org/specifications | UEFI specification download]]
{{:uefi_flow1.png|}}
* DSC -- Platform Description File, 是對整個 Package 的描述。DSC描述的是整個解決方案,再進一步切分後就是具有具體功能的工程文件。
* DEC -- Package Declaration File, 定義公開的數據和接口。意味著其他的 Package 就可以調用這些公開的內容,是UEFI接口的實現。
* INF -- INF 文件就是描述這個具體工程。
===== INF的組成 =====
^Needed Block^ Description^
|[Defines]| 定義本模塊的屬性變量及其他變量,這些變量可在工程文件其他塊中引用|
|[Sources]| 列出本模塊的所有源文件及資源文件|
|[Packages]| 列出本模塊引用到的所有包的包聲明文件。可能引用到的資源包括投文件、GUID、Protocol等,這些資源都聲明再包的包聲明文件.dec中|
|[LibraryClasses]| 列出本模塊要練接的庫模塊|
^Option Block^ Description^
|[Protocols]| 列出本模塊用到的 Protocol|
|[Guids]| 列出本模塊用到的 GUID|
|[BuildOptions]| 指定編譯和鏈接選項|
|[Pcd]| Pcd 全稱為平台配置數據庫 (Platform Configuration Database)。 [Pcd] 用於列出本模塊用到的Pcd變量,這些Pcd變量可被整個 UEFI系統訪問|
|[PcdEx]| 用於列出本模塊用到的Pcd變量,這些 Pcd變量可被整個 UEFI系統訪問|
|[FixedPcd]| 用於列出本模塊用到的Pcd編譯期常量|
|[FeaturePcd]| 用於列出本模塊用到的Pcd 常量|
|[PatchPcd]| 列出的Pcd 變量僅本模塊可用|
[[http://guidgen.com|GUID 的生成]]
1、[Defines]塊:
屬性定義語法:\\
屬性名 = 屬性值
[Defines]
INF_VERSION = 0x00010005 //必须 ,INF标准的版本号。edsk2的build会检查这个值而去解释.inf文件,最新的版本号是0x00010016,通常设置成0x00010015就行
BASE_NAME = DxeCore //必须,模块名字字符串,不能包含空格。它通常也是输出文件的名字,生成文件DxeMain.efi
MODULE_UNI_FILE = DxeCore.uni //非必须 ,字符串资源文件
FILE_GUID = D6A2CB7F-6A18-4e2f-B43B-9920A733700A //必须, 8-4-4-4-4-12格式的唯一GUID,用于生成固件,
MODULE_TYPE = DXE_CORE //必须 ,定义模块的模块类型。SEC、PEIM、DXE_DEIVER等
VERSION_STRING = 1.0 //必须, 模块的版本号字符串
ENTRY_POINT = DxeMain //必须,模块的入口函数
2、[Sources]塊:
此塊下的每一行表示一個檔,檔使用相對路徑
[Sources] //一般情况下不指定特定编译项时,使用这个即可,适用于任何体系结构
Test.c
[Sources.IA32] //编译32位模块时(build命令选项中指定了-a IA32选项),包含这个和Sources中的Test.c
Cpu32.c
[Sources.X64] //编译64位模块时,包含这个和Sources中的Test.c
Cpu64.c
[Sources]
TimerWin.c | MSFT //指定编译器有效
TimerLinux.c | GCC //指定编译器有效
3、[Packages]塊:
此塊下面列出了模組所引用到的所有包的聲明檔dec檔。如果在Sources塊內列出了原始檔案,在此塊下必須列出MdePkg/MePkg.dec,並放在此塊的首行
4、[LibraryClasses]塊:
此塊內列出了模組所要連結的所有庫模組(使用了庫的庫函數就要列出來)。庫定義在包的dsc檔中
5、[BuildOptions]塊:\\
5-1、語法:
[BuildOptions]
[編譯器家族] : [$ (Target)][Tool_CHAIN_TAG][$ (Arch)]_[CC | DLINK]_FLAGS [= |==]選項
5-2、編譯器家族
可以是MSFT(Visual Studio編譯器家族)、INTEL(Intel編譯器家族)、GCC(Gcc編譯器家族)
和 RVCT(ARM編譯器家族)中的一個
Target是DEBUG、RELEASE和*中的一個,*是萬用字元
TOOL_CHAIN_TAG是編譯器的名字,編譯器定義在Conf/tools_def.txt檔中,*表示對指定編譯器家族內的所有編譯器都有效
Arch是體系結構,可以是IA32、X64、IPF、EBC或ARM,*是萬用字元
CC表示編譯選項,
DLINK表示連結選項
=表示選項附加到預設選項後面,
==表示僅使用所定義的選項,不用預設選項。它們後面是編譯選項或連接選項
==== inf檔編譯運行 ====
原始檔案,inf工程檔完成,想要編譯運行這個模組,需要將inf工程檔添加到dsc的[Components]部分,然後就可以使用build工具編譯\\
模組應用程式是如何被編譯成.efi文件的:\\
1、.c源碼檔先被編譯成目的檔案.obj\\
2、連接器將目的檔案*.obj和其他的庫連接成*.dll\\
3、GenFw工具將*.dll轉化成*.efi\\
這些過程都由build命令自動完成\\
標準應用程式載入過程(efi檔載入)\\
1、將efi檔載入到記憶體\\
efi模組檔————gBS ->LoadImage()將efi檔載入到記憶體中生成image物件————gBS->StartImage(Image)啟動Image物件\\
* gBS 指向啟動服務表 global Boot Servers\\
* gST 指向系統表 global System Table\\
* gRT 指向運行時服務表 global Running Time\\
* gImageHandle 指向正在執行的驅動或應用程式\\
EFI_STATUS
LoadDriver(
IN CONST CHAR16 *FileName,
IN CONST BOOLEAN Connect
)
{
EFI_HANDLE LoadedDriverHandle;
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *FilePath;
EFI_LOADED_IMAGE_PROTOCOL *LoadedDriverImage;
LoadedDriverImage = NULL;
FilePath = NULL;
LoadedDriverHandle = NULL;
Status = EFI_SUCCESS;
*************************************
//
// Use LoadImage to get it into memory
//
Status = gBS->LoadImage(
FALSE,
gImageHandle,
FilePath,
NULL,
0,
&LoadedDriverHandle);
**********************************************
//
// Make sure it is a driver image
//
Status = gBS->HandleProtocol (LoadedDriverHandle, &gEfiLoadedImageProtocolGuid, (VOID *) &LoadedDriverImage);
*************************************************
/*
* 找出可执行的程序镜像Image的入口函数并执行找到的入口函数, gBS->StartImage是个函数指针,实际指向CoreStartImage函数
*/
Status = gBS->StartImage(LoadedDriverHandle, NULL, NULL);
if (EFI_ERROR(Status)) {
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOAD_ERROR), gShellLevel2HiiHandle, FileName, Status);
} else {
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOAD_LOADED), gShellLevel2HiiHandle, FileName, LoadedDriverImage->ImageBase, Status);
}
}
**********************************************************
return (Status);
}
2、進入鏡像image的入口函數
CoreStartImage的主要作用是調用鏡像的入口函數,在這裡面SetIump/LongJumps是為應用程式提供了一種錯誤處理機制。gBS->StartImage的核心是EntryPoint函數,它就是這個應用程式的入口函數,就是_ModuleEntryPoint函數,進入到這裡,代碼執行的控制權才轉交給我們的應用程式efi
EFI_STATUS
EFIAPI
CoreStartImage (
IN EFI_HANDLE ImageHandle,
OUT UINTN *ExitDataSize,
OUT CHAR16 **ExitData OPTIONAL
)
{
EFI_STATUS Status;
LOADED_IMAGE_PRIVATE_DATA *Image;
LOADED_IMAGE_PRIVATE_DATA *LastImage;
UINT64 HandleDatabaseKey;
UINTN SetJumpFlag;
EFI_HANDLE Handle;
**************************************************************
//
// Set long jump for Exit() support
// JumpContext must be aligned on a CPU specific boundary.
// Overallocate the buffer and force the required alignment
//
Image->JumpBuffer = AllocatePool (sizeof (BASE_LIBRARY_JUMP_BUFFER) + BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT);
if (Image->JumpBuffer == NULL) {
//
// Image may be unloaded after return with failure,
// then ImageHandle may be invalid, so use NULL handle to record perf log.
//
PERF_START_IMAGE_END (NULL);
//
// Pop the current start image context
//
mCurrentImage = LastImage;
return EFI_OUT_OF_RESOURCES;
}
Image->JumpContext = ALIGN_POINTER (Image->JumpBuffer, BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT);
SetJumpFlag = SetJump (Image->JumpContext);
//
// The initial call to SetJump() must always return 0.
// Subsequent calls to LongJump() cause a non-zero value to be returned by SetJump().
//
if (SetJumpFlag == 0) {
RegisterMemoryProfileImage (Image, (Image->ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION ? EFI_FV_FILETYPE_APPLICATION : EFI_FV_FILETYPE_DRIVER));
//
// Call the image's entry point 核心
//
Image->Started = TRUE;
Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);
******************************************************************
//
// UEFI Specification - StartImage() - EFI 1.10 Extension
// To maintain compatibility with UEFI drivers that are written to the EFI
// 1.02 Specification, StartImage() must monitor the handle database before
// and after each image is started. If any handles are created or modified
// when an image is started, then EFI_BOOT_SERVICES.ConnectController() must
// be called with the Recursive parameter set to TRUE for each of the newly
// created or modified handles before StartImage() returns.
//
**********************************************************************
//
// Save the Status because Image will get destroyed if it is unloaded.
//
Status = Image->Status;
**************************************************************************
//
// Done
//
PERF_START_IMAGE_END (Handle);
return Status;
}
_ModuleEntryPoint函数:
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
if (_gPeimRevision != 0) {
//
// Make sure that the PEI spec revision of the platform is >= PEI spec revision of the driver
//
ASSERT ((*PeiServices)->Hdr.Revision >= _gPeimRevision);
}
//
// Call constructor for all libraries
//
ProcessLibraryConstructorList (FileHandle, PeiServices);
//
// Call the driver entry point
//
return ProcessModuleEntryPointList (FileHandle, PeiServices);
}
3、進入模組的入口函數
在_ModuleEntryPoint中,調用了ProcessModuleEntryPointList ,然後在build過程中,會解析inf檔,生成AutoGen.c檔,裡面查看ProcessModuleEntryPointList ,它調用了應用程式工程模組的真正入口函數,就是inf檔中指定的那個入口函數
EFI_STATUS
EFIAPI
ProcessModuleEntryPointList (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
return PcdPeimInit (FileHandle, PeiServices);
}
標準應用程式工程模組入口函式呼叫過程:\\
efi——LoadImage——StartImage——_ModuleEntryPoint——ProcessModuleEntryPointList ——inf中指定的入口函数\\
原文連結:https://blog.csdn.net/wangyongh/article/details/105345077
===== DSC 文件 =====
.inf 用于编译一个模块, 而.dsc 文件用于编译一个Package, 它包含了[Defines]、
[LibraryClasses]、[Components] 几个必需部分以及[PCD]、[BuildOptions] 等几个可选部分。
===== DEC 文件 =====
.dec 文件定义了公开的数据和接口,供其他模块使用。它包含了必需区块[Defines] 以及可选区块[Includes]、[LibraryClasses]、[Guids]、[Protocols]、[Ppis] 和[PCD] 几个部分。
.dec文件需要被调用模块.inf文件所包含。
UEFI 編譯 BaseTools \\
''make -C BaseTools''
^Module type ^描述^
|BASE|可執行在任何環境下|
|SEC|在SEC階段執行,將資料傳進PEI階段[註3]|
|PEIM|在PEI階段執行|
|DXE_DRIVER|在boot service環境下執行,直至呼叫ExitBootService而中斷|
|DXE_RUNTIME_DRIVER|在Run Time階段仍可執行的DXE Module|
|UEFI_APPICATION|可在UEFI Shell被執行|
|UEFI_DRIVER|只在boot service環境下有效|
===== EDK2 Directory Description =====
^目錄 ^說明 ^
|AppPkg|UEFI Application Development Kit是一系列用來進行uefi app開發的套件,標準依賴庫,工具以及demo,目標是降低UEFI app的開發門檻。|
|MdePkg|全稱為“Module Development Environment Package”,這是一個特殊的package,包含了用於開發一個module所需要的最小環境。一個module可能也會依賴於其他的Package,但是所有modules必須依賴於MdePkg。|
|MdeModulePkg|此包提供符合UEFI/PI工業標準的模組,也提供該標準相關的開發環境,PPIs/PROTOCOLs/GUIDs和依賴庫。|
|ArmPkg|提供ARM架構相關的protocols,屬於ARM平臺上的通用代碼。|
|ArmPlatformPkg|ARM開發板相關的UEFI代碼,包含ARM平臺上通用的一些組件,重複利用這些組建會令ARM平臺的不同板型之間的移植變得更加容易。|
|BaseTools|該包提供編譯相關的工具用於EDK和EDK2,比如:AutoGen, Build, GenSec, GenFV, GenFW, GenRds工具。|
|BeagleBoardPkg|BeagleBoard是一個便宜並且靈活的單板電腦,玩過開發板的同學應該都瞭解過,基於OMAP3530 SoC (ARM Cortex TM-A8)處理器的開發板。這個包裡是對此開發板的支持代碼。|
|CorebootModulePkg|Coreboot原本和UEFI/BIOS是競爭關係,但是現在Coreboot也逐漸向UEFI標準靠攏,目前依靠此包可以輕鬆從coreboot啟動到UEFI,Coreboot Support Modules包含了如下代碼:用來解析coreboot table的代碼,報告記憶體/IO資源的代碼,安裝acpi table和smbios table 到EFI系統表中的代碼。它位於uefi環境和hardware的中間層。|
|CorebootPayloadPkg|用來載入上層UEFI環境的中間層,Hardware->Coreboot->CorebootPayloadPkg->UEFI->HLOS|
|CryptoPkg|UEFI定義了HLOS(high level OS)和平臺固件之間的介面,多個安全特性也在2.2版本之後加入其中,此包是用來提供加密支援的。|
|DuetPkg|開發者使用的UEFI模擬器,能夠在BIOS機器上模擬UEFI環境。DUET 是基於Legacy BIOS 提供了UEFI模擬器,提供了在傳統BIOS系統上的UEFI環境。|
|EdkCompatibilityPkg|EDK相容包提供庫和標頭檔用來使能在UEFI2.0+Framework0.9x模式下的EDK編譯。|
|EdkShellPkg, EdkShellBinPkg|EDK Shell開發包以及一個官方的EDK Shell實現,這兩個包已經在被Shell 2.x逐漸取代了。|
|ShellPkg,ShellBinPkg|Shell 2.x版本開發包以及一個官方的UEFI Shell 實現。|
|EmbeddedPkg|為memory mapped controllers提供protocol實現,以及一個簡單的EFI shell(EBL)。|
|EmulatorPkg|Emulator虛擬環境,用來取代Nt32Pkg和UnixPkg,可以跨平臺編譯運行的一個虛擬器。|
|Nt32Pkg|一個win環境下的UEFI虛擬器,不能用語載入實際的OS,只能用來調試UEFI代碼。已經逐漸被Emulator取代。|
|UnixPkg|一個類Unix作業系統下的UEFI虛擬器,已經逐漸被Emulator取代。|
|OvmfPkg|OVMF是用來給虛擬機器提供UEFI支援的包,可以使用QEMU和KVM來引導OVMF固件,並進一步引導HLOS。|
|NetworkPkg|提供網路支援的包,比如:IPv6網路通訊協定棧/IPsec驅動/PXE驅動/iSCSI驅動/網路配置相關的shell app。|
|Omap35xxPkg|Omap35xxPkg是Texas Instrument OMAP35xx平臺支援包。|
|OptionRomPkg|這個包的目的是為了提供Option ROM image支援的,如果要編譯PCI相容的Option ROM image,需要依賴此包。|
|SecurityPkg|提供了四種安全相關的特性:TPM, User identification (UID),secure boot, authenticated variable|
|StdLib, StdLibPrivateInternalFiles|StdLib提供了標準庫的UDK實現,StdLibPrivateInternalFiles包是用來給StdLib使用的,不能用作其他引用。|
|UefiCpuPkg|提供相容UEFI的CPU模組和庫。|
|SourceLevelDebugPkg|用來提供debug支援的包。|
|SignedCapsulePkg|提供了一個簽名校驗方案,用來支持安全升級和恢復。|
|PcAtChipsetPkg|這個包提供了符合PcAt標準器件的介面和實現。|
|FatPkg, FatBinPkg|FAT支持包。| \\
原文連結:https://blog.csdn.net/u011057409/article/details/117636656
===== Build 參數 =====
^ 命令參數縮寫 ^ 命令參數全稱 ^ 描述 ^
|-h |--help |show this help message and exit顯示説明資訊。|
|-a TARGETARCH |--arch=TARGETARCH|選擇目標平臺架構,該選項被指定會取代Conf/target.txt中的TARGET_ARCH。|
|-p PLATFORMFILE |--platform=PLATFORMFILE|通過指定.dsc檔指定要編譯的package,該選項將會Conf/target.txt文件ACTIVE_PLATFORM。|
|-m MODULEFILE| --module=MODULEFILE|Build the module specified by the INF file name argument。|
|-b BUILDTARGET| --buildtarget=BUILDTARGET |選擇編譯成DEBUG還是RELEASE。指定該選項後會取代target.txt文件中TARGET。|
|-t TOOLCHAIN| --tagname=TOOLCHAIN|選擇tools_def.txt中定義的編譯工具,例如VS2012:-t vs2012|
|-n THREADNUMBER| |編譯器使用的執行緒數量,指定後取代target.txt檔中MAX_CONCURRENT_THREAD_NUMBER指定的執行緒數量。 Less than 2 will disable multi-thread builds.小於2就禁止多執行緒編譯。|
|-u| --skip-autogen | Skip AutoGen step.跳過AutoGen這一步。|
|-c| --case-insensitive | Don't check case of file name.檔案名不區分大小寫。|
|-w| --warning-as-error | Treat warning in tools as error.將警告做錯誤處理。|
|-j LOGFILE| --log=LOGFILE| Put log in specified file as well as on console.將編譯資訊輸出到檔。|
|-s| --silent| Make use of silent mode of (n)make.使用沉默模式執行make或nmake。|
|-q| --quiet| Disable all messages except FATAL ERRORS.編譯過程中只顯示嚴重錯誤資訊。|
|-d DEBUG| --debug=DEBUG| Enable debug messages at specified level.在指定的級別啟用調試消息。|
|-D MACROS| --define=MACROS| Macro: "Name [= Value]".定義宏。|
| | ---version| show program's version number and exit。|
===== UEFI API =====
==== FeaturePcdGet ====
FeaturePcdGet(PcdHelloWorldPrintString) 查看在PcdHelloWorldPrintString 是否可用。
==== Print() ====
Print函數定義在 UefiLib.h 裡有聲明,其作用為打印字符串。
==== PcdGetPtr() ====
PcdGetPtr(PcdHelloWorldPrintString) 讀取在PcdHelloWorldPrintString 的字串。
==== AMI VEB ====
AMI VEB 基本上就是一堆 .CIF 檔案的集合