| /** @file
|
| Provides interface to shell internal functions for shell commands.
|
|
|
| Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
|
| (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR> |
| (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> |
| |
| This program and the accompanying materials
|
| are licensed and made available under the terms and conditions of the BSD License
|
| which accompanies this distribution. The full text of the license may be found at
|
| http://opensource.org/licenses/bsd-license.php
|
|
|
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
| **/
|
|
|
| #include "UefiShellCommandLib.h"
|
|
|
| // STATIC local variables
|
| STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
|
| STATIC SCRIPT_FILE_LIST mScriptList;
|
| STATIC ALIAS_LIST mAliasList;
|
| STATIC BOOLEAN mEchoState;
|
| STATIC BOOLEAN mExitRequested;
|
| STATIC UINT64 mExitCode;
|
| STATIC BOOLEAN mExitScript;
|
| STATIC CHAR16 *mProfileList;
|
| STATIC UINTN mProfileListSize;
|
| STATIC UINTN mFsMaxCount = 0;
|
| STATIC UINTN mBlkMaxCount = 0;
|
| STATIC BUFFER_LIST mFileHandleList;
|
|
|
| STATIC CONST CHAR8 Hex[] = {
|
| '0',
|
| '1',
|
| '2',
|
| '3',
|
| '4',
|
| '5',
|
| '6',
|
| '7',
|
| '8',
|
| '9',
|
| 'A',
|
| 'B',
|
| 'C',
|
| 'D',
|
| 'E',
|
| 'F'
|
| };
|
|
|
| // global variables required by library class.
|
| EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
|
| SHELL_MAP_LIST gShellMapList;
|
| SHELL_MAP_LIST *gShellCurDir = NULL;
|
|
|
| CONST CHAR16* SupportLevel[] = {
|
| L"Minimal",
|
| L"Scripting",
|
| L"Basic",
|
| L"Interactive"
|
| };
|
|
|
| /**
|
| Function to make sure that the global protocol pointers are valid.
|
| must be called after constructor before accessing the pointers.
|
| **/
|
| EFI_STATUS
|
| EFIAPI
|
| CommandInit(
|
| VOID
|
| )
|
| {
|
| EFI_STATUS Status;
|
| if (gUnicodeCollation == NULL) {
|
| Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
|
| if (EFI_ERROR(Status)) {
|
| return (EFI_DEVICE_ERROR);
|
| }
|
| }
|
| return (EFI_SUCCESS);
|
| }
|
|
|
| /**
|
| Constructor for the Shell Command library.
|
|
|
| Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
|
|
|
| @param ImageHandle the image handle of the process
|
| @param SystemTable the EFI System Table pointer
|
|
|
| @retval EFI_SUCCESS the initialization was complete sucessfully
|
| **/
|
| RETURN_STATUS
|
| EFIAPI
|
| ShellCommandLibConstructor (
|
| IN EFI_HANDLE ImageHandle,
|
| IN EFI_SYSTEM_TABLE *SystemTable
|
| )
|
| {
|
| EFI_STATUS Status;
|
| InitializeListHead(&gShellMapList.Link);
|
| InitializeListHead(&mCommandList.Link);
|
| InitializeListHead(&mAliasList.Link);
|
| InitializeListHead(&mScriptList.Link);
|
| InitializeListHead(&mFileHandleList.Link);
|
| mEchoState = TRUE;
|
|
|
| mExitRequested = FALSE;
|
| mExitScript = FALSE;
|
| mProfileListSize = 0;
|
| mProfileList = NULL;
|
|
|
| if (gUnicodeCollation == NULL) {
|
| Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
|
| if (EFI_ERROR(Status)) {
|
| return (EFI_DEVICE_ERROR);
|
| }
|
| }
|
|
|
| return (RETURN_SUCCESS);
|
| }
|
|
|
| /**
|
| Frees list of file handles.
|
|
|
| @param[in] List The list to free.
|
| **/
|
| VOID
|
| EFIAPI
|
| FreeFileHandleList (
|
| IN BUFFER_LIST *List
|
| )
|
| {
|
| BUFFER_LIST *BufferListEntry;
|
|
|
| if (List == NULL){
|
| return;
|
| }
|
| //
|
| // enumerate through the buffer list and free all memory
|
| //
|
| for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
|
| ; !IsListEmpty (&List->Link)
|
| ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
|
| ){
|
| RemoveEntryList(&BufferListEntry->Link);
|
| ASSERT(BufferListEntry->Buffer != NULL);
|
| SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE*)(BufferListEntry->Buffer))->Path);
|
| SHELL_FREE_NON_NULL(BufferListEntry->Buffer);
|
| SHELL_FREE_NON_NULL(BufferListEntry);
|
| }
|
| }
|
|
|
| /**
|
| Destructor for the library. free any resources.
|
|
|
| @param ImageHandle the image handle of the process
|
| @param SystemTable the EFI System Table pointer
|
|
|
| @retval RETURN_SUCCESS this function always returns success
|
| **/
|
| RETURN_STATUS
|
| EFIAPI
|
| ShellCommandLibDestructor (
|
| IN EFI_HANDLE ImageHandle,
|
| IN EFI_SYSTEM_TABLE *SystemTable
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
| ALIAS_LIST *Node2;
|
| SCRIPT_FILE_LIST *Node3;
|
| SHELL_MAP_LIST *MapNode;
|
| //
|
| // enumerate throught the list and free all the memory
|
| //
|
| while (!IsListEmpty (&mCommandList.Link)) {
|
| Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
|
| RemoveEntryList(&Node->Link);
|
| SHELL_FREE_NON_NULL(Node->CommandString);
|
| FreePool(Node);
|
| DEBUG_CODE(Node = NULL;);
|
| }
|
|
|
| //
|
| // enumerate through the alias list and free all memory
|
| //
|
| while (!IsListEmpty (&mAliasList.Link)) {
|
| Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link);
|
| RemoveEntryList(&Node2->Link);
|
| SHELL_FREE_NON_NULL(Node2->CommandString);
|
| SHELL_FREE_NON_NULL(Node2->Alias);
|
| SHELL_FREE_NON_NULL(Node2);
|
| DEBUG_CODE(Node2 = NULL;);
|
| }
|
|
|
| //
|
| // enumerate throught the list and free all the memory
|
| //
|
| while (!IsListEmpty (&mScriptList.Link)) {
|
| Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
|
| RemoveEntryList(&Node3->Link);
|
| DeleteScriptFileStruct(Node3->Data);
|
| FreePool(Node3);
|
| }
|
|
|
| //
|
| // enumerate throught the mappings list and free all the memory
|
| //
|
| if (!IsListEmpty(&gShellMapList.Link)) {
|
| for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
|
| ; !IsListEmpty (&gShellMapList.Link)
|
| ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
|
| ){
|
| ASSERT(MapNode != NULL);
|
| RemoveEntryList(&MapNode->Link);
|
| SHELL_FREE_NON_NULL(MapNode->DevicePath);
|
| SHELL_FREE_NON_NULL(MapNode->MapName);
|
| SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
|
| FreePool(MapNode);
|
| }
|
| }
|
| if (!IsListEmpty(&mFileHandleList.Link)){
|
| FreeFileHandleList(&mFileHandleList);
|
| }
|
|
|
| if (mProfileList != NULL) {
|
| FreePool(mProfileList);
|
| }
|
|
|
| gUnicodeCollation = NULL;
|
| gShellCurDir = NULL;
|
|
|
| return (RETURN_SUCCESS);
|
| }
|
|
|
| /**
|
| Find a dynamic command protocol instance given a command name string.
|
|
|
| @param CommandString the command name string
|
|
|
| @return instance the command protocol instance, if dynamic command instance found
|
| @retval NULL no dynamic command protocol instance found for name
|
| **/
|
| CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
|
| EFIAPI
|
| ShellCommandFindDynamicCommand (
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| EFI_STATUS Status;
|
| EFI_HANDLE *CommandHandleList;
|
| EFI_HANDLE *NextCommand;
|
| EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
| CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
|
| if (CommandHandleList == NULL) {
|
| //
|
| // not found or out of resources
|
| //
|
| return NULL;
|
| }
|
|
|
| for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
|
| Status = gBS->HandleProtocol(
|
| *NextCommand,
|
| &gEfiShellDynamicCommandProtocolGuid,
|
| (VOID **)&DynamicCommand
|
| );
|
|
|
| if (EFI_ERROR(Status)) {
|
| continue;
|
| }
|
|
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)CommandString,
|
| (CHAR16*)DynamicCommand->CommandName) == 0
|
| ){
|
| FreePool(CommandHandleList);
|
| return (DynamicCommand);
|
| }
|
| }
|
|
|
| FreePool(CommandHandleList);
|
| return (NULL);
|
| }
|
|
|
| /**
|
| Checks if a command exists as a dynamic command protocol instance
|
|
|
| @param[in] CommandString The command string to check for on the list.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandDynamicCommandExists (
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| return (BOOLEAN) ((ShellCommandFindDynamicCommand(CommandString) != NULL));
|
| }
|
|
|
| /**
|
| Checks if a command is already on the internal command list.
|
|
|
| @param[in] CommandString The command string to check for on the list.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandIsCommandOnInternalList(
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
|
|
| //
|
| // assert for NULL parameter
|
| //
|
| ASSERT(CommandString != NULL);
|
|
|
| //
|
| // check for the command
|
| //
|
| for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
|
| ; !IsNull(&mCommandList.Link, &Node->Link)
|
| ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
|
| ){
|
| ASSERT(Node->CommandString != NULL);
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)CommandString,
|
| Node->CommandString) == 0
|
| ){
|
| return (TRUE);
|
| }
|
| }
|
| return (FALSE);
|
| }
|
|
|
| /**
|
| Checks if a command exists, either internally or through the dynamic command protocol.
|
|
|
| @param[in] CommandString The command string to check for on the list.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandIsCommandOnList(
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| if (ShellCommandIsCommandOnInternalList(CommandString)) {
|
| return TRUE;
|
| }
|
|
|
| return ShellCommandDynamicCommandExists(CommandString);
|
| }
|
|
|
| /**
|
| Get the help text for a dynamic command.
|
|
|
| @param[in] CommandString The command name.
|
|
|
| @retval NULL No help text was found.
|
| @return String of help text. Caller required to free.
|
| **/
|
| CHAR16*
|
| EFIAPI
|
| ShellCommandGetDynamicCommandHelp(
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
| DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand(CommandString);
|
| if (DynamicCommand == NULL) {
|
| return (NULL);
|
| }
|
|
|
| //
|
| // TODO: how to get proper language?
|
| //
|
| return DynamicCommand->GetHelp(DynamicCommand, "en");
|
| }
|
|
|
| /**
|
| Get the help text for an internal command.
|
|
|
| @param[in] CommandString The command name.
|
|
|
| @retval NULL No help text was found.
|
| @return String of help text. Caller reuiqred to free.
|
| **/
|
| CHAR16*
|
| EFIAPI
|
| ShellCommandGetInternalCommandHelp(
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
|
|
| //
|
| // assert for NULL parameter
|
| //
|
| ASSERT(CommandString != NULL);
|
|
|
| //
|
| // check for the command
|
| //
|
| for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
|
| ; !IsNull(&mCommandList.Link, &Node->Link)
|
| ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
|
| ){
|
| ASSERT(Node->CommandString != NULL);
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)CommandString,
|
| Node->CommandString) == 0
|
| ){
|
| return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
|
| }
|
| }
|
| return (NULL);
|
| }
|
|
|
| /**
|
| Get the help text for a command.
|
|
|
| @param[in] CommandString The command name.
|
|
|
| @retval NULL No help text was found.
|
| @return String of help text.Caller reuiqred to free.
|
| **/
|
| CHAR16*
|
| EFIAPI
|
| ShellCommandGetCommandHelp (
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| CHAR16 *HelpStr;
|
| HelpStr = ShellCommandGetInternalCommandHelp(CommandString);
|
|
|
| if (HelpStr == NULL) {
|
| HelpStr = ShellCommandGetDynamicCommandHelp(CommandString);
|
| }
|
|
|
| return HelpStr;
|
| }
|
|
|
|
|
| /**
|
| Registers handlers of type SHELL_RUN_COMMAND and
|
| SHELL_GET_MAN_FILENAME for each shell command.
|
|
|
| If the ShellSupportLevel is greater than the value of the
|
| PcdShellSupportLevel then return RETURN_UNSUPPORTED.
|
|
|
| Registers the handlers specified by GetHelpInfoHandler and CommandHandler
|
| with the command specified by CommandString. If the command named by
|
| CommandString has already been registered, then return
|
| RETURN_ALREADY_STARTED.
|
|
|
| If there are not enough resources available to register the handlers then
|
| RETURN_OUT_OF_RESOURCES is returned.
|
|
|
| If CommandString is NULL, then ASSERT().
|
| If GetHelpInfoHandler is NULL, then ASSERT().
|
| If CommandHandler is NULL, then ASSERT().
|
| If ProfileName is NULL, then ASSERT().
|
|
|
| @param[in] CommandString Pointer to the command name. This is the
|
| name to look for on the command line in
|
| the shell.
|
| @param[in] CommandHandler Pointer to a function that runs the
|
| specified command.
|
| @param[in] GetManFileName Pointer to a function that provides man
|
| filename.
|
| @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
|
| function.
|
| @param[in] ProfileName profile name to require for support of this
|
| function.
|
| @param[in] CanAffectLE indicates whether this command's return value
|
| can change the LASTERROR environment variable.
|
| @param[in] HiiHandle Handle of this command's HII entry.
|
| @param[in] ManFormatHelp HII locator for the help text.
|
|
|
| @retval RETURN_SUCCESS The handlers were registered.
|
| @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
|
| register the shell command.
|
| @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
|
| currently allowed support level.
|
| @retval RETURN_ALREADY_STARTED The CommandString represents a command that
|
| is already registered. Only 1 handler set for
|
| a given command is allowed.
|
| @sa SHELL_GET_MAN_FILENAME
|
| @sa SHELL_RUN_COMMAND
|
| **/
|
| RETURN_STATUS
|
| EFIAPI
|
| ShellCommandRegisterCommandName (
|
| IN CONST CHAR16 *CommandString,
|
| IN SHELL_RUN_COMMAND CommandHandler,
|
| IN SHELL_GET_MAN_FILENAME GetManFileName,
|
| IN UINT32 ShellMinSupportLevel,
|
| IN CONST CHAR16 *ProfileName,
|
| IN CONST BOOLEAN CanAffectLE,
|
| IN CONST EFI_HANDLE HiiHandle,
|
| IN CONST EFI_STRING_ID ManFormatHelp
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
|
| INTN LexicalMatchValue;
|
|
|
| //
|
| // Initialize local variables.
|
| //
|
| Command = NULL;
|
| PrevCommand = NULL;
|
| LexicalMatchValue = 0;
|
|
|
| //
|
| // ASSERTs for NULL parameters
|
| //
|
| ASSERT(CommandString != NULL);
|
| ASSERT(GetManFileName != NULL);
|
| ASSERT(CommandHandler != NULL);
|
| ASSERT(ProfileName != NULL);
|
|
|
| //
|
| // check for shell support level
|
| //
|
| if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
|
| return (RETURN_UNSUPPORTED);
|
| }
|
|
|
| //
|
| // check for already on the list
|
| //
|
| if (ShellCommandIsCommandOnList(CommandString)) {
|
| return (RETURN_ALREADY_STARTED);
|
| }
|
|
|
| //
|
| // allocate memory for new struct
|
| //
|
| Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
|
| ASSERT(Node != NULL);
|
| Node->CommandString = AllocateCopyPool(StrSize(CommandString), CommandString);
|
| ASSERT(Node->CommandString != NULL);
|
|
|
| Node->GetManFileName = GetManFileName;
|
| Node->CommandHandler = CommandHandler;
|
| Node->LastError = CanAffectLE;
|
| Node->HiiHandle = HiiHandle;
|
| Node->ManFormatHelp = ManFormatHelp;
|
|
|
| if ( StrLen(ProfileName)>0
|
| && ((mProfileList != NULL
|
| && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
|
| ){
|
| ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
|
| if (mProfileList == NULL) {
|
| //
|
| // If this is the first make a leading ';'
|
| //
|
| StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
|
| }
|
| StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
|
| StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
|
| }
|
|
|
| //
|
| // Insert a new entry on top of the list
|
| //
|
| InsertHeadList (&mCommandList.Link, &Node->Link);
|
|
|
| //
|
| // Move a new registered command to its sorted ordered location in the list
|
| //
|
| for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
|
| PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
|
| ; !IsNull (&mCommandList.Link, &Command->Link)
|
| ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) {
|
|
|
| //
|
| // Get Lexical Comparison Value between PrevCommand and Command list entry
|
| //
|
| LexicalMatchValue = gUnicodeCollation->StriColl (
|
| gUnicodeCollation,
|
| PrevCommand->CommandString,
|
| Command->CommandString
|
| );
|
|
|
| //
|
| // Swap PrevCommand and Command list entry if PrevCommand list entry
|
| // is alphabetically greater than Command list entry
|
| //
|
| if (LexicalMatchValue > 0){
|
| Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link);
|
| } else if (LexicalMatchValue < 0) {
|
| //
|
| // PrevCommand entry is lexically lower than Command entry
|
| //
|
| break;
|
| }
|
| }
|
|
|
| return (RETURN_SUCCESS);
|
| }
|
|
|
| /**
|
| Function to get the current Profile string.
|
|
|
| @retval NULL There are no installed profiles.
|
| @return A semi-colon delimited list of profiles.
|
| **/
|
| CONST CHAR16 *
|
| EFIAPI
|
| ShellCommandGetProfileList (
|
| VOID
|
| )
|
| {
|
| return (mProfileList);
|
| }
|
|
|
| /**
|
| 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_STATUS
|
| EFIAPI
|
| ShellCommandRunCommandHandler (
|
| IN CONST CHAR16 *CommandString,
|
| IN OUT SHELL_STATUS *RetVal,
|
| IN OUT BOOLEAN *CanAffectLE OPTIONAL
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
| EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
| //
|
| // assert for NULL parameters
|
| //
|
| ASSERT(CommandString != NULL);
|
|
|
| //
|
| // check for the command
|
| //
|
| for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
|
| ; !IsNull(&mCommandList.Link, &Node->Link)
|
| ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
|
| ){
|
| ASSERT(Node->CommandString != NULL);
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)CommandString,
|
| Node->CommandString) == 0
|
| ){
|
| if (CanAffectLE != NULL) {
|
| *CanAffectLE = Node->LastError;
|
| }
|
| if (RetVal != NULL) {
|
| *RetVal = Node->CommandHandler(NULL, gST);
|
| } else {
|
| Node->CommandHandler(NULL, gST);
|
| }
|
| return (RETURN_SUCCESS);
|
| }
|
| }
|
|
|
| //
|
| // An internal command was not found, try to find a dynamic command
|
| //
|
| DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand(CommandString);
|
| if (DynamicCommand != NULL) {
|
| if (RetVal != NULL) {
|
| *RetVal = DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
|
| } else {
|
| DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
|
| }
|
| return (RETURN_SUCCESS);
|
| }
|
|
|
| return (RETURN_NOT_FOUND);
|
| }
|
|
|
| /**
|
| Checks if a command string has been registered for CommandString and if so it
|
| returns the MAN filename specified for that command.
|
|
|
| If CommandString is NULL, then ASSERT().
|
|
|
| @param[in] CommandString Pointer to the command name. This is the name
|
| found on the command line in the shell.\
|
|
|
| @retval NULL the commandString was not a registered command.
|
| @return other the name of the MAN file.
|
| @sa SHELL_GET_MAN_FILENAME
|
| **/
|
| CONST CHAR16*
|
| EFIAPI
|
| ShellCommandGetManFileNameHandler (
|
| IN CONST CHAR16 *CommandString
|
| )
|
| {
|
| SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
|
|
| //
|
| // assert for NULL parameters
|
| //
|
| ASSERT(CommandString != NULL);
|
|
|
| //
|
| // check for the command
|
| //
|
| for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
|
| ; !IsNull(&mCommandList.Link, &Node->Link)
|
| ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
|
| ){
|
| ASSERT(Node->CommandString != NULL);
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)CommandString,
|
| Node->CommandString) == 0
|
| ){
|
| return (Node->GetManFileName());
|
| }
|
| }
|
| return (NULL);
|
| }
|
|
|
| /**
|
| Get the list of all available shell internal commands. This is a linked list
|
| (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
|
| list functions. do not modify the values.
|
|
|
| @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
|
|
|
| @return a Linked list of all available shell commands.
|
| **/
|
| CONST COMMAND_LIST*
|
| EFIAPI
|
| ShellCommandGetCommandList (
|
| IN CONST BOOLEAN Sort
|
| )
|
| {
|
| // if (!Sort) {
|
| // return ((COMMAND_LIST*)(&mCommandList));
|
| // }
|
| return ((COMMAND_LIST*)(&mCommandList));
|
| }
|
|
|
| /**
|
| Registers aliases to be set as part of the initialization of the shell application.
|
|
|
| If Command is NULL, then ASSERT().
|
| If Alias is NULL, then ASSERT().
|
|
|
| @param[in] Command Pointer to the Command
|
| @param[in] Alias Pointer to Alias
|
|
|
| @retval RETURN_SUCCESS The handlers were registered.
|
| @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
|
| register the shell command.
|
| **/
|
| RETURN_STATUS
|
| EFIAPI
|
| ShellCommandRegisterAlias (
|
| IN CONST CHAR16 *Command,
|
| IN CONST CHAR16 *Alias
|
| )
|
| {
|
| ALIAS_LIST *Node;
|
| ALIAS_LIST *CommandAlias;
|
| ALIAS_LIST *PrevCommandAlias;
|
| INTN LexicalMatchValue;
|
|
|
| //
|
| // Asserts for NULL
|
| //
|
| ASSERT(Command != NULL);
|
| ASSERT(Alias != NULL);
|
|
|
| //
|
| // allocate memory for new struct
|
| //
|
| Node = AllocateZeroPool(sizeof(ALIAS_LIST));
|
| ASSERT(Node != NULL);
|
| Node->CommandString = AllocateCopyPool(StrSize(Command), Command);
|
| Node->Alias = AllocateCopyPool(StrSize(Alias), Alias);
|
| ASSERT(Node->CommandString != NULL);
|
| ASSERT(Node->Alias != NULL);
|
|
|
| InsertHeadList (&mAliasList.Link, &Node->Link);
|
|
|
| //
|
| // Move a new pre-defined registered alias to its sorted ordered location in the list
|
| //
|
| for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
|
| PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
|
| ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
|
| ; CommandAlias = (ALIAS_LIST *) GetNextNode (&mAliasList.Link, &CommandAlias->Link) ) {
|
| //
|
| // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
|
| //
|
| LexicalMatchValue = gUnicodeCollation->StriColl (
|
| gUnicodeCollation,
|
| PrevCommandAlias->Alias,
|
| CommandAlias->Alias
|
| );
|
|
|
| //
|
| // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
|
| // is alphabetically greater than CommandAlias list entry
|
| //
|
| if (LexicalMatchValue > 0) {
|
| CommandAlias = (ALIAS_LIST *) SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
|
| } else if (LexicalMatchValue < 0) {
|
| //
|
| // PrevCommandAlias entry is lexically lower than CommandAlias entry
|
| //
|
| break;
|
| }
|
| }
|
|
|
| return (RETURN_SUCCESS);
|
| }
|
|
|
| /**
|
| Get the list of all shell alias commands. This is a linked list
|
| (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
|
| list functions. do not modify the values.
|
|
|
| @return a Linked list of all requested shell alias'.
|
| **/
|
| CONST ALIAS_LIST*
|
| EFIAPI
|
| ShellCommandGetInitAliasList (
|
| VOID
|
| )
|
| {
|
| return (&mAliasList);
|
| }
|
|
|
| /**
|
| Determine if a given alias is on the list of built in alias'.
|
|
|
| @param[in] Alias The alias to test for
|
|
|
| @retval TRUE The alias is a built in alias
|
| @retval FALSE The alias is not a built in alias
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandIsOnAliasList(
|
| IN CONST CHAR16 *Alias
|
| )
|
| {
|
| ALIAS_LIST *Node;
|
|
|
| //
|
| // assert for NULL parameter
|
| //
|
| ASSERT(Alias != NULL);
|
|
|
| //
|
| // check for the Alias
|
| //
|
| for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
|
| ; !IsNull(&mAliasList.Link, &Node->Link)
|
| ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
|
| ){
|
| ASSERT(Node->CommandString != NULL);
|
| ASSERT(Node->Alias != NULL);
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)Alias,
|
| Node->CommandString) == 0
|
| ){
|
| return (TRUE);
|
| }
|
| if (gUnicodeCollation->StriColl(
|
| gUnicodeCollation,
|
| (CHAR16*)Alias,
|
| Node->Alias) == 0
|
| ){
|
| return (TRUE);
|
| }
|
| }
|
| return (FALSE);
|
| }
|
|
|
| /**
|
| Function to determine current state of ECHO. Echo determines if lines from scripts
|
| and ECHO commands are enabled.
|
|
|
| @retval TRUE Echo is currently enabled
|
| @retval FALSE Echo is currently disabled
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandGetEchoState(
|
| VOID
|
| )
|
| {
|
| return (mEchoState);
|
| }
|
|
|
| /**
|
| Function to set current state of ECHO. Echo determines if lines from scripts
|
| and ECHO commands are enabled.
|
|
|
| If State is TRUE, Echo will be enabled.
|
| If State is FALSE, Echo will be disabled.
|
|
|
| @param[in] State How to set echo.
|
| **/
|
| VOID
|
| EFIAPI
|
| ShellCommandSetEchoState(
|
| IN BOOLEAN State
|
| )
|
| {
|
| mEchoState = State;
|
| }
|
|
|
| /**
|
| Indicate that the current shell or script should exit.
|
|
|
| @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
|
| @param[in] ErrorCode The 64 bit error code to return.
|
| **/
|
| VOID
|
| EFIAPI
|
| ShellCommandRegisterExit (
|
| IN BOOLEAN ScriptOnly,
|
| IN CONST UINT64 ErrorCode
|
| )
|
| {
|
| mExitRequested = (BOOLEAN)(!mExitRequested);
|
| if (mExitRequested) {
|
| mExitScript = ScriptOnly;
|
| } else {
|
| mExitScript = FALSE;
|
| }
|
| mExitCode = ErrorCode;
|
| }
|
|
|
| /**
|
| Retrieve the Exit indicator.
|
|
|
| @retval TRUE Exit was indicated.
|
| @retval FALSE Exis was not indicated.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandGetExit (
|
| VOID
|
| )
|
| {
|
| return (mExitRequested);
|
| }
|
|
|
| /**
|
| Retrieve the Exit code.
|
|
|
| If ShellCommandGetExit returns FALSE than the return from this is undefined.
|
|
|
| @return the value passed into RegisterExit.
|
| **/
|
| UINT64
|
| EFIAPI
|
| ShellCommandGetExitCode (
|
| VOID
|
| )
|
| {
|
| return (mExitCode);
|
| }
|
| /**
|
| Retrieve the Exit script indicator.
|
|
|
| If ShellCommandGetExit returns FALSE than the return from this is undefined.
|
|
|
| @retval TRUE ScriptOnly was indicated.
|
| @retval FALSE ScriptOnly was not indicated.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellCommandGetScriptExit (
|
| VOID
|
| )
|
| {
|
| return (mExitScript);
|
| }
|
|
|
| /**
|
| Function to cleanup all memory from a SCRIPT_FILE structure.
|
|
|
| @param[in] Script The pointer to the structure to cleanup.
|
| **/
|
| VOID
|
| EFIAPI
|
| DeleteScriptFileStruct (
|
| IN SCRIPT_FILE *Script
|
| )
|
| {
|
| UINT8 LoopVar;
|
|
|
| if (Script == NULL) {
|
| return;
|
| }
|
|
|
| for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
|
| SHELL_FREE_NON_NULL(Script->Argv[LoopVar]);
|
| }
|
| if (Script->Argv != NULL) {
|
| SHELL_FREE_NON_NULL(Script->Argv);
|
| }
|
| Script->CurrentCommand = NULL;
|
| while (!IsListEmpty (&Script->CommandList)) {
|
| Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
|
| if (Script->CurrentCommand != NULL) {
|
| RemoveEntryList(&Script->CurrentCommand->Link);
|
| if (Script->CurrentCommand->Cl != NULL) {
|
| SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl);
|
| }
|
| if (Script->CurrentCommand->Data != NULL) {
|
| SHELL_FREE_NON_NULL(Script->CurrentCommand->Data);
|
| }
|
| SHELL_FREE_NON_NULL(Script->CurrentCommand);
|
| }
|
| }
|
| SHELL_FREE_NON_NULL(Script->ScriptName);
|
| SHELL_FREE_NON_NULL(Script);
|
| }
|
|
|
| /**
|
| Function to return a pointer to the currently running script file object.
|
|
|
| @retval NULL A script file is not currently running.
|
| @return A pointer to the current script file object.
|
| **/
|
| SCRIPT_FILE*
|
| EFIAPI
|
| ShellCommandGetCurrentScriptFile (
|
| VOID
|
| )
|
| {
|
| SCRIPT_FILE_LIST *List;
|
| if (IsListEmpty (&mScriptList.Link)) {
|
| return (NULL);
|
| }
|
| List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
|
| return (List->Data);
|
| }
|
|
|
| /**
|
| Function to set a new script as the currently running one.
|
|
|
| This function will correctly stack and unstack nested scripts.
|
|
|
| @param[in] Script Pointer to new script information structure. if NULL
|
| will remove and de-allocate the top-most Script structure.
|
|
|
| @return A pointer to the current running script file after this
|
| change. NULL if removing the final script.
|
| **/
|
| SCRIPT_FILE*
|
| EFIAPI
|
| ShellCommandSetNewScript (
|
| IN SCRIPT_FILE *Script OPTIONAL
|
| )
|
| {
|
| SCRIPT_FILE_LIST *Node;
|
| if (Script == NULL) {
|
| if (IsListEmpty (&mScriptList.Link)) {
|
| return (NULL);
|
| }
|
| Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
|
| RemoveEntryList(&Node->Link);
|
| DeleteScriptFileStruct(Node->Data);
|
| FreePool(Node);
|
| } else {
|
| Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
|
| if (Node == NULL) {
|
| return (NULL);
|
| }
|
| Node->Data = Script;
|
| InsertHeadList(&mScriptList.Link, &Node->Link);
|
| }
|
| return (ShellCommandGetCurrentScriptFile());
|
| }
|
|
|
| /**
|
| Function to generate the next default mapping name.
|
|
|
| If the return value is not NULL then it must be callee freed.
|
|
|
| @param Type What kind of mapping name to make.
|
|
|
| @retval NULL a memory allocation failed.
|
| @return a new map name string
|
| **/
|
| CHAR16*
|
| EFIAPI
|
| ShellCommandCreateNewMappingName(
|
| IN CONST SHELL_MAPPING_TYPE Type
|
| )
|
| {
|
| CHAR16 *String;
|
| ASSERT(Type < MappingTypeMax);
|
|
|
| String = NULL;
|
|
|
| String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
|
| UnicodeSPrint(
|
| String,
|
| PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
|
| Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
|
| Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
|
|
|
| return (String);
|
| }
|
|
|
| /**
|
| Function to add a map node to the list of map items and update the "path" environment variable (optionally).
|
|
|
| If Path is TRUE (during initialization only), the path environment variable will also be updated to include
|
| default paths on the new map name...
|
|
|
| Path should be FALSE when this function is called from the protocol SetMap function.
|
|
|
| @param[in] Name The human readable mapped name.
|
| @param[in] DevicePath The Device Path for this map.
|
| @param[in] Flags The Flags attribute for this map item.
|
| @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
|
|
|
| @retval EFI_SUCCESS The addition was sucessful.
|
| @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
|
| @retval EFI_INVALID_PARAMETER A parameter was invalid.
|
| **/
|
| EFI_STATUS
|
| EFIAPI
|
| ShellCommandAddMapItemAndUpdatePath(
|
| IN CONST CHAR16 *Name,
|
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
| IN CONST UINT64 Flags,
|
| IN CONST BOOLEAN Path
|
| )
|
| {
|
| EFI_STATUS Status;
|
| SHELL_MAP_LIST *MapListNode;
|
| CONST CHAR16 *OriginalPath;
|
| CHAR16 *NewPath;
|
| UINTN NewPathSize;
|
|
|
| NewPathSize = 0;
|
| NewPath = NULL;
|
| OriginalPath = NULL;
|
| Status = EFI_SUCCESS;
|
|
|
| MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
|
| if (MapListNode == NULL) {
|
| Status = EFI_OUT_OF_RESOURCES;
|
| } else {
|
| MapListNode->Flags = Flags;
|
| MapListNode->MapName = AllocateCopyPool(StrSize(Name), Name);
|
| MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
|
| if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
|
| Status = EFI_OUT_OF_RESOURCES;
|
| } else {
|
| InsertTailList(&gShellMapList.Link, &MapListNode->Link);
|
| }
|
| }
|
| if (EFI_ERROR(Status)) {
|
| if (MapListNode != NULL) {
|
| if (MapListNode->DevicePath != NULL) {
|
| FreePool(MapListNode->DevicePath);
|
| }
|
| if (MapListNode->MapName != NULL) {
|
| FreePool(MapListNode->MapName);
|
| }
|
| FreePool(MapListNode);
|
| }
|
| } else if (Path) {
|
| //
|
| // Since there was no error and Path was TRUE
|
| // Now add the correct path for that mapping
|
| //
|
| OriginalPath = gEfiShellProtocol->GetEnv(L"path");
|
| ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
|
| if (OriginalPath != NULL) {
|
| StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
|
| } else {
|
| StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0);
|
| }
|
| StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
|
| StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
|
|
|
| Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
|
| ASSERT_EFI_ERROR(Status);
|
| FreePool(NewPath);
|
| }
|
| return (Status);
|
| }
|
|
|
| /**
|
| Creates the default map names for each device path in the system with
|
| a protocol depending on the Type.
|
|
|
| Creates the consistent map names for each device path in the system with
|
| a protocol depending on the Type.
|
|
|
| Note: This will reset all mappings in the system("map -r").
|
|
|
| Also sets up the default path environment variable if Type is FileSystem.
|
|
|
| @retval EFI_SUCCESS All map names were created sucessfully.
|
| @retval EFI_NOT_FOUND No protocols were found in the system.
|
| @return Error returned from gBS->LocateHandle().
|
|
|
| @sa LocateHandle
|
| **/
|
| EFI_STATUS
|
| EFIAPI
|
| ShellCommandCreateInitialMappingsAndPaths(
|
| VOID
|
| )
|
| {
|
| EFI_STATUS Status;
|
| EFI_HANDLE *HandleList;
|
| UINTN Count;
|
| EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
|
| CHAR16 *NewDefaultName;
|
| CHAR16 *NewConsistName;
|
| EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
|
| SHELL_MAP_LIST *MapListNode;
|
|
|
| HandleList = NULL;
|
|
|
| //
|
| // Reset the static members back to zero
|
| //
|
| mFsMaxCount = 0;
|
| mBlkMaxCount = 0;
|
|
|
| gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
|
|
|
| //
|
| // First empty out the existing list.
|
| //
|
| if (!IsListEmpty(&gShellMapList.Link)) {
|
| for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
|
| ; !IsListEmpty(&gShellMapList.Link)
|
| ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
|
| ){
|
| RemoveEntryList(&MapListNode->Link);
|
| SHELL_FREE_NON_NULL(MapListNode->DevicePath);
|
| SHELL_FREE_NON_NULL(MapListNode->MapName);
|
| SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
|
| FreePool(MapListNode);
|
| } // for loop
|
| }
|
|
|
| //
|
| // Find each handle with Simple File System
|
| //
|
| HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
|
| if (HandleList != NULL) {
|
| //
|
| // Do a count of the handles
|
| //
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++);
|
|
|
| //
|
| // Get all Device Paths
|
| //
|
| DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
|
| ASSERT(DevicePathList != NULL);
|
|
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
|
| DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
|
| }
|
|
|
| //
|
| // Sort all DevicePaths
|
| //
|
| PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
|
|
|
| ShellCommandConsistMappingInitialize(&ConsistMappingTable);
|
| //
|
| // Assign new Mappings to all...
|
| //
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
|
| //
|
| // Get default name first
|
| //
|
| NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
|
| ASSERT(NewDefaultName != NULL);
|
| Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
|
| ASSERT_EFI_ERROR(Status);
|
| FreePool(NewDefaultName);
|
|
|
| //
|
| // Now do consistent name
|
| //
|
| NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
|
| if (NewConsistName != NULL) {
|
| Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
|
| ASSERT_EFI_ERROR(Status);
|
| FreePool(NewConsistName);
|
| }
|
| }
|
|
|
| ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
|
|
|
| SHELL_FREE_NON_NULL(HandleList);
|
| SHELL_FREE_NON_NULL(DevicePathList);
|
|
|
| HandleList = NULL;
|
| } else {
|
| Count = (UINTN)-1;
|
| }
|
|
|
| //
|
| // Find each handle with Block Io
|
| //
|
| HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid);
|
| if (HandleList != NULL) {
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++);
|
|
|
| //
|
| // Get all Device Paths
|
| //
|
| DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
|
| ASSERT(DevicePathList != NULL);
|
|
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
|
| DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
|
| }
|
|
|
| //
|
| // Sort all DevicePaths
|
| //
|
| PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
|
|
|
| //
|
| // Assign new Mappings to all...
|
| //
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
|
| //
|
| // Get default name first
|
| //
|
| NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
|
| ASSERT(NewDefaultName != NULL);
|
| Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
|
| ASSERT_EFI_ERROR(Status);
|
| FreePool(NewDefaultName);
|
| }
|
|
|
| SHELL_FREE_NON_NULL(HandleList);
|
| SHELL_FREE_NON_NULL(DevicePathList);
|
| } else if (Count == (UINTN)-1) {
|
| return (EFI_NOT_FOUND);
|
| }
|
|
|
| return (EFI_SUCCESS);
|
| }
|
|
|
| /**
|
| Add mappings for any devices without one. Do not change any existing maps.
|
|
|
| @retval EFI_SUCCESS The operation was successful.
|
| **/
|
| EFI_STATUS
|
| EFIAPI
|
| ShellCommandUpdateMapping (
|
| VOID
|
| )
|
| {
|
| EFI_STATUS Status;
|
| EFI_HANDLE *HandleList;
|
| UINTN Count;
|
| EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
|
| CHAR16 *NewDefaultName;
|
| CHAR16 *NewConsistName;
|
| EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
|
|
|
| HandleList = NULL;
|
| Status = EFI_SUCCESS;
|
|
|
| //
|
| // remove mappings that represent removed devices.
|
| //
|
|
|
| //
|
| // Find each handle with Simple File System
|
| //
|
| HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
|
| if (HandleList != NULL) {
|
| //
|
| // Do a count of the handles
|
| //
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++);
|
|
|
| //
|
| // Get all Device Paths
|
| //
|
| DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
|
| if (DevicePathList == NULL) {
|
| return (EFI_OUT_OF_RESOURCES);
|
| }
|
|
|
| for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
|
| DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
|
| }
|
|
|
| //
|
| // Sort all DevicePaths
|
| //
|
| PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
|
|
|
| ShellCommandConsistMappingInitialize(&ConsistMappingTable);
|
|
|
| //
|
| // Assign new Mappings to remainders
|
| //
|
| for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {
|
| //
|
| // Skip ones that already have
|
| //
|
| if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {
|
| continue;
|
| }
|
| //
|
| // Get default name
|
| //
|
| NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
|
| if (NewDefaultName == NULL) {
|
| Status = EFI_OUT_OF_RESOURCES;
|
| break;
|
| }
|
|
|
| //
|
| // Call shell protocol SetMap function now...
|
| //
|
| Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);
|
|
|
| if (!EFI_ERROR(Status)) {
|
| //
|
| // Now do consistent name
|
| //
|
| NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
|
| if (NewConsistName != NULL) {
|
| Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);
|
| FreePool(NewConsistName);
|
| }
|
| }
|
|
|
| FreePool(NewDefaultName);
|
| }
|
| ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
|
| SHELL_FREE_NON_NULL(HandleList);
|
| SHELL_FREE_NON_NULL(DevicePathList);
|
|
|
| HandleList = NULL;
|
| } else {
|
| Count = (UINTN)-1;
|
| }
|
| //
|
| // Do it all over again for gEfiBlockIoProtocolGuid
|
| //
|
|
|
| return (Status);
|
| }
|
|
|
| /**
|
| Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
|
|
|
| @param[in] Handle The SHELL_FILE_HANDLE to convert.
|
|
|
| @return a EFI_FILE_PROTOCOL* representing the same file.
|
| **/
|
| EFI_FILE_PROTOCOL*
|
| EFIAPI
|
| ConvertShellHandleToEfiFileProtocol(
|
| IN CONST SHELL_FILE_HANDLE Handle
|
| )
|
| {
|
| return ((EFI_FILE_PROTOCOL*)(Handle));
|
| }
|
|
|
| /**
|
| Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
|
|
|
| @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
|
| @param[in] Path The path to the file for verification.
|
|
|
| @return A SHELL_FILE_HANDLE representing the same file.
|
| @retval NULL There was not enough memory.
|
| **/
|
| SHELL_FILE_HANDLE
|
| EFIAPI
|
| ConvertEfiFileProtocolToShellHandle(
|
| IN CONST EFI_FILE_PROTOCOL *Handle,
|
| IN CONST CHAR16 *Path
|
| )
|
| {
|
| SHELL_COMMAND_FILE_HANDLE *Buffer;
|
| BUFFER_LIST *NewNode;
|
|
|
| if (Path != NULL) {
|
| Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
|
| if (Buffer == NULL) {
|
| return (NULL);
|
| }
|
| NewNode = AllocateZeroPool(sizeof(BUFFER_LIST));
|
| if (NewNode == NULL) {
|
| SHELL_FREE_NON_NULL(Buffer);
|
| return (NULL);
|
| }
|
| Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle;
|
| Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
|
| if (Buffer->Path == NULL) {
|
| SHELL_FREE_NON_NULL(NewNode);
|
| SHELL_FREE_NON_NULL(Buffer);
|
| return (NULL);
|
| }
|
| NewNode->Buffer = Buffer;
|
|
|
| InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
|
| }
|
| return ((SHELL_FILE_HANDLE)(Handle));
|
| }
|
|
|
| /**
|
| Find the path that was logged with the specified SHELL_FILE_HANDLE.
|
|
|
| @param[in] Handle The SHELL_FILE_HANDLE to query on.
|
|
|
| @return A pointer to the path for the file.
|
| **/
|
| CONST CHAR16*
|
| EFIAPI
|
| ShellFileHandleGetPath(
|
| IN CONST SHELL_FILE_HANDLE Handle
|
| )
|
| {
|
| BUFFER_LIST *Node;
|
|
|
| for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
|
| ; !IsNull(&mFileHandleList.Link, &Node->Link)
|
| ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
|
| ){
|
| if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
|
| return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
|
| }
|
| }
|
| return (NULL);
|
| }
|
|
|
| /**
|
| Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
|
|
|
| @param[in] Handle The SHELL_FILE_HANDLE to remove.
|
|
|
| @retval TRUE The item was removed.
|
| @retval FALSE The item was not found.
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellFileHandleRemove(
|
| IN CONST SHELL_FILE_HANDLE Handle
|
| )
|
| {
|
| BUFFER_LIST *Node;
|
|
|
| for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
|
| ; !IsNull(&mFileHandleList.Link, &Node->Link)
|
| ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
|
| ){
|
| if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
|
| RemoveEntryList(&Node->Link);
|
| SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
|
| SHELL_FREE_NON_NULL(Node->Buffer);
|
| SHELL_FREE_NON_NULL(Node);
|
| return (TRUE);
|
| }
|
| }
|
| return (FALSE);
|
| }
|
|
|
| /**
|
| Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
|
|
|
| This will NOT work on directories.
|
|
|
| If Handle is NULL, then ASSERT.
|
|
|
| @param[in] Handle the file handle
|
|
|
| @retval TRUE the position is at the end of the file
|
| @retval FALSE the position is not at the end of the file
|
| **/
|
| BOOLEAN
|
| EFIAPI
|
| ShellFileHandleEof(
|
| IN SHELL_FILE_HANDLE Handle
|
| )
|
| {
|
| EFI_FILE_INFO *Info;
|
| UINT64 Pos;
|
| BOOLEAN RetVal;
|
|
|
| //
|
| // ASSERT if Handle is NULL
|
| //
|
| ASSERT(Handle != NULL);
|
|
|
| gEfiShellProtocol->GetFilePosition(Handle, &Pos);
|
| Info = gEfiShellProtocol->GetFileInfo (Handle);
|
| gEfiShellProtocol->SetFilePosition(Handle, Pos);
|
|
|
| if (Info == NULL) {
|
| return (FALSE);
|
| }
|
|
|
| if (Pos == Info->FileSize) {
|
| RetVal = TRUE;
|
| } else {
|
| RetVal = FALSE;
|
| }
|
|
|
| FreePool (Info);
|
|
|
| return (RetVal);
|
| }
|
|
|
| /**
|
| Frees any BUFFER_LIST defined type.
|
|
|
| @param[in] List The BUFFER_LIST object to free.
|
| **/
|
| VOID
|
| EFIAPI
|
| FreeBufferList (
|
| IN BUFFER_LIST *List
|
| )
|
| {
|
| BUFFER_LIST *BufferListEntry;
|
|
|
| if (List == NULL){
|
| return;
|
| }
|
| //
|
| // enumerate through the buffer list and free all memory
|
| //
|
| for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
|
| ; !IsListEmpty (&List->Link)
|
| ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
|
| ){
|
| RemoveEntryList(&BufferListEntry->Link);
|
| if (BufferListEntry->Buffer != NULL) {
|
| FreePool(BufferListEntry->Buffer);
|
| }
|
| FreePool(BufferListEntry);
|
| }
|
| }
|
|
|
| /**
|
| Dump some hexadecimal data to the screen.
|
|
|
| @param[in] Indent How many spaces to indent the output.
|
| @param[in] Offset The offset of the printing.
|
| @param[in] DataSize The size in bytes of UserData.
|
| @param[in] UserData The data to print out.
|
| **/
|
| VOID
|
| DumpHex (
|
| IN UINTN Indent,
|
| IN UINTN Offset,
|
| IN UINTN DataSize,
|
| IN VOID *UserData
|
| )
|
| {
|
| UINT8 *Data;
|
|
|
| CHAR8 Val[50];
|
|
|
| CHAR8 Str[20];
|
|
|
| UINT8 TempByte;
|
| UINTN Size;
|
| UINTN Index;
|
|
|
| Data = UserData;
|
| while (DataSize != 0) {
|
| Size = 16;
|
| if (Size > DataSize) {
|
| Size = DataSize;
|
| }
|
|
|
| for (Index = 0; Index < Size; Index += 1) {
|
| TempByte = Data[Index];
|
| Val[Index * 3 + 0] = Hex[TempByte >> 4];
|
| Val[Index * 3 + 1] = Hex[TempByte & 0xF];
|
| Val[Index * 3 + 2] = (CHAR8) ((Index == 7) ? '-' : ' ');
|
| Str[Index] = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
|
| }
|
|
|
| Val[Index * 3] = 0;
|
| Str[Index] = 0;
|
| ShellPrintEx(-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
|
|
|
| Data += Size;
|
| Offset += Size;
|
| DataSize -= Size;
|
| }
|
| }
|
| |
| /** |
| Dump HEX data into buffer. |
| |
| @param[in] Buffer HEX data to be dumped in Buffer. |
| @param[in] Indent How many spaces to indent the output. |
| @param[in] Offset The offset of the printing. |
| @param[in] DataSize The size in bytes of UserData. |
| @param[in] UserData The data to print out. |
| **/ |
| CHAR16* |
| CatSDumpHex ( |
| IN CHAR16 *Buffer, |
| IN UINTN Indent, |
| IN UINTN Offset, |
| IN UINTN DataSize, |
| IN VOID *UserData |
| ) |
| { |
| UINT8 *Data; |
| UINT8 TempByte; |
| UINTN Size; |
| UINTN Index; |
| CHAR8 Val[50]; |
| CHAR8 Str[20]; |
| CHAR16 *RetVal; |
| CHAR16 *TempRetVal; |
| |
| Data = UserData; |
| RetVal = Buffer; |
| while (DataSize != 0) { |
| Size = 16; |
| if (Size > DataSize) { |
| Size = DataSize; |
| } |
| |
| for (Index = 0; Index < Size; Index += 1) { |
| TempByte = Data[Index]; |
| Val[Index * 3 + 0] = Hex[TempByte >> 4]; |
| Val[Index * 3 + 1] = Hex[TempByte & 0xF]; |
| Val[Index * 3 + 2] = (CHAR8) ((Index == 7) ? '-' : ' '); |
| Str[Index] = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte); |
| } |
| |
| Val[Index * 3] = 0; |
| Str[Index] = 0; |
| TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str); |
| SHELL_FREE_NON_NULL (RetVal); |
| RetVal = TempRetVal; |
| |
| Data += Size; |
| Offset += Size; |
| DataSize -= Size; |
| } |
| |
| return RetVal; |
| } |