有些时候,我们编写的 EFI 程序不希望有人改动,这个时候就可以考虑将程序“藏”到 Shell 中。本文就实验将 RU.EFI 编译到 Shell 中。为了更好的理解本文的方案,最好先阅读理解前介绍过的如何将一个 EFI 程序包含在另外的 EFI中运行的方法【参考1】。具体方法是将下面的代码直接插入上一篇的 lzc.c 中:
// // print it... // if (ShellStatus == SHELL_SUCCESS) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LZC_OUTPUT),gShellLevel3HiiHandle); //Your code DP=FileDevicePath(NULL,L"fso:\\fake.efi"); Print(L"%s\n",ConvertDevicePathToText(DP,TRUE,FALSE)); // // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, ImageHandle, DP, (VOID*)&Hello2_efi[0], sizeof(Hello2_efi), &NewHandle); if (EFI_ERROR(Status)) { Print(L"Load image Error! [%r]\n",Status); return 0; } // // now start the image, passing up exit data if the caller requested it // Status = gBS->StartImage( NewHandle, &ExitDataSizePtr, NULL ); if (EFI_ERROR(Status)) { Print(L"\nError during StartImage [%X]\n",Status); return 0; } Status = gBS->UnloadImage(NewHandle); if (EFI_ERROR(Status)) { Print(L"Un-Load image Error! %r\n",Status); return 0; }
但是,运行结果显示无法加载,错误原因是 Invalid Parameter。同样的代码单独编译后完全可以在 Shell 下运行。
经过VS2015 动态调试发现,gBS->LoadImage()传递的 ImageHandle 参数为 0,就是说下面这个函数入口处的 ImageHandle 直接被赋值为0.
/** Function for 'lzc' command. @param[in] ImageHandle Handle to the Image (NULL if Internal). @param[in] SystemTable Pointer to the System Table (NULL if Internal).**/SHELL_STATUSEFIAPIShellCommandRunLzc ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable )
经过研究,产生问题的原因是下面代码,因此在调用每一个命令的时候,入口上的 ImageHandle 直接就赋值为0.
edk202008\ShellPkg\Library\UefiShellCommandLib\UefiShellCommandLib.c
/** Checks if a command string has been registered for CommandString and if so it runs the previously registered handler for that command with the command line. If CommandString is NULL, then ASSERT(). If Sections is specified, then each section name listed will be compared in a casesensitive manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists, it will be appended to the returned help text. If the section does not exist, no information will be returned. If Sections is NULL, then all help text information available will be returned. @param[in] CommandString Pointer to the command name. This is the name found on the command line in the shell. @param[in, out] RetVal Pointer to the return vaule from the command handler. @param[in, out] CanAffectLE indicates whether this command's return value needs to be placed into LASTERROR environment variable. @retval RETURN_SUCCESS The handler was run. @retval RETURN_NOT_FOUND The CommandString did not match a registered command name. @sa SHELL_RUN_COMMAND**/RETURN_STATUSEFIAPIShellCommandRunCommandHandler ( IN CONST CHAR16 *CommandString, IN OUT SHELL_STATUS *RetVal, IN OUT BOOLEAN *CanAffectLE OPTIONAL )……………… if (RetVal != NULL) { *RetVal = Node->CommandHandler(NULL, gST); } else { Node->CommandHandler(NULL, gST); } return (RETURN_SUCCESS);
搜索其他命令作为参考,都没有使用ImageHandle作为参数,但是有使用 gImageHandle。于是,就使用 gImageHandle 作为参数。最终代码如下:
/** @file Main file for LABZ Command Test shell level 3 function. (C) Copyright 2015 Hewlett-Packard Development Company, L.P. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent**/#include "UefiShellLevel3CommandsLib.h"#include #include #include /** Function for 'lzc' command. @param[in] ImageHandle Handle to the Image (NULL if Internal). @param[in] SystemTable Pointer to the System Table (NULL if Internal).**/SHELL_STATUSEFIAPIShellCommandRunLzc ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ){ EFI_STATUS Status; LIST_ENTRY *Package; CHAR16 *ProblemParam; SHELL_STATUS ShellStatus; EFI_DEVICE_PATH *DP; EFI_HANDLE NewHandle; UINTN ExitDataSizePtr; ProblemParam = NULL; ShellStatus = SHELL_SUCCESS; //CpuBreakpoint(); // // initialize the shell lib (we must be in non-auto-init...) // Status = ShellInitialize(); ASSERT_EFI_ERROR(Status); // // parse the command line // Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE); if (EFI_ERROR(Status)) { if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel3HiiHandle, L"lzc", ProblemParam); FreePool(ProblemParam); ShellStatus = SHELL_INVALID_PARAMETER; } else { ASSERT(FALSE); } } else { // // check for "-?" // if (ShellCommandLineGetFlag(Package, L"-?")) { ASSERT(FALSE); } else if (ShellCommandLineGetRawValue(Package, 1) != NULL) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel3HiiHandle, L"lzc"); ShellStatus = SHELL_INVALID_PARAMETER; } else { // // print it... // if (ShellStatus == SHELL_SUCCESS) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LZC_OUTPUT),gShellLevel3HiiHandle); //Your code start DP=FileDevicePath(NULL,L"fso:\\fake.efi"); Print(L"%s\n",ConvertDevicePathToText(DP,TRUE,FALSE)); // // Load the image with: // FALSE - not from boot manager and NULL, 0 being not already in memory // Status = gBS->LoadImage( FALSE, gImageHandle, DP, (VOID*)&RU_efi[0], sizeof(RU_efi), &NewHandle); if (EFI_ERROR(Status)) { Print(L"Load image Error! [%r]\n",Status); return 0; } // // now start the image, passing up exit data if the caller requested it // Status = gBS->StartImage( NewHandle, &ExitDataSizePtr, NULL ); if (EFI_ERROR(Status)) { Print(L"\nError during StartImage [%X]\n",Status); return 0; } Status = gBS->UnloadImage(NewHandle); if (EFI_ERROR(Status)) { Print(L"Un-Load image Error! %r\n",Status); return 0; } //Your code end } } // // free the command line package // ShellCommandLineFreeVarList (Package); } return (ShellStatus);}
编译之后可以在 WinHost 下运行 lzc 命令(RU涉及到硬件的直接访问,界面闪现就会退出)。
模拟器里面的 Shell 运行自定义的 command
使用 build -a X64 -p ShellPkg\ShellPkg.dsc 命令后可以得到包含了 lzc 命令的 Shell ,放置到 U盘 \EFI\Boot\ 下,改名为 BootX64.efi 后即可从 U盘直接启, 有兴趣的朋友可以自己动手实验。
本文提到的代码可以在原文链接看到。