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

编译与运行

  1. 将上述两个文件放入EDK2源码树的AppPkg/Applications/ExitShellApp/目录(或自定义包目录)。
  2. 在包的.dsc文件中添加组件声明,例如:
    AppPkg/Applications/ExitShellApp/ExitShellApp.inf
  3. 编译:build -p AppPkg/AppPkg.dsc -m AppPkg/Applications/ExitShellApp/ExitShellApp.inf
  4. 将生成的ExitShellApp.efi复制到启动盘的EFI/BOOT/目录或通过QEMU虚拟磁盘挂载。
  5. 启动至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后光标消失:通常是因为应用修改了控制台属性未恢复,本案例未涉及控制台修改,若扩展功能需在退出前恢复原属性。