3. UEFI应用退出与返回Shell
任务目标
编写一个UEFI应用,在Shell中加载执行后能够正常退出并返回UEFI Shell提示符。通过该案例理解UEFI应用的入口点约定、返回值语义以及Shell如何回收应用占用的资源。
核心机制说明
UEFI应用与UEFI驱动不同,前者由Shell通过LoadImage/StartImage加载启动,后者由固件在Boot Manager阶段加载。应用退出只需从其入口点函数返回即可,返回值EFI_SUCCESS表示正常退出,Shell会回收应用映像占用的内存并重新显示提示符。若返回错误码,Shell同样会回收资源但会打印错误信息。应用退出时无需也不能调用ExitBootServices,因为Shell环境仍处于Boot Services阶段。
环境依赖
- EDK2开发环境(含
MdePkg、ShellPkg) - 可启动至UEFI Shell的QEMU虚拟机或真实硬件
- 编译工具链:EDK2默认的GCC或VS工具链
完整实现代码
以下为最小可复现的UEFI应用源码,文件名为ExitShellApp.c:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
// 打印一条信息,确认应用已启动
Print (L"ExitShellApp: 应用已启动,即将退出并返回Shell。\n");
// 可选:演示如何主动返回错误码(取消注释下一行可观察Shell错误提示)
// Status = EFI_INVALID_PARAMETER;
// return Status;
// 正常退出:返回EFI_SUCCESS
Status = EFI_SUCCESS;
return Status;
}
对应的ExitShellApp.inf模块描述文件:
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = ExitShellApp
FILE_GUID = 8A2B3C4D-5E6F-4A5B-8C9D-1E2F3A4B5C6D
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
ExitShellApp.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
UefiLib
UefiBootServicesTableLib
DebugLib
PrintLib
编译与运行
- 将上述两个文件放入EDK2源码树的
AppPkg/Applications/ExitShellApp/目录(或自定义包目录)。 - 在包的
.dsc文件中添加组件声明,例如:
AppPkg/Applications/ExitShellApp/ExitShellApp.inf - 编译:
build -p AppPkg/AppPkg.dsc -m AppPkg/Applications/ExitShellApp/ExitShellApp.inf - 将生成的
ExitShellApp.efi复制到启动盘的EFI/BOOT/目录或通过QEMU虚拟磁盘挂载。 - 启动至UEFI Shell,切换到文件所在文件系统(如
fs0:),执行:
ExitShellApp.efi
验证方法与判定标准
- 预期输出:Shell屏幕显示
ExitShellApp: 应用已启动,即将退出并返回Shell。,随后自动回到Shell提示符(如Shell>)。 - 退出码验证:在Shell中执行
echo %lasterror%,应显示Success(即0x0000000000000000)。 - 资源回收验证:连续执行该应用10次以上,Shell不报内存不足或崩溃,说明每次退出后映像内存均被正确回收。
- 错误路径验证(可选):取消源码中
Status = EFI_INVALID_PARAMETER;的注释并重新编译执行,Shell会打印错误信息但不会死机,%lasterror%显示错误码0x8000000000000002,证明错误码传递机制有效。
常见问题
- 编译报错找不到Print:确认
.inf中已包含UefiLib和PrintLib库。 - 执行后Shell无响应:检查应用是否误调用了
ExitBootServices,该调用会终止Shell环境导致系统挂起。 - 返回Shell后光标消失:通常是因为应用修改了控制台属性未恢复,本案例未涉及控制台修改,若扩展功能需在退出前恢复原属性。