EDK2:模块(Module)和包(Package)
包:是一组模块及工程描述文件(.dsc文件)
模块:动态加载到UEFI内核中,工程模块由元数据文件和源文件组成。
UEFI 模块
标准应用程序工程模块
初识UEFI
按惯例,首先让我们用HelloWorld跟UEFI打个招呼吧
标准application
1。 头文件, 所有的UEFI程序都有include <Uefi.h>
2。 main函数, UEFI 基本Application的main函数是UefiMain
3。 main函数的返回值类型 EFI_STATUS。 在UEFI中基本上所有的返回值类型都是EFI_STATUS。
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文件
然后将 main.inf 添加到 Nt32Pkg.dsc 或UnixPkg.dsc 的[Components]部分, 例如添加下面一行(example目录在EDK2下)
其他类型的inf文件
(1) 可以看出标准的application处理命令行参数不方便,UEFI提供了帮我们处理命令行参数的入口函数ShellCEntryLib。 我们要实现INTN ShellAppMain(UINTN Argc, CHAR16** Argv) 作为(开发者视角的)入口函数。
(2)使用main函数的application。如果你想像C一样使用main函数,那么你需要用到LibC。 LibC 中提供了ShellAppMain函数,我们要提供 int main(int Argc, char** Argv) 供其调用。
EFIAPI
main (
IN int Argc,
IN char ** Argv
)
{
} 真正的入口函数是
ShellCEntryLib, 调用过程为
ShellCEntryLib -> ShellAppMain -> main.
inf 文件: 我们需要连接 ShellCEntryLib 和LibC库。
(3)Lib 模块的inf文件。开发大型工程的时候我们会用到lib,例如我们要开发视频解码程序,会用到zlib库,
中添加 zlib即可。
(4)driver模块的inf文件。例如DiskIo的inf(
)
现在我们已经扫除了编译UEFI应用的所有障碍。 在下一部分,我们将了解开发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;
}
有以下几点需要注意:
#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 #模块类型:
,
,
,
,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 =
[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
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
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 库。
#include <Uefi.h>
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
gST -> ConOut-> OutputString(gST -> ConOut, L"HelloWorld\n");
return 0;
}
[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]
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
#include <Uefi.h>
EFIAPI
main (
IN int Argc,
IN char ** Argv
)
{
gST
->
ConOut
->
OutputString(gST -> ConOut, L"HelloWorld\n");
return
0
;}
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
[BuildOptions]
MSFT: * _ * _IA32_CC_FLAGS = / Oi -
还要再说明一点,如果你的程序中用到了printf(...)等等标准C的库函数,那么一定要使用此种类型的application。 因为
ShellCEntryLib 函数中会调用ShellAppMain(...), StdLib的ShellAppMain(...) 会对stdlib 进行初始化。 然后才可以调用stdlib的函数。 (当然,如果你已经清楚地了解了入口函数的处理流程,你也可以手工调用StdLib的ShellAppMain进行出事后).
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 -
(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
然后将
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]
(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
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一定用到的系统服务。