UEFI标准应用程序工程模块不方便处理命令行参数。但是一般在shell中执行的命令都会带有命令行参数。为了解决这个问题,EDK2提供了Shell应用程序工程模块。
Shell应用程序工程模块的入口函数:
INTN ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
仿照标准应用程序工程模块编写Shell应用程序工程模块的源文件:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
INTN ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
{
gST->ConOut->OutputString (gST->ConOut,L"Hello man, welcome to UEFI world \n");
return 0;
}
现在可以看到入口函数ShellAppMain
有了类似于C/C++
中的参数。但是,现在没有了SystemTable,所以可以通过全局变量gTS使用系统表。Argv列表中的每个参数都是Unicode字符串。
基本与标准应用程序工程模块相同,除了EntryPoint
。在标准应用程序工程模块中,入口函数名,需要开发者自己定义,需要保持工程文件与源文件一致,一般设置为UefiMain。但是在Shell应用程序工程模块中,它必须设置为ShellAppMain
。
语法与标准应用程序工程模块基本相同,只是必须列出MdePkg/MdePkg.dec
和ShellPkg/ShellPkg.dec
。
必须列出ShellCEntryLib,通常还要列出 UefiBootServicesTableLib
和UefiLib
。
其它块语法与标准应用程序工程模块相同。
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = HelloWorld
FILE_GUID = 00000000-0000-0000-0000-000000000000
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = ShellCEntryLib
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
ShellCEntryLib
UefiBootServicesTableLib
UefiLib
[BuildOptions]
MSFT:*_*_*_CC_FLAGS = /w
基本上和标准应用程序工程模块相同,我们可以将工程文件相对于EDK2根目录的路径名添加到ShellPkg.dsc [Components]里。
执行下面的命令:
build -p ShellPkg\ShellPkg.dsc -m [工程文件相对EDK2根目录路径名] -a IA32 (64位用:X64)
在UEFI原理与编程(二):UEFI工程模块文件-标准应用程序工程模块中提到,它是应用程序工程模块的基础,那么Shell应用程序工程模块入口函数是如何做到可以传递命令行参数的?可以查看ShellCEntryLib的源代码:
//路径:*/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c*
EFI_STATUS
EFIAPI
ShellCEntryLib (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
INTN ReturnFromMain;
EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol;
EFI_SHELL_INTERFACE *EfiShellInterface;
EFI_STATUS Status;
ReturnFromMain = -1;
EfiShellParametersProtocol = NULL;
EfiShellInterface = NULL;
Status = SystemTable->BootServices->OpenProtocol(ImageHandle,
&gEfiShellParametersProtocolGuid,
(VOID **)&EfiShellParametersProtocol,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR(Status)) {
//
// use shell 2.0 interface
//
ReturnFromMain = ShellAppMain (
EfiShellParametersProtocol->Argc,
EfiShellParametersProtocol->Argv
);
} else {
//
// try to get shell 1.0 interface instead.
//
Status = SystemTable->BootServices->OpenProtocol(ImageHandle,
&gEfiShellInterfaceGuid,
(VOID **)&EfiShellInterface,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR(Status)) {
//
// use shell 1.0 interface
//
ReturnFromMain = ShellAppMain (
EfiShellInterface->Argc,
EfiShellInterface->Argv
);
} else {
ASSERT(FALSE);
}
}
return ReturnFromMain;
}
从源代码的入口函数:
EFI_STATUS EFIAPI
ShellCEntryLib (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
可以看到它的参数可标准应用程序工程模块的入口函数参数实现共同的。在ShellCEntryLib中,首先打开EFI_SHELL_PARAMETERS_PROTOCOL
可以获取命令行参数 Argc
,Argv
,然后调用ShellAppMain 函数。
关于 EFI_SHELL_PARAMETERS_PROTOCOL
源代码
typedef struct _EFI_SHELL_PARAMETERS_PROTOCOL {
///
/// Points to an Argc-element array of points to NULL-terminated strings containing
/// the command-line parameters. The first entry in the array is always the full file
/// path of the executable. Any quotation marks that were used to preserve
/// whitespace have been removed.
///
CHAR16 **Argv;
///
/// The number of elements in the Argv array.
///
UINTN Argc;
///
/// The file handle for the standard input for this executable. This may be different
/// from the ConInHandle in EFI_SYSTEM_TABLE.
///
SHELL_FILE_HANDLE StdIn;
///
/// The file handle for the standard output for this executable. This may be different
/// from the ConOutHandle in EFI_SYSTEM_TABLE.
///
SHELL_FILE_HANDLE StdOut;
///
/// The file handle for the standard error output for this executable. This may be
/// different from the StdErrHandle in EFI_SYSTEM_TABLE.
///
SHELL_FILE_HANDLE StdErr;
} EFI_SHELL_PARAMETERS_PROTOCOL;
发现它除了Argc
和Argv
还有SHELL_FILE_HANDLE
类型的成员:
Shell应用程序工程模块是在标准应用程序模块的基础之上利用Protocol做了封装,使我们可以很好地编写带命令行参数的UEFI程序。
本部分demo是在已经安装了EDK2环境基础上编译运行的。如果还没有安装,可以参考:UEFI原理与编程(一):环境搭建。
标准应用程序工程模块内容可以参考:UEFI原理与编程(二):UEFI工程模块文件-标准应用程序工程模块
<1>《UEFI原理与编程》戴正华 著
<2> UEFI Spec2_6