继EDK2的环境搭建与模拟执行。现在开始学习如何在EDK2中使用UEFI提供的接口编写一个属于自己的包,并完成一些力所能及的实验性功能。
1、在制作一些大型事务之前,很多软件都会需要先新建工程。在EDK2中也是如此,在之前的文章中有介绍过EDK2使用包来区分不同的工程代码。并使用一些文件来描述这个工程的内容,被称之为包描述文件,或者说时项目描述文件。
因此在开始写代码前需要先新建一个包。来到edk2源码根目录,有很多的Pkg。
2、新建HelloUefiPkg文件夹用于作为此文章中的实验性包。
edk2自身的包下还存在很多目录用于划分包下面的各个功能点项目的代码,但我们的包没有那么多功能点,所以可以不需要再套更多的文件夹。直接新建HelloUefi.c。
3、写如下代码:
EFI_STATUS UefiMain(IN EFI_HANDLE image, IN EFI_SYSTEM_TABLE* systemTable)
{
Print("Hello UEFI.n");
return EFI_SUCCESS;
}
其中Uefi.h和Library/UefiLib.h相当于Uefi中的标准库,是必须要导入的库,其中包含了很多基础设施。包含基本的数据类型,基本的函数。
UefiMain是Uefi的入口点,是常用的名称但不是固定的名称,入口函数的名称需要在接下来的编译配置文件中指定。但它的函数参数与返回值是固定的。
关于UefiMain:
1)返回值EFI_STATUS作为当前Uefi应用程序是否正常执行的标识。当程序正常结束时返回EFI_SUCCESS,否则返回错误原因。
2)参数image:类型是EFI_HANDLE,此句柄描述着Uefi程序本身在内存中的对象。在使用Uefi提供的部分API时需要传递给API来记录调用者。
3)参数systemTable:类型是EFI_SYSTEM_TABLE指针,此结构体描述着当前系统各种状态与系统提供的一些接口、对象供给Uefi应用程序使用。是经常使用的表项之一。
4、Print和c语言的printf一样支持格式化字符串输出。之后返回EFI_SUCCESS标识程序成功运行。至此最简单的Uefi应用程序就算完成了。
5、为了方便之后的编码更加方便,可以使用VSCode来对edk2的项目进行管理。
使用VSCode打开edk2的根目录:
如果没装C/C++扩展,可以参考网上教程安装VSCode的C扩展插件。
6、随后找到刚才新建的HelloUefi.c,可能会出现找不到头文件的情况。这是因为没有在VSCode中配置Uefi导入库。
在.vscode目录下新建VSCode C/C++配置文件c_cpp_properties.json并写入如下配置项:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/MdePkg/Include"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "XXX\WinGW\bin\gcc.exe",
"cStandard": "c11",
"cppStandard": "gnu++14",
"intelliSenseMode": "windows-gcc-x86"
}
],
"version": 4
}
7、之后VSCode就能正常识别导入库了。ctrl+点击 跟进Print试试:
8、随后要创建两个文件,一个和Makefile的功能差不多的inf文件,用于描述如何编译这些.c文件。另一个是dsc文件用于描述当前包信息。
1)dsc文件用于描述整个解决方案的配置信息, 包括支持的平台信息,编译的目标类型和项目名称, 格式如下:
[Defines] 属性定义标签
INF_VERSION INF版本号 (0x00010006,可以通过查看edk2自身的inf文件查看)
BASE_NAME 编译结果的文件名称,不包含后缀
FILE_GUID 随机GUID用于唯一标识子模块,可在线生成或使用VSCode的GUID生成器插件
MODULE_TYPE 可选BASE、SEC、PEI_CORE、PEIM、DXE_CORE、DXE_DRIVER、DXE_RUNTIME_DRIVER、DXE_SAL_DRIVER、DXE_SMM_DRIVE、UEFI_DRIVER、UEFI_APPLICATION、USER_DEFINED、HOST_APPLICATION、SMM_CORE、MM_STANDALONE、MM_CORE_STANDALONE,使用UEFI_APPLICATION表示开发UEFI应用程序。
VERSION_STRING 自定义的模块版本,第一个版本1.0
ENTRY_POINT 模块入口点函数名,此处为UefiMain
[Sources] 源文件及资源文件列表
[Sources.$(ARCH)] 可以指定仅在$(ARCH)架构下才编译的文件
[Sources.common] 表示可以在所有架构下编译的文件
[Packages] 列出本模块引用的所有包的dec文件,MdePkg/MdePkg.dec必须包含并且放在第一位。
[LibraryClasses] 列出本模块引用的链接库名,如UefiLib。在dsc中写的包名称
[Protocols] 引用到的Protocols
[BuildOptions] 附加的编译选项
[Depex] 用来定义驱动的执行顺序,当设置为TRUE时表示当前模块不需要依赖任何模块,轮询到直接执行。否则需要写依赖项,如MdeModulePkg/Logo/LogoDxe.inf中。
2)dsc文件用于描述整个解决方案的配置信息,包括支持的平台信息,编译的目标类型和项目名称,格式如下:
[Defines] 属性定义标签
PLATFORM_NAME 解决方案名称
PLATFORM_GUID 随机GUID用于唯一标识解决方案,可在线生成或使用VSCode的GUID生成器插件
PLATFORM_VERSION 包版本,第一版本1.0
DSC_SPECIFICATION dsc文件版本号(当前为0x00010005,可以通过查看edk2自身的dsc查看)
OUTPUT_DIRECTORY 编译结果目录(可选填)
SUPPORTED_ARCHITECTURES 包所支持的cpu架构,用|分割,大小写敏感。包括IA32|X64|EBC|ARM|AARCH64|RISCV64|LOONGARCH64等
BUILD_TARGETS 编译的目标版本,包括DEBUG|RELEASE|NOOPT
SKUID_IDENTIFIER DEFAULT(可选,暂时不需要填)
FLASH_DEFINITION 当前模块的fdf文件
[LibraryClasses]
格式:库名称 | 库的inf文件路径,
以下这两个必加
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
[Components] 包内需要编译的inf路径
[BuildOptions] 附加的编译选项
9、编写HelloUefiPkg.dsc文件:
[Defines]
PLATFORM_NAME = HelloUefiPkg
PLATFORM_GUID = 645dc7cb-fb24-4635-a062-a1ce306ea8e3
PLATFORM_VERSION = 1.0
DSC_SPECIFICATION = 0x00010005
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
[LibraryClasses]
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
[Components]
10、HelloUefiPkg/HelloUefi.inf其中
编写HelloUefi.inf文件:
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = HelloUefi
FILE_GUID = ddc14bd1-9cd6-4206-a6e4-2284f299537b
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
HelloUefi.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiLib
DebugLib
PrintLib
BaseLib
PcdLib
BaseMemoryLib
RegisterFilterLib
UefiApplicationEntryPoint
MemoryAllocationLib
UefiBootServicesTableLib
DevicePathLib
UefiRuntimeServicesTableLib
11、关于LibraryClasses如何选取没什么好的办法。当缺少导入库时会报错提示,如在inf与dsc中删除掉UefiRuntimeServicesTableLib,编译时的报错。
此时在edk2中搜索报错中缺少的库就能找到其他dsc的导入代码,可以拿来用。
12、随后修改ACTIVE_PLATFORM为HelloUefiPkg.dsc的路径,直接编译:
编译成功:
13、生成的文件在edk2BuildHelloUefiPkgDEBUG_VS2019X64HelloUefi.efi。
拷贝到上节课搭建的环境的文件夹下:
14、进入fs0:EFI并执行HelloUefi.efi,可以看到输出了Hello UEFI。
下篇文章介绍如何通过UEFI协议接口操作设备,实现修改分辨率与在屏幕中绘制图像,制作简易的开机动画。
原文始发于微信公众号(锋刃科技):EDK2之Hello Uefi
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论