UEFI开发学习22 – 退出码

简介

退出码(Exit Code),也可以当作是程序的返回值。在C语言中,主函数main可以写成void类型,可以不用返回任何值,但这样程序运行后,父级进程无法获得该程序的任何状态,对于一些有先后执行顺序的程序来说,这样是很危险的,因为如果前一个没执行成功,又继续执行后面的程序,可能会导致系统崩溃。所以有些编译器会检查main的类型,如果是void,则会编译报错。

在使用C库的EFI APP中,虽然也可以使用void类型的main,但并不推荐这么做,最好养成一个好习惯,使用具有返回值的类型。

而在标准UEFI的入口函数中,其类型定义是EFI_STATUS,即函数最后必须返回一个值。该值返回后,可以被父级程序捕捉到,或保存在控制台的lasterror环境变量中,脚本同样能获取该值,这样便可以检验程序的运行状态,进行一些错误处理。

APP错误处理

上一篇文章中,写了如何运行一个 EFI APP ,那如何判断该 APP 的是否正常执行呢?方法就是获取它的exit code,下面还是以上篇文章的代码为例,用StartEfi.efi启动一个Helloworld.efi,Helloworld.efi 结束后,StartEfi.efi 打印 Helloworld.efi 的Status,如下:

// Helloworld.efi
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
    Print(L"Hello world!\n\r");

    return EFI_NOT_FOUND;
}
// StartEfi.efi 

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h> 
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Protocol/LoadedImage.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        MyImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                         Status;
  UINTN                              Index;
  UINTN                              NoHandles;
  EFI_HANDLE                       * Buffer;
  EFI_DEVICE_PATH_PROTOCOL         * DevicePath;
  EFI_HANDLE                         ImageHandle;
  CHAR16                           * EfiImageName = L"\\HelloWorld.efi";

  Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &NoHandles, &Buffer);
  if (EFI_ERROR(Status)) {
    Print(L"Unsupported\n\r");
    return 0;
  }

  for (Index = 0; Index < NoHandles; Index ++){
    DevicePath  = FileDevicePath (Buffer[Index], EfiImageName);
    Status = gBS->LoadImage (
                    FALSE,
                    gImageHandle,
                    DevicePath,
                    NULL,
                    0,
                    &ImageHandle
                    );
    if (EFI_ERROR (Status)) {
      Print(L"Load Image error %r %x\n\r", Status, gImageHandle);
      continue;
    }

    Status = gBS->StartImage (ImageHandle, NULL, NULL);
    if (Status == EFI_SUCCESS) {
      Print (L"Program run successfully!\n\r", Status);
    }
    else if (Status == EFI_INVALID_PARAMETER) {
      Print (L"Program can not start!\n\r");
    }
    else if (Status == EFI_SECURITY_VIOLATION) {
      Print (L"Program can not start!\n\r");
    }
    else {
      Print(L"Program exit with an error status: %r\n\r", Status);
      break;
    }
  }

  return Status;
}

执行结果如下:

控制台错误处理

控制台错误处理一般指的是在nsh脚本中通过判断环境变量lasterror进行的。我们可以进行如下实验:

1. 打开EFI shell,先输出lasterror的状态

2. 随意输入一串字符,然后回车,会提示无法识别的命令,此时再打印lasterror的状态

可看到,出错后lasterror变量会被置为非0值,该值对应于EFI_STATUS所定义的错误代码,部分如下:

下面是一个nsh脚本,运行上例HelloWorld.efi,然后输出运行结果:

@echo -off
HelloWorld.efi
if %lasterror% == 0 then
    echo Program run successfully!
else
    echo Program exit with an error: %lasterror%
endif

扩展

退出码不单单在EFI中用到,大多数的程序都是有这种机制的。比如在Windows中,在控制台执行一个命令,或运行一个程序,其退出码会写在errorlevel变量中,那我们就可以根据它来判断脚本后续的执行流程。一个简单的例子,如用脚本调用FPT刷BIOS,刷完后执行reset。

FPTW64.EXE -F xxx.rom
IF %errorlevel% == 0 (
  FPTW64.EXE -Greset
)

实例代码

https://gitee.com/ay123net/uefistudy

版权声明:
作者:bin
链接:https://ay123.net/mystudy/uefi/1491/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
UEFI开发学习22 – 退出码
简介 退出码(Exit Code),也可以当作是程序的返回值。在C语言中,主函数main可以写成void类型,可以不用返回任何值,但这样程序运行后,父级进程无法获得该……
<<上一篇
下一篇>>
文章目录
关闭
目 录