> 文章列表 > UEFI 实战(2) HelloWorld 之一 helloworld及.inf文件

UEFI 实战(2) HelloWorld 之一 helloworld及.inf文件

UEFI 实战(2) HelloWorld 之一 helloworld及.inf文件

初识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字符串。
要想编译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应用的所有障碍。 在下一部分,我们将了解开发UEFI一定用到的系统服务。