UEFI與inf文件
背景
學習高通UEFI中的LCD顯示框架,看到有些博客對inf文件進行了介紹,因此整理了這方面的一些入門知識。
參考:
- https://blog.csdn.net/yunfenglw/article/details/46946907
- http://www.cppblog.com/djxzh/archive/2012/03/13/167690.html
inf文件的構成
Defines
INF_VERSION:Inf版本號,用於編譯時解釋inf文件,通常設置為0x00010005,INF_VERSION版本號便於EDKII系統升級。
BASE_NAME:用於設置編譯輸出的文件名稱,根據文件功能設置,不能包含空格。它通常也是輸出文件名字。
FILE_GUID:文件的全局唯一標識符,格式:8-4-4-4-12,用於生成固件。例如: 4ea97c46-7491-4dfd-b442-747010f3ce5f
VERSION_STRING:文件版本字符串,用於標注文件的版本號
MODUL_TYPE:用於標識模塊的類型、應用工程文件設置為:UEF工_APPLICATION
定義模塊的模塊類型,可以是SEC/PEI_CORE/PEIM/DXE_CORE/DXE_SAL_DRIVER/DXE_SMM_DRIVER/UEFI_DRIVER/DXE_DRIVER/DXE_RUNTIME_DRIVER/UEFI_APPLICATION/BASE中的一個。對於標准應用程序工程模塊來說,為UEFI_APPLICATION類。
ENTRY POINT:模塊的入口函數
Sources
列出文件內所有的源文件和資源文件
Packages
列出文件使用的包的描述文件,即.dec文件,如果source塊內包含了源文件,必須將MdePkg/MdePkg.dec放在首行
LibraryClasses
列出文件內所使用的庫文件,應用工程文件必須包含UefiApplicationEntryPoint,驅動文件必須包含UEFIDriverEntryPoint
Protocols
列出文件內使用的Protocol的GUID
BuildOptions
編譯配置。
- = 表示選項附加到默認選項后面。
- == 表示僅使用所定義的選項,棄用默認選項。
附錄
初識UEFI
按慣例,首先讓我們用HelloWorld跟UEFI打個招呼吧
標准application
/*main.c */
#include <Uefi.h>
EFI_STATUS
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
SystemTable -> ConOut-> OutputString(SystemTable -> ConOut, L"HelloWorld\n");
return EFI_SUCCESS;
}
有以下幾點需要注意:
1、 頭文件, 所有的UEFI程序都有#include <Uefi.h>
2、 main函數, UEFI 基本Application的main函數是UefiMain
3、 main函數的返回值類型 EFI_STATUS。 在UEFI中基本上所有的返回值類型都是EFI_STATUS。它本質上是UINTN。
4、 main函數的參數。.efi 文件加載到內存后稱為Image, ImageHandle 用來描述、訪問、控制此Image。 第二個參數是SystemTable,它是我們的程序同UEFI內核打交道的橋梁,通過它我們可以使用UEFI提供的各種服務,如Boot Services和 Runtime Services。 SystemTable是UEFI內核中的一個全局結構體。
5、 輸出是通過EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的OutputString服務完成的。 服務(函數)的第一個參數是This指針,指向Protocol本身。 OutputString()的第二個參數是Unicode字符串。
對應的inf文件
要想編譯main.c,我們還需要.inf文件, 在main.c所在的目錄下編輯main.inf文件
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = main #輸出文件的名字為 main.efi
FILE_GUID = 6987936E-ED34-ffdb-AE97-1FA5E4ED2117
MODULE_TYPE = UEFI_APPLICATION #模塊類型: UEFI_DRIVER, DXE_DRIVER, DXE_RUNTIME_DRIVER,UEFI_APPLICATION,BASE,等
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain #入口函數
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
# 源文件
[Sources]
main.c
# .dec里面定義 include的路徑
[Packages]
MdePkg/MdePkg.dec
#要鏈接的庫
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
[Protocols]
[FeaturePcd]
[Pcd.common]
[Guids]
#編譯選項, = 表示選項附加到默認選項后面。 == 表示僅使用所定義的選項,棄用默認選項。
[BuildOptions]
#MSFT:*_*_*_CC_FLAGS == /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1ib2 /GL /EHs-c- /GR- /GF /Gy /Zi /Gm /D EFI_SPECIFICATION_VERSION=0x0002000A /D TIANO_RELEASE_VERSION=0x00080006 /FAs /Oi-
#MSFT:*_*_*_CC_FLAGS = /wd4804
#MSFT:Debug_*_IA32_CC_FLAGS =
#MSFT:Debug_*_X64_CC_FLAGS =
#MSFT:Release_*_IA32_CC_FLAGS =
#MSFT:Release_*_IA32_CC_FLAGS =
#MSFT:Release_*_IA32_DLINK_FLAGS =
#GCC:Release_*_IA32_CC_FLAGS =
然后將 main.inf 添加到 Nt32Pkg.dsc 或UnixPkg.dsc 的[Components]部分,
例如添加下面一行(example目錄在EDK2下)example/main/main.inf
然后就可以使用BaseTools下的build進行編譯了。
Windows下執行:
edksetup.bat
build -p Nt32Pkg\t32Pkg.dsc -a IA32
Linux 執行
source ./edksetup.sh BaseTools
build -p UnixPkg/UnixPkg.dsc -a IA32
其他類型的inf文件
(1) 可以看出標准的application處理命令行參數不方便,UEFI提供了幫我們處理命令行參數的入口函數ShellCEntryLib。 我們要實現INTN ShellAppMain(UINTN Argc, CHAR16** Argv)
作為(開發者視角的)入口函數。
/*Main.c */
#include <Uefi.h>
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
gST -> ConOut-> OutputString(gST -> ConOut, L"HelloWorld\n");
return 0;
}
inf文件。 我們需要連接ShellCEntryLib 庫。
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = Main
FILE_GUID = 4ea97c46-7491-4dfd-b442-747010f3ce5f
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = ShellCEntryLib
#
# VALID_ARCHITECTURES = IA32 X64 IPF
#
[Sources]
Main.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
ShellCEntryLib
UefiLib
[BuildOptions]
(2)使用main函數的application。如果你想像C一樣使用main函數,那么你需要用到LibC。 LibC 中提供了ShellAppMain函數,我們要提供int main(int Argc, char** Argv)
供其調用。
/*Main.c */
#include <Uefi.h>
int
EFIAPI
main (
IN int Argc,
IN char **Argv
)
{
gST -> ConOut-> OutputString(gST -> ConOut, L"HelloWorld\n");
return 0;
}
真正的入口函數是ShellCEntryLib, 調用過程為 ShellCEntryLib -> ShellAppMain -> main.
inf 文件: 我們需要連接ShellCEntryLib 和LibC庫。
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = Main
FILE_GUID = 4ea97c46-7491-4dfd-b442-747010f3ce5f
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = ShellCEntryLib
#
# VALID_ARCHITECTURES = IA32 X64 IPF
#
[Sources]
Main.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
LibC
ShellCEntryLib
UefiLib
[BuildOptions]
MSFT:*_*_IA32_CC_FLAGS = /Oi-
還要再說明一點,如果你的程序中用到了printf(...)等等標准C的庫函數,那么一定要使用此種類型的application。 因為ShellCEntryLib 函數中會調用ShellAppMain(...), StdLib的ShellAppMain(...) 會對stdlib 進行初始化。 然后才可以調用stdlib的函數。 (當然,如果你已經清楚地了解了入口函數的處理流程,你也可以手工調用StdLib的ShellAppMain進行出事后).
(3)Lib 模塊的inf文件。開發大型工程的時候我們會用到lib。
例如我們要開發視頻解碼程序,會用到zlib庫
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = zlib
FILE_GUID = 348aaa62-BFBD-4882-9ECE-C80BBbbbb736
VERSION_STRING = 1.0
MODULE_TYPE = BASE #Base 表示此模塊編譯為library
LIBRARY_CLASS = zlib
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources]
adler32.c
crc32.c
deflate.c
infback.c
inffast.c
inflate.c
inftrees.c
trees.c
zutil.c
compress.c
uncompr.c
gzclose.c
gzlib.c
gzread.c
gzwrite.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
StdLib/StdLib.dec
[LibraryClasses]
MemoryAllocationLib
BaseLib
UefiBootServicesTableLib
BaseMemoryLib
UefiLib
UefiRuntimeServicesTableLib
[Protocols]
[FeaturePcd]
[Pcd]
[Guids]
[BuildOptions]
GCC:*_*_IA32_CC_FLAGS = -D__UEFI__ -DLARGEFILE64_SOURCE=1 -w
然后將 zlib|zlib-1.2.6/zlib.inf # zlib-1.2.6 在EKD2的根目錄下放到.dsc 文件 [LibraryClasses]中。
需要鏈接zlib的時候,在.inf文件的[LibraryClasses]中添加 zlib即可。
(4)driver模塊的inf文件。例如DiskIo的inf(MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf)
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = DiskIoDxe
FILE_GUID = 6B38F7B4-AD98-40e9-9093-ACA2B5A253C4
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = InitializeDiskIo
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
# DRIVER_BINDING = gDiskIoDriverBinding
# COMPONENT_NAME = gDiskIoComponentName
# COMPONENT_NAME2 = gDiskIoComponentName2
#
[Sources]
ComponentName.c
DiskIo.h
DiskIo.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiBootServicesTableLib
MemoryAllocationLib
BaseMemoryLib
BaseLib
UefiLib
UefiDriverEntryPoint
DebugLib
[Protocols]
gEfiDiskIoProtocolGuid ## BY_START
gEfiBlockIoProtocolGuid ## TO_START
現在我們已經掃除了編譯UEFI應用的所有障礙。