在QEMU 的Boot Manager/Menu 中可以看到”EFI Internal Shell” 这个选项,选中之后会跳转到其中执行 Shell.efi,这次就尝试编代码在其中插入能够启动到自己Application的选项。
首先我们研究一下这个选项是如何插入的。在\ovmfpkg\library\platformbootmanagerlib\BdsPlatform.c 的 PlatformBootManagerAfterConsole()函数中有:
//
// Perform some platform specific connect sequence
//
PlatformBdsConnectSequence ();
EfiBootManagerRefreshAllBootOption ();
//
// Register UEFI Shell
//
PlatformRegisterFvBootOption (
&gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
);
RemoveStaleFvFileOptions ();
SetBootOrderFromQemu ();
其中gUefiShellFileGuid 的定义在 \ShellPkg\ShellPkg.dec
# FILE_GUID as defined in ShellPkg/Application/Shell/Shell.inf
gUefiShellFileGuid = {0x7c04a583, 0x9e3e, 0x4f1c, {0xad, 0x65, 0xe0, 0x52, 0x68, 0xd0, 0xb4, 0xd1}}
也可以在\ShellPkg\Application\Shell\Shell.inf 中看到
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = Shell
FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
使用工具可以看到:
接下来我们着手设计一个自己的 Application 用于验证,需要特别注意的是,这个程序不能使用 SHELL 的任何Protocol和Service,因为调用它的时候没有 Shell。代码非常简单:
1. BootTest.c 如下
#include <Uefi.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
for (int i=0;i<1000;i++) {
SystemTable->ConOut->OutputString(
SystemTable->ConOut,
L"www.lab-z.com\n");
}
return(0);
}
2. BootTest.inf如下:
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = BootTest
FILE_GUID = a912f198-7f0e-4803-b908-b757b806ec89
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = UefiMain
#
# VALID_ARCHITECTURES = IA32 X64 IPF
#
[Sources]
BootTest.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
UefiLib
UefiApplicationEntryPoint
DebugLib
EDK2 202108 已经没有 AppPkg , 所以我们直接放在 ShellPkg 中。接下来修改代码如下:
1.\OvmfPkg\OvmfPkgX64.dsc 中,插入 INF 文件
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
}
ShellPkg/Application/BootTest/BootTest.inf
!if $(SECURE_BOOT_ENABLE) == TRUE
SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf
!endif
2.\OvmfPkg\OvmfPkgX64.fdf 中插入
!if $(TOOL_CHAIN_TAG) != "XCODE5"
INF ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
INF ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
INF OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
!endif
INF ShellPkg/Application/Shell/Shell.inf
INF ShellPkg/Application/BootTest/BootTest.inf
INF MdeModulePkg/Logo/LogoDxe.inf
#
# Network modules
#
!if $(E1000_ENABLE)
FILE DRIVER = 5D695E11-9B3F-4b83-B25F-4A8D5D69BE07 {
SECTION PE32 = Intel3.5/EFIX64/E3522X2.EFI
}
3. \OvmfPkg\Library\PlatformBootManagerLib\BdsPlatform.c 文件做插入 BootMenu的动作
定义一个 GUID,来自 BootTest.inf 中的FILE_GUID
EFI_GUID gBootTest = {
0xa912f198, 0x7f0e, 0x4803,
{ 0xb9, 0x08, 0xb7, 0x57, 0xb8, 0x06, 0xec, 0x89 } };
插入动作:
//
// Perform some platform specific connect sequence
//
PlatformBdsConnectSequence ();
EfiBootManagerRefreshAllBootOption ();
DEBUG((DEBUG_INFO, "LABZTest_Start\n"));
//
// Register a Test Application
//
PlatformRegisterFvBootOption(
&gBootTest, L"LABZTest", LOAD_OPTION_ACTIVE
);
DEBUG((DEBUG_INFO, "LABZTest_End\n"));
//
// Register UEFI Shell
//
PlatformRegisterFvBootOption (
&gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
);
重新编译之后,可以用工具看到我们插入的 Application:
运行结果:
可以看到 BootMenu 中出现了我们设定的启动选项。
本文提到的用于测试的 BootTest: