【UEFI】UEFI Shell下带参数的Application

1.简述

前几天需要写一个发送数据的Application,要在命令后面加几个数据,所以写了一个demo先测试一下Application中加参数的相关函数。
EDK2版本:edk2-stable/202011(所有代码均基于开源代码)

2.代码

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/ShellCommandLib.h>
#include <Library/DebugLib.h>
STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
  {L"-aa", TypeValue},
  {L"-cc", TypeValue},
  {NULL, TypeMax}
  };
  //临时debug用
// typedef struct {
//   LIST_ENTRY     Link;
//   CHAR16         *Name;
//   SHELL_PARAM_TYPE      Type;
//   CHAR16         *Value;
//   UINTN          OriginalPosition;
// } SHELL_PARAM_PACKAGE;
  //临时debug用
/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the application.
  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.
  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print(L"Hello,this is Entry of UefiMain!\n");
  EFI_STATUS          Status;
  LIST_ENTRY          *Package;
  CHAR16              *ProblemParam;
  // SHELL_STATUS        ShellStatus;
  // UINT8               Level;
  CONST CHAR16        *ParamAA ;  //?????                     
  CONST CHAR16        *ParamCC ;  
  UINT16            intParamAA = 0;        
  UINT16            intParamCC = 0;
  // Level = PcdGet8(PcdShellSupportLevel);
  ProblemParam        = NULL;
  // ShellStatus         = SHELL_SUCCESS;
  //
  // initialize the shell lib (we must be in non-auto-init...)
  //
  Status = ShellInitialize();
  if (EFI_ERROR(Status)) {
    DEBUG ((EFI_D_INFO, "[ZK] !!!ShellInitialize Fail!,Status :%r.\n", Status));
  }
  Status = CommandInit();
  if (EFI_ERROR(Status)) {
    DEBUG ((EFI_D_INFO, "[ZK] !!!CommandInit Fail!,Status :%r.\n", Status));
  }
  //
  // parse the command line
  //
  Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
//临时debug
  // ParamAA =  ShellCommandLineGetValue(Package, L"-a");
  // LIST_ENTRY     *Node;
  //  for ( Node = GetFirstNode(Package)
  //     ; !IsNull (Package, Node)
  //     ; Node = GetNextNode(Package, Node)
  //     ){
  //       DEBUG ((EFI_D_INFO, "-------------------------------------------------\n"));
  //       DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Name = %s\n",((SHELL_PARAM_PACKAGE*)Node)->Name));
  //       DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Value = %s\n",((SHELL_PARAM_PACKAGE*)Node)->Value));
  //       DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Type = %d\n",((SHELL_PARAM_PACKAGE*)Node)->Type));
  //       // }
  //     }
 
  //   DEBUG ((EFI_D_INFO, "OVER"));
//临时debug

  
  //如果ShellCommandLineParse失败
  if (EFI_ERROR(Status)) {
    DEBUG ((EFI_D_INFO, "[ZK] !!!ShellCommandLineParse Fail!,Status :%r.\n", Status));
  } else {
    if (ShellCommandLineGetFlag(Package, L"-aa")) { //-a
      ParamAA =  ShellCommandLineGetValue(Package, L"-aa");
      if(ParamAA != NULL){
        DEBUG ((EFI_D_INFO, "ParamAA = %s\n", ParamAA));
        intParamAA = (UINT8)ShellStrToUintn(ParamAA);
      }
    }
    if (ShellCommandLineGetFlag(Package, L"-cc")) { //-b
      ParamCC = ShellCommandLineGetValue(Package, L"-cc");
      if(ParamAA != NULL){
        DEBUG ((EFI_D_INFO, "ParamCC = %s\n", ParamCC));
        intParamCC = (UINT8)ShellStrToUintn(ParamCC);
      }
    }
    // free the command line package
    ShellCommandLineFreeVarList (Package);
  }
  Print(L"intParamAA = %d,intParamCC = %d\n",  intParamAA, intParamCC);
  Print(L"-----------------------------------\n");
  // DEBUG ((EFI_D_INFO, "ParamAA = %s\n", ParamAA));
  // DEBUG ((EFI_D_INFO, "ParamCC = %s\n", ParamCC));
  return EFI_SUCCESS;
}

3.重要数据结构和函数解释

3.1数组 ParamList[]

STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
  {L"-aa", TypeValue},
  {L"-cc", TypeValue},
  {NULL, TypeMax}
  };

ParamList[]数组中:"-aa""-cc"就是我设置的参数,TypeValue 是指形式如:"-aa 1""-cc 2",关于TypeValue 的解释可以在<shelllib.h>中看到,其中的注释也写得非常明白:

typedef enum {
  TypeFlag  = 0,    ///< A flag that is present or not present only (IE "-a").
  TypeValue,        ///< A flag that has some data following it with a space (IE "-a 1").
  TypePosition,     ///< Some data that did not follow a parameter (IE "filename.txt").
  TypeStart,        ///< A flag that has variable value appended to the end (IE "-ad", "-afd", "-adf", etc...).
  TypeDoubleValue,  ///< A flag that has 2 space seperated value data following it (IE "-a 1 2").
  TypeMaxValue,     ///< A flag followed by all the command line data before the next flag.
  TypeTimeValue,    ///< A flag that has a time value following it (IE "-a -5:00").
  TypeMax,
} SHELL_PARAM_TYPE;

3.2函数 CommandInit()和ShellInitialize()

两个初始化函数,要把相关库放到inf文件中,dsc文件也要相应做配置

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  ShellLib
  DebugLib
  HandleParsingLib
  ShellCommandLib
  IpmiBaseLib

其中 HandleParsingLib这个lib我们其实没用到,是包在ShellCommandLib中的,但是在编译过程中一直报HandleParsingLib这个库"not found"的错误,所以我把它也移到这里了。

3.3函数 ShellCommandLineParse()

这个函数用来解析命令行,有一个宏定义:

#define ShellCommandLineParse(CheckList,CheckPackage,ProblemParam,AutoPageBreak) ShellCommandLineParseEx(CheckList,CheckPackage,ProblemParam,AutoPageBreak,FALSE)

所以看一下函数ShellCommandLineParseEx的声明:

EFI_STATUS
EFIAPI
ShellCommandLineParseEx (
  IN CONST SHELL_PARAM_ITEM     *CheckList,
  OUT LIST_ENTRY                **CheckPackage,
  OUT CHAR16                    **ProblemParam OPTIONAL,
  IN BOOLEAN                    AutoPageBreak,
  IN BOOLEAN                    AlwaysAllowNumbers
  )
{
	...
}

看一下这个函数的参数

参数解释
CheckList这个就是3.1中的数组,存储参数的flag
CheckPackage这里面存的是解析出来的参数
CheckPackage将非CheckList数组中的参数保存到此 ,认为是有问题的参数
AutoPageBreakTRUE
AlwaysAllowNumbers宏中定义设置为了False

3.4函数 ShellCommandLineGetFlag()和ShellCommandLineGetValue()

ShellCommandLineGetFlag用来检查是否有你输入的参数,有就返回True,否则就返回False,比如:

if (ShellCommandLineGetFlag(Package, L"-aa"))

就是判断是否有“-aa”这个Flag出现,出现就返回True

ShellCommandLineGetValue()用来获取某个Flag后面的值,如:
ShellCommandLineGetValue(Package, L"-aa")用来获取"-aa"这个Flag后输入的值。

3.5函数 ShellStrToUintn()

ShellStrToUintn()用于string和Uint的类型转换,返回是UINT64的值,程序里强制转换成了UNIT8

intParamCC = (UINT8)ShellStrToUintn(ParamCC);

4.问题与解决

遇到了一个非常坑爹的问题,本来是打算做一个demo,初始的flag就很随意的设置成了"-a""-b",结果一直报错,我就去看了一下解析出来的CheckPackage里面到底是什么,就有了被注释掉的代码。

 typedef struct {
   LIST_ENTRY     Link;
   CHAR16         *Name;
   SHELL_PARAM_TYPE      Type;
   CHAR16         *Value;
   UINTN          OriginalPosition;
 } SHELL_PARAM_PACKAGE;

这个结构体中就是CheckPackage的结构,"Name"就是Flag,"Value"就是Flag后面跟的值。用如下代码输入打印所有CheckPackage里面的东西:

//临时debug
   ParamAA =  ShellCommandLineGetValue(Package, L"-a");
   LIST_ENTRY     *Node;
    for ( Node = GetFirstNode(Package)
       ; !IsNull (Package, Node)
       ; Node = GetNextNode(Package, Node)
       ){
         DEBUG ((EFI_D_INFO, "-------------------------------------------------\n"));
         DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Name = %s\n",((SHELL_PARAM_PACKAGE*)Node)->Name));
         DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Value = %s\n",((SHELL_PARAM_PACKAGE*)Node)->Value));
         DEBUG ((EFI_D_INFO, "((SHELL_PARAM_PACKAGE*)Node)->Type = %d\n",((SHELL_PARAM_PACKAGE*)Node)->Type));
          }
       }
 
     DEBUG ((EFI_D_INFO, "OVER"));
//临时debug

最后输出的的flag和value根本对不上,我就看了一下,在函数ShellCommandLineParse()的最后中看到了如下code:

  //
  // support for AutoPageBreak
  //
  if (AutoPageBreak && ShellCommandLineGetFlag(*CheckPackage, L"-b")) {
    ShellSetPageBreakMode(TRUE);
  }

AutoPageBreak 我设置的是TRUE,而且我用的flag是"-b",就是这个导致了最后的输出不对。
所以教训就是:AutoPageBreak 设置为TRUE的时候不要用"-b"的flag,否则结果会很惨。无论你数组ParamList[]里面有没有设置"-b",都会有"-b"这个flag存在。

5.补充

1.对于函数ShellCommandLineParse()的返回值的判断:

Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
  //如果ShellCommandLineParse失败
  if (EFI_ERROR(Status)) {
    DEBUG ((EFI_D_INFO, "[ZK] !!!ShellCommandLineParse Fail!,Status :%r.\n", Status));
  } else {

ShellCommandLineParse() 这个函数针对不同情况有好几个返回值,看一下注释:

@retval EFI_SUCCESS           The operation completed sucessfully.
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
  @retval EFI_INVALID_PARAMETER A parameter was invalid.
  @retval EFI_VOLUME_CORRUPTED  The command line was corrupt.
  @retval EFI_DEVICE_ERROR      The commands contained 2 opposing arguments.  One
                                of the command line arguments was returned in
                                ProblemParam if provided.
  @retval EFI_NOT_FOUND         A argument required a value that was missing.
                                The invalid command line argument was returned in
                                ProblemParam if provided.

建议是针对不同的返回值做判断,特别是@retval EFI_INVALID_PARAMETER这个返回值,如果你输入的flag是数组ParamList[]中没有的,就会返回这一值。比如我的Flag是"-aa","-cc",但是如果我在shell下面用的时候输入了"-dd",这时候就会返回VOLUME_CORRUPTED,而且"-dd"以及后面跟的值会进入到ProblemParam 中,从而可以做判断。

2.数组ParamList[]中的类型一定要对,比如我是"-aa 1"这种就要用TypeValue,你如果是"-a",就要用TypeFlag ,如果混用就会出问题。一旦有问题建议用我注释掉的代码中的方法进行debug,可以看到他解析出来的命令到底是什么,再一步一步找问题所在。

3.许多程序都用到了ShellPrintHiiEx()这个函数,我没有用,因为我所有的信息都通过Print和Debug打印出来了,这个函数应该是一个高级打印函数吧。

6.结果

正常输入结果:
图1 正常输出
从下面图中可以看到:
第二个中我没有设置"-b"这个flag,但是输入-b没有报错,这就是上面提到的问题。
第三个中没有我没有设置"-a"这个flag,所以输入-a会报错。
在这里插入图片描述

参考

1.https://blog.csdn.net/Lq19880521/article/details/112294014
2.源码:edk2-202011\edk2\ShellPkg\Library\UefiShellLevel3CommandsLib\Ver.c

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值