UEFI开发-知识点汇总
文章目录
概述
BIOS
作用
BIOS(basic I/O system)即基本输入/输出系统,它是存储在主板rom中的一段程序,作用包括:
1、加电硬件检测:开机时检测硬件设备
2、硬件设备初始化,创建中断向量
3、将操作系统拷贝到ram中,并执行
缺点
1、它是由汇编代码实现的:不便于开发
2、性能差
3、不支持硬盘2TB以上的地址寻址,bios是32位寻址最大到2的32次方=2TB
UEFI
UEFI(Unified extensible firmware interface)统一可扩展固件接口,它是一种标准,其实现是由其他公司或开源组织提供的。
作用
扫描硬件、安装驱动;提供给操作系统启动服务(boot services,BS)和运行时服务(runtime service,RT)、以及protocol
BS提供:
1、事件服务,可支持异步
2、内存管理
3、驱动管理
RT提供:
1、读写UEFI系统变量,例如bootOrder用于指定启动项顺序。
2、虚拟内存服务
参考资料
1、edk2用户手册
2、UEFI原理与编程-戴春华
编译
使用edk2进行开发,edk2是实现了uefi标准的开关框架。
在edk2开发,需要遵守以下规则:
Pkg目录
以pkg结尾的文件夹,作为一个模块。
在uefi执行中,将安装各个模块。
inf文件
相当于是一个模块pkg的子功能模块的makefile
[Sources]
注意:
1、要把uni文件、vfr文件放在前面,源文件放在后面。
因为uni文件会转换为h文件,vfr文件会转换为c文件。
同时vfr文件的名字不要和同目录下的文件名重名了。
dec文件
.dec文件记录的本模块的信息。
dsc文件
其中指定了编译规则,相当于Makefile
vfr文件
界面文件,可参考MdeModulePkg/Universal/DriverSampleDxe
UEFI启动各阶段说明
DXE阶段
执行系统大部分的初始化工作。由于此阶段内存已经可以被正常使用,因此该阶段可以执行大量复杂的工作。
BDS阶段
硬件检测和驱动匹配在该阶段进行。
具体功能如下:
1、执行启动策略(主要功能)。
2、初始化控制台设备。
3、加载必要的设备驱动。
4、根据系统设置加载和执行启动项。
网卡检测过程
在DXE阶段加载驱动
在BDS阶段扫描硬件设备,并匹配驱动(supported)
网卡设备的匹配,一些基础网络工具包均可与之相匹配,按照示例图所示:
先安装SNP,将device信息放到SNP句柄中,然后安装MNP,MNP获取SNP句柄中的device信息,获取设备mac地址等信息。
启动系统
启动项排序
获取启动项:
PhytiumPkg/Phytium2004Pkg/Library/PlatformBootManagerLib/
PlatformBm.c
PlatformBootManagerAfterConsole
启动项排序:
PhytiumPkg/Phytium2004Pkg/Library/PlatformBootManagerLib/
PlatformBm.c
PlatformBootManagerAfterConsole
CompareBootOptionKL (0最高,100最低)
注意:一定要把想删掉的启动项优先级排到最低。
添加启动项
PlatformRegisterFvBootOption
通过上个接口添加启动项,其中:
参数1:模块的GUID,即inf文件里的FILE_GUID
参数2:显示在启动列表中的名字
参数3:启动类型
添加按键启动项
F8.ScanCode = SCAN_F8;
F8.UnicodeChar = CHAR_NULL;
// Register setup
OptionNumber = PlatformRegisterFvBootOption (
&gUiAppFileGuid,
L"Enter Setup",
LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP /*| LOAD_OPTION_HIDDEN*/
);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16)OptionNumber, 0, &F8, NULL
);
界面设计
界面设计可以通过vfr文件和Hii相关接口实现。
vfr文件
界面的主体文件,需要将其加到inf文件的sources下去编译它,可以得到vfr.c,其中包含vfr文件名+bin的一个数组。
例如:LaunchCheckPageVfr.vfr 编译得到 LaunchCheckPageVfr.c,其中包含数组:LaunchCheckPageVfrBin,它里面就是界面数据。
这个LaunchCheckPageVfrBin,通过如下加载到uefi中使用:
HiiHandle[0] = HiiAddPackages (
&gLaunchCheckFormSetGuid,
DriverHandle[0],
LaunchCheckPageStrings,
LaunchCheckPageVfrBin,
NULL
);
其中LaunchCheckPageStrings,是inf文件中MODULE_UNI_FILE指定的文件名+Strings。
例:
MODULE_UNI_FILE = LaunchCheckPage.uni
那么对应的就是:LaunchCheckPageStrings
hii接口创建界面中的成员
复选框
在主界面上增加一个复选框,可选择X8X8或X16
创建界面
/**
Create Select language menu in the front page with oneof opcode.
@param[in] HiiHandle The hii handle for the Uiapp driver.
@param[in] StartOpCodeHandle The opcode handle to save the new opcode.
**/
VOID
BmmCreatePcieMenu (
IN EFI_HII_HANDLE HiiHandle,
IN VOID *StartOpCodeHandle
)
{
CHAR8 *LangCode;
CHAR8 *Lang;
UINTN LangSize;
//CHAR8 *CurrentLang;
UINTN OptionCount;
CHAR16 *StringBuffer;
VOID *OptionsOpCodeHandle;
UINTN StringSize;
EFI_STATUS Status;
EFI_HII_STRING_PROTOCOL *HiiString;
UINT32 pcimode;
UINT8 FuncConfig;
UINT8 done_flag;
Lang = NULL;
StringBuffer = NULL;
//
// Init OpCode Handle and Allocate space for creation of UpdateData Buffer
//
OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (OptionsOpCodeHandle != NULL);
OptionCount = 0;
pcimode = GetPcieMode(); //为了显示之前设置的模式
//FuncConfig = (UINT8)(itemp >> 16);
FuncConfig = (UINT8)(pcimode >> 0);//peu0
if(FuncConfig == 1)//x16
{
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN(STR_PCIE_MODE_X16),//实现的内容,定义在uni文件中
EFI_IFR_OPTION_DEFAULT,
EFI_IFR_NUMERIC_SIZE_1,
0 //0表示X16是第一个显示
);
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN(STR_PCIE_MODE_X8X8),
0,
EFI_IFR_NUMERIC_SIZE_1,
1
);
gCurrentPeu0Index = PEU_X16;//表示0位置是X16
}
else
{
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN(STR_PCIE_MODE_X8X8),
0,
EFI_IFR_NUMERIC_SIZE_1,
0
);
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN(STR_PCIE_MODE_X16),
EFI_IFR_OPTION_DEFAULT,
EFI_IFR_NUMERIC_SIZE_1,
1
);
gCurrentPeu0Index = PEU_X8X8;
}
HiiCreateOneOfOpCode (
StartOpCodeHandle,
(EFI_QUESTION_ID)FORM_PEU0_MODE_ID,//自定义
VARSTORE_ID_BOOT_MAINT,//要和vfr中的varsore对应的varid一致
PEU0_MODE_VAR_OFFSET,
STRING_TOKEN (STR_PCIE_PEU0_MODE_CFG),
STRING_TOKEN (STR_PCIE_PEU0_MODE_CFG_HELP),
EFI_IFR_FLAG_CALLBACK,
EFI_IFR_NUMERIC_SIZE_1,
OptionsOpCodeHandle,
NULL
);
}
注意:
1.
#define VAR_OFFSET(Field) ((UINT16) ((UINTN) &(((BMM_FAKE_NV_DATA *) 0)->Field)))
#define PEU0_MODE_VAR_OFFSET VAR_OFFSET (Peu0Mode)
其中Peu0Mode是结构体BMM_FAKE_NV_DATA的成员名
后端
当对上述复选框操作后,保存时将进入RouteConfig接口。
相关代码如下:
EFI_STATUS
EFIAPI
SetPcieMode(UINT8 PeuMode)
{
EFI_STATUS Status;
#define PAR_TABLE_LEN 0x00000580
#define PCIEPAR_OFFSET 0x00000200
#define PCIE_FUNC_OFFSET 0x00000014
UINT8 *configBuff;
ParTableSpiInit();
//step 1:read par table from flash
configBuff = AllocatePool(PAR_TABLE_LEN);
ASSERT (configBuff != NULL);
Status = ParTableRead(PAR_TABLE_LEN,configBuff);
UINT32 pcie_mode = 0;
if(PeuMode == 0)
{
if(gCurrentPeu0Index == PEU_X8X8)
pcie_mode = 0x30003;
else
pcie_mode = 0x30001;
}
else if(PeuMode == 1)
{
if(gCurrentPeu0Index == PEU_X8X8)
pcie_mode = 0x30001;
else
pcie_mode = 0x30003;
}
CopyMem(configBuff + PCIEPAR_OFFSET + PCIE_FUNC_OFFSET,&pcie_mode,4);
Status = ParTableWrite(PAR_TABLE_LEN, configBuff);
FreePool (configBuff);
#undef PAR_TABLE_LEN
#undef PCIEPAR_OFFSET
#undef PCIE_FUNC_OFFSET
return Status;
}
RouteConfig接口中相关代码:
if (CompareMem (&NewBmmData->Peu0Mode, &OldBmmData->Peu0Mode, sizeof (NewBmmData->Peu0Mode)) != 0) {
Status = SetPcieMode(NewBmmData->Peu0Mode);
if (EFI_ERROR (Status)) {
Offset = OFFSET_OF (BMM_FAKE_NV_DATA, Peu0Mode);
goto Exit;
}
}
注意:
- 如何获取用户在界面上复选框的选择
Status = SetPcieMode(NewBmmData->Peu0Mode); NewBmmData->Peu0Mode的值就是如下: HiiCreateOneOfOptionOpCode ( OptionsOpCodeHandle, STRING_TOKEN(STR_PCIE_MODE_X8X8), 0, EFI_IFR_NUMERIC_SIZE_1, 0 //NewBmmData->Peu0Mode对应的值 ); HiiCreateOneOfOptionOpCode ( OptionsOpCodeHandle, STRING_TOKEN(STR_PCIE_MODE_X16), EFI_IFR_OPTION_DEFAULT, EFI_IFR_NUMERIC_SIZE_1, 1 ); gCurrentPeu0Index = PEU_X8X8; 即0对应的是X8X8,1对应的是用户选择的是X16
Hii.h文件
一般放到MdeModulePkg/include/Guid/下,内容:
#ifndef __LAUNCH_CHECK_HII_GUID_H__
#define __LAUNCH_CHECK_HII_GUID_H__
#define LAUNCH_CHECK_FORMSET_GUID \
{ \
0x543E79AE, 0x82BA, 0xDFB6, {0x0D, 0x01, 0xD5, 0xDA, 0x73, 0xF3, 0x61, 0xCE} \
}
#define LAUNCH_CHECK_INVENTORY_GUID \
{ \
0x40E54D59, 0x6ADB, 0xF6FF, {0xA4, 0xCE, 0x8A, 0x70, 0xDB, 0x77, 0x4D, 0xFC} \
}
#define EFI_IFR_REFRESH_ID_OP_GUID \
{ \
0xA297B2CD, 0x1D86, 0x15B5, {0x77, 0x5F, 0x35, 0xE5, 0x1A, 0xB5, 0x08, 0x57} \
}
extern EFI_GUID gLaunchCheckFormSetGuid;
extern EFI_GUID gLaunchCheckInventoryGuid;
extern EFI_GUID gLaunchCheckIfrRefreshIdOpGuid;
其中,gLaunchCheckFormSetGuid,可以理解为指针,你往里面放什么它就是什么。
它的值,在dec文件中定义,值和LAUNCH_CHECK_FORMSET_GUID相同。
使用示例:
HiiHandle[0] = HiiAddPackages (
&gLaunchCheckFormSetGuid,
DriverHandle[0],
LaunchCheckPageStrings,
LaunchCheckPageVfrBin,
NULL
);
上述就是告诉UEFI,找到gLaunchCheckFormSetGuid,就能找到对应的界面数据了,即LaunchCheckPageVfrBin。
界面的后端处理
操作界面时,需要用到如下几个重要的回调函数
ExtractConfig
作用:用于获取之前用户在该页面上配置。
进入页面时调用。
RouteConfig
作用:保存用户修改后的配置数据。
当界面数据变化时进入该回调。
Callback
作用:处理用户对界面上的动作,包括保存、退出等等。
实例1:按F8进入配置界面流程
PlatformBm.c
PlatformRegisterOptionsAndKeys
// Register setup
OptionNumber = PlatformRegisterFvBootOption (
&gUiAppFileGuid,
L"Enter Setup",
LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP /*| LOAD_OPTION_HIDDEN*/
);
其中PlatformRegisterFvBootOption获取gUiAppFileGuid对应的efi文件,并和按键绑定。
gUiAppFileGuid:PhytiumPkg/Phytium2004Pkg/setup/UiApp/UiApp.inf的FILE_GUID
在PhytiumPkg/Phytium2004Pkg/Phytium2004Pkg.dec里面引用:
gUiAppFileGuid = { 0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }}
注意这里是引用,不是定义,定义是在UiApp.inf的FILE_GUID
这样按下F8就进入PhytiumPkg/Phytium2004Pkg/setup/UiApp/的入口函数中了。
实例2:增加Shell启动项
目前代码将shell启动项删除了,增加shell需要修改如下:
1、MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenu.c
@@ -315,10 +315,11 @@ InitializeBootMenuData (
if (IgnoreBootOption (&BootOption[Index])) {
continue;
}
-
+#if 0
if ( StrStr (BootOption[Index].Description , L"UEFI Shell" ) != NULL) {
continue;
}
+#endif
#if 0
if ( StrStr (BootOption[Index].Description , L"Update Bios" ) != NULL) {
continue;
2、PhytiumPkg/Phytium2004Pkg/Library/PlatformBootManagerLib/PlatformBm.c
@@ -927,7 +927,7 @@ PlatformBootManagerAfterConsole (
// Register UEFI Shell
//
//add by sunshuai : del shell in boot options
-#if 0
+#if 1
PlatformRegisterFvBootOption (
&gUefiShellFileGuid, L"UEFI Shell", LOAD_OPTION_ACTIVE
// &gUefiShellFileGuid, L"UEFI Shell", LOAD_OPTION_HIDDEN
3、UefiPayloadPkg/Library/PlatformBootManagerLib/PlatformBootManager.c
@@ -222,8 +222,9 @@ PlatformBootManagerAfterConsole (
//
// Register UEFI Shell
//
-// PlatformRegisterFvBootOption (PcdGetPtr (PcdShellFile), L"UEFI Shell", LOAD_OPTION_ACTIVE); //ling
-
+ #if 1
+ PlatformRegisterFvBootOption (PcdGetPtr (PcdShellFile), L"UEFI Shell", LOAD_OPTION_ACTIVE); //ling
+ #endif
Print (
L"\n"
L"F2 or Down to enter Boot Manager Menu.\n"
内存拷贝接口
mdepkg\library\baselib
1、CopyMem
2、ZeroMem
3、AllocateCopyPool
UnicodeSPrint (ipaddr, sizeof (ipaddr), L"192.168.%d.%d", Private[loop]->IfInfo->HwAddress.Addr[4],Private[loop]->IfInfo->HwAddress.Addr[5]);
打印接口
打印 8位 字符串:
AsciiPrint("ver_data:%a\n", ver_data);
打印 16位 字符串:
CHAR16 *ver_data
Print(L"%s\n", ver_data)
CHAR8 *ver_data
Print(L"%a\n", ver_data)
字符串处理
mdepkg/library/baselib/String.c
mdepkg/library/baselib/SafeString.c
文件处理
gEfiSimpleFileSystemProtocolGuid
如果要遍历目录和文件,需要使用到gEfiSimpleFileSystemProtocolGuid协议。
UINTN HandleCount;
EFI_HANDLE *Handles = NULL;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&HandleCount,
&Handles
);
if(EFI_ERROR(Status) || HandleCount == 0) {
Status = EFI_NOT_FOUND;
if(Handles != NULL){
FreePool(Handles);
}
}
获取各文件系统的EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
一般BIOS会挂载多个文件系统,需要遍历,查找所有挂载的文件系统中的文件。
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FS;
// RootFile相当于根目录
EFI_FILE_PROTOCOL *RootFile = NULL;
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID**)&FS
);
ASSERT(!EFI_ERROR (Status));
if(RootFile != NULL) {
RootFile->Close(RootFile);
RootFile = NULL;
}
//得到根目录
Status = FS->OpenVolume(FS, &RootFile);
if(EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "OpenRootFile Error:%r\n", Status));
continue;
}
//在根目录中打开特定文件
Status = ReadFileInFs(RootFile, BiosFileName, BiosData, BiosSize);
if(!EFI_ERROR(Status)) {
break;
}
}
if(Index >= HandleCount) {
Status = EFI_NOT_FOUND;
if(Handles != NULL) {
FreePool(Handles);
}
if(RootFile != NULL) {
RootFile->Close(RootFile);
}
}
文件读取
使用EFI_FILE_PROTOCOL协议进行操作
STATIC
EFI_STATUS
ReadFileInFs (
EFI_FILE_PROTOCOL *RootFile,
CHAR16 *FilePathName,
UINT8 **OutFileData,
UINTN *OutFileSize
)
{
EFI_STATUS Status;
EFI_FILE_PROTOCOL *File = NULL;
EFI_FILE_INFO *FileInfo = NULL;
UINTN FileInfoSize;
UINT8 *FileData = NULL;
UINTN FileSize = 0;
Status = RootFile->Open(
RootFile,
&File,
FilePathName,
EFI_FILE_MODE_READ,
0
);
if(EFI_ERROR(Status)){
goto ProcExit;
}
FileInfo = NULL;
FileInfoSize = 0;
Status = File->GetInfo (
File,
&gEfiFileInfoGuid,
&FileInfoSize,
FileInfo
);
if(Status == EFI_BUFFER_TOO_SMALL){
FileInfo = AllocatePool(FileInfoSize);
if(FileInfo == NULL){
Status = EFI_OUT_OF_RESOURCES;
} else {
Status = File->GetInfo (
File,
&gEfiFileInfoGuid,
&FileInfoSize,
FileInfo
);
}
}
if(EFI_ERROR(Status)){
goto ProcExit;
}
if(FileInfo->Attribute & EFI_FILE_DIRECTORY) {
Status = EFI_INVALID_PARAMETER;
goto ProcExit;
}
FileSize = (UINTN)FileInfo->FileSize;
FileData = AllocatePages(EFI_SIZE_TO_PAGES(FileSize));
if(FileData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ProcExit;
}
Status = File->Read(File, &FileSize, FileData);
if(EFI_ERROR(Status)) {
goto ProcExit;;
}
//OutFileData : 包含固件的所有数据
*OutFileData = FileData;
*OutFileSize = FileSize;
ProcExit:
if(EFI_ERROR(Status) && FileData != NULL) {
FreePages(FileData, EFI_SIZE_TO_PAGES(FileSize));
}
if(FileInfo != NULL){
FreePool(FileInfo);
}
if(File != NULL){
File->Close(File);
}
return Status;
}
库
在UEFI代码中添加自己的库的过程
1、代码放到:MdeModulePkg\Library
2、对外提供的头文件放到:MdeModulePkg\Include\Library
3、在MdeModulePkg.dsc添加编译流程
[LibraryClasses]
LaunchCheckPage|MdeModulePkg/Library/LaunchCheckPage/LaunchCheckPage.inf
[Components]
MdeModulePkg/Library/LaunchCheckPage/LaunchCheckPage.inf
在MdeModulePkg.dec中添加:
[LibraryClasses]
LaunchCheckPage|Include/Library/LaunchCheckPage.h
4、在Phytium2004Pkg.dsc添加编译流程
原因未知
[LibraryClasses.common]
LaunchCheckPage|MdeModulePkg/Library/LaunchCheckPage/LaunchCheckPage.inf
注意:
1、库的inf文件内链接的库,不能包含UefiApplicationEntryPoint(app应用需要链接的库)
2、库中内部使用的接口要用STATIC修饰,否则可能会多重定义
外部使用的过程
1、在使用模块的inf中添加
[LibraryClasses]
库名
2、源文件中包含头文件
#include <Library/库对外提供的头文件>
如果编译时报找不到对应接口定义
1、find -name 库名.lib
2、nm 库名.lib
查看库中是否有对应的接口,再继续查原因。
- 分享
- 举报
-
浏览量:1677次2024-01-05 14:17:30
-
浏览量:558次2023-09-08 10:12:29
-
浏览量:20396次2022-02-11 09:00:22
-
浏览量:479次2024-02-21 17:08:25
-
浏览量:2045次2020-08-04 20:27:13
-
浏览量:2506次2024-01-25 15:00:06
-
浏览量:5777次2021-06-28 13:41:26
-
浏览量:3133次2022-10-11 11:14:08
-
浏览量:2420次2023-04-19 09:15:44
-
浏览量:1490次2020-08-05 20:47:40
-
浏览量:1701次2023-04-19 09:15:27
-
浏览量:4650次2020-08-29 19:35:34
-
浏览量:2669次2020-08-19 16:44:21
-
浏览量:48879次2019-07-26 15:43:22
-
浏览量:1144次2023-04-19 09:13:50
-
浏览量:5343次2021-05-08 15:04:36
-
浏览量:3035次2017-11-28 12:33:29
-
浏览量:1180次2024-02-02 10:51:53
-
浏览量:2948次2022-08-17 09:00:19
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
阿帅
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明