+++ /dev/null
-/** @file\r
- Basic command line parser for EBL (Embedded Boot Loader)\r
-\r
- Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>\r
- Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
- (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
-\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
-\r
-**/\r
-\r
-#include "Ebl.h"\r
-\r
-// Globals for command history processing\r
-INTN mCmdHistoryEnd = -1;\r
-INTN mCmdHistoryStart = -1;\r
-INTN mCmdHistoryCurrent = -1;\r
-CHAR8 mCmdHistory[MAX_CMD_HISTORY][MAX_CMD_LINE];\r
-CHAR8 *mCmdBlank = "";\r
-\r
-// Globals to remember current screen geometry\r
-UINTN gScreenColumns;\r
-UINTN gScreenRows;\r
-\r
-// Global to turn on/off breaking commands with prompts before they scroll the screen\r
-BOOLEAN gPageBreak = TRUE;\r
-\r
-VOID\r
-RingBufferIncrement (\r
- IN INTN *Value\r
- )\r
-{\r
- *Value = *Value + 1;\r
-\r
- if (*Value >= MAX_CMD_HISTORY) {\r
- *Value = 0;\r
- }\r
-}\r
-\r
-VOID\r
-RingBufferDecrement (\r
- IN INTN *Value\r
- )\r
-{\r
- *Value = *Value - 1;\r
-\r
- if (*Value < 0) {\r
- *Value = MAX_CMD_HISTORY - 1;\r
- }\r
-}\r
-\r
-/**\r
- Save this command in the circular history buffer. Older commands are\r
- overwritten with newer commands.\r
-\r
- @param Cmd Command line to archive the history of.\r
-\r
- @return None\r
-\r
-**/\r
-VOID\r
-SetCmdHistory (\r
- IN CHAR8 *Cmd\r
- )\r
-{\r
- // Don't bother adding empty commands to the list\r
- if (AsciiStrLen(Cmd) != 0) {\r
-\r
- // First entry\r
- if (mCmdHistoryStart == -1) {\r
- mCmdHistoryStart = 0;\r
- mCmdHistoryEnd = 0;\r
- } else {\r
- // Record the new command at the next index\r
- RingBufferIncrement(&mCmdHistoryStart);\r
-\r
- // If the next index runs into the end index, shuffle end back by one\r
- if (mCmdHistoryStart == mCmdHistoryEnd) {\r
- RingBufferIncrement(&mCmdHistoryEnd);\r
- }\r
- }\r
-\r
- // Copy the new command line into the ring buffer\r
- AsciiStrnCpyS (&mCmdHistory[mCmdHistoryStart][0], MAX_CMD_LINE, Cmd, MAX_CMD_LINE);\r
- }\r
-\r
- // Reset the command history for the next up arrow press\r
- mCmdHistoryCurrent = mCmdHistoryStart;\r
-}\r
-\r
-\r
-/**\r
- Retreave data from the Command History buffer. Direction maps into up arrow\r
- an down arrow on the command line\r
-\r
- @param Direction Command forward or back\r
-\r
- @return The Command history based on the Direction\r
-\r
-**/\r
-CHAR8 *\r
-GetCmdHistory (\r
- IN UINT16 Direction\r
- )\r
-{\r
- CHAR8 *HistoricalCommand = NULL;\r
-\r
- // No history yet?\r
- if (mCmdHistoryCurrent == -1) {\r
- HistoricalCommand = mCmdBlank;\r
- goto Exit;\r
- }\r
-\r
- if (Direction == SCAN_UP) {\r
- HistoricalCommand = &mCmdHistory[mCmdHistoryCurrent][0];\r
-\r
- // if we just echoed the last command, hang out there, don't wrap around\r
- if (mCmdHistoryCurrent == mCmdHistoryEnd) {\r
- goto Exit;\r
- }\r
-\r
- // otherwise, back up by one\r
- RingBufferDecrement(&mCmdHistoryCurrent);\r
-\r
- } else if (Direction == SCAN_DOWN) {\r
-\r
- // if we last echoed the start command, put a blank prompt out\r
- if (mCmdHistoryCurrent == mCmdHistoryStart) {\r
- HistoricalCommand = mCmdBlank;\r
- goto Exit;\r
- }\r
-\r
- // otherwise increment the current pointer and return that command\r
- RingBufferIncrement(&mCmdHistoryCurrent);\r
- RingBufferIncrement(&mCmdHistoryCurrent);\r
-\r
- HistoricalCommand = &mCmdHistory[mCmdHistoryCurrent][0];\r
- RingBufferDecrement(&mCmdHistoryCurrent);\r
- }\r
-\r
-Exit:\r
- return HistoricalCommand;\r
-}\r
-\r
-\r
-/**\r
- Parse the CmdLine and break it up into Argc (arg count) and Argv (array of\r
- pointers to each argument). The Cmd buffer is altered and separators are\r
- converted to string terminators. This allows Argv to point into CmdLine.\r
- A CmdLine can support multiple commands. The next command in the command line\r
- is returned if it exists.\r
-\r
- @param CmdLine String to parse for a set of commands\r
- @param Argc Returns the number of arguments in the CmdLine current command\r
- @param Argv Argc pointers to each string in CmdLine\r
-\r
- @return Next Command in the command line or NULL if non exists\r
-**/\r
-CHAR8 *\r
-ParseArguments (\r
- IN CHAR8 *CmdLine,\r
- OUT UINTN *Argc,\r
- OUT CHAR8 **Argv\r
- )\r
-{\r
- UINTN Arg;\r
- CHAR8 *Char;\r
- BOOLEAN LookingForArg;\r
- BOOLEAN InQuote;\r
-\r
- *Argc = 0;\r
- if (AsciiStrLen (CmdLine) == 0) {\r
- return NULL;\r
- }\r
-\r
- // Walk a single command line. A CMD_SEPARATOR allows multiple commands on a single line\r
- InQuote = FALSE;\r
- LookingForArg = TRUE;\r
- for (Char = CmdLine, Arg = 0; *Char != '\0'; Char++) {\r
- if (!InQuote && *Char == CMD_SEPARATOR) {\r
- break;\r
- }\r
-\r
- // Perform any text conversion here\r
- if (*Char == '\t') {\r
- // TAB to space\r
- *Char = ' ';\r
- }\r
-\r
- if (LookingForArg) {\r
- // Look for the beginning of an Argv[] entry\r
- if (*Char == '"') {\r
- Argv[Arg++] = ++Char;\r
- LookingForArg = FALSE;\r
- InQuote = TRUE;\r
- } else if (*Char != ' ') {\r
- Argv[Arg++] = Char;\r
- LookingForArg = FALSE;\r
- }\r
- } else {\r
- // Looking for the terminator of an Argv[] entry\r
- if (!InQuote && (*Char == ' ')) {\r
- *Char = '\0';\r
- LookingForArg = TRUE;\r
- } else if (!InQuote && (*Char == '"') && (*(Char-1) != '\\')) {\r
- InQuote = TRUE;\r
- } else if (InQuote && (*Char == '"') && (*(Char-1) != '\\')) {\r
- *Char = '\0';\r
- InQuote = FALSE;\r
- }\r
- }\r
- }\r
-\r
- *Argc = Arg;\r
-\r
- if (*Char == CMD_SEPARATOR) {\r
- // Replace the command delimiter with null and return pointer to next command line\r
- *Char = '\0';\r
- return ++Char;\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-\r
-/**\r
- Return a keypress or optionally timeout if a timeout value was passed in.\r
- An optional callback function is called every second when waiting for a\r
- timeout.\r
-\r
- @param Key EFI Key information returned\r
- @param TimeoutInSec Number of seconds to wait to timeout\r
- @param CallBack Callback called every second during the timeout wait\r
-\r
- @return EFI_SUCCESS Key was returned\r
- @return EFI_TIMEOUT If the TimoutInSec expired\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-EblGetCharKey (\r
- IN OUT EFI_INPUT_KEY *Key,\r
- IN UINTN TimeoutInSec,\r
- IN EBL_GET_CHAR_CALL_BACK CallBack OPTIONAL\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN WaitCount;\r
- UINTN WaitIndex;\r
- EFI_EVENT WaitList[2];\r
-\r
- WaitCount = 1;\r
- WaitList[0] = gST->ConIn->WaitForKey;\r
- if (TimeoutInSec != 0) {\r
- // Create a time event for 1 sec duration if we have a timeout\r
- gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &WaitList[1]);\r
- gBS->SetTimer (WaitList[1], TimerPeriodic, EFI_SET_TIMER_TO_SECOND);\r
- WaitCount++;\r
- }\r
-\r
- for (;;) {\r
- Status = gBS->WaitForEvent (WaitCount, WaitList, &WaitIndex);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- switch (WaitIndex) {\r
- case 0:\r
- // Key event signaled\r
- Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
- if (!EFI_ERROR (Status)) {\r
- if (WaitCount == 2) {\r
- gBS->CloseEvent (WaitList[1]);\r
- }\r
- return EFI_SUCCESS;\r
- }\r
- break;\r
-\r
- case 1:\r
- // Periodic 1 sec timer signaled\r
- TimeoutInSec--;\r
- if (CallBack != NULL) {\r
- // Call the users callback function if registered\r
- CallBack (TimeoutInSec);\r
- }\r
- if (TimeoutInSec == 0) {\r
- gBS->CloseEvent (WaitList[1]);\r
- return EFI_TIMEOUT;\r
- }\r
- break;\r
- default:\r
- ASSERT (FALSE);\r
- }\r
- }\r
-}\r
-\r
-\r
-/**\r
- This routine is used prevent command output data from scrolling off the end\r
- of the screen. The global gPageBreak is used to turn on or off this feature.\r
- If the CurrentRow is near the end of the screen pause and print out a prompt\r
- If the use hits Q to quit return TRUE else for any other key return FALSE.\r
- PrefixNewline is used to figure out if a newline is needed before the prompt\r
- string. This depends on the last print done before calling this function.\r
- CurrentRow is updated by one on a call or set back to zero if a prompt is\r
- needed.\r
-\r
- @param CurrentRow Used to figure out if its the end of the page and updated\r
- @param PrefixNewline Did previous print issue a newline\r
-\r
- @return TRUE if Q was hit to quit, FALSE in all other cases.\r
-\r
-**/\r
-BOOLEAN\r
-EFIAPI\r
-EblAnyKeyToContinueQtoQuit (\r
- IN UINTN *CurrentRow,\r
- IN BOOLEAN PrefixNewline\r
- )\r
-{\r
- EFI_INPUT_KEY InputKey;\r
-\r
- if (!gPageBreak) {\r
- // global disable for this feature\r
- return FALSE;\r
- }\r
-\r
- if (*CurrentRow >= (gScreenRows - 2)) {\r
- if (PrefixNewline) {\r
- AsciiPrint ("\n");\r
- }\r
- AsciiPrint ("Any key to continue (Q to quit): ");\r
- EblGetCharKey (&InputKey, 0, NULL);\r
- AsciiPrint ("\n");\r
-\r
- // Time to promt to stop the screen. We have to leave space for the prompt string\r
- *CurrentRow = 0;\r
- if (InputKey.UnicodeChar == 'Q' || InputKey.UnicodeChar == 'q') {\r
- return TRUE;\r
- }\r
- } else {\r
- *CurrentRow += 1;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-\r
-/**\r
- Set the text color of the EFI Console. If a zero is passed in reset to\r
- default text/background color.\r
-\r
- @param Attribute For text and background color\r
-\r
-**/\r
-VOID\r
-EblSetTextColor (\r
- UINTN Attribute\r
- )\r
-{\r
- if (Attribute == 0) {\r
- // Set the text color back to default\r
- Attribute = (UINTN)PcdGet32 (PcdEmbeddedDefaultTextColor);\r
- }\r
-\r
- gST->ConOut->SetAttribute (gST->ConOut, Attribute);\r
-}\r
-\r
-\r
-/**\r
- Collect the keyboard input for a cmd line. Carriage Return, New Line, or ESC\r
- terminates the command line. You can edit the command line via left arrow,\r
- delete and backspace and they all back up and erase the command line.\r
- No edit of command line is possible without deletion at this time!\r
- The up arrow and down arrow fill Cmd with information from the history\r
- buffer.\r
-\r
- @param Cmd Command line to return\r
- @param CmdMaxSize Maximum size of Cmd\r
-\r
- @return The Status of EblGetCharKey()\r
-\r
-**/\r
-EFI_STATUS\r
-GetCmd (\r
- IN OUT CHAR8 *Cmd,\r
- IN UINTN CmdMaxSize\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN Index;\r
- UINTN Index2;\r
- CHAR8 Char;\r
- CHAR8 *History;\r
- EFI_INPUT_KEY Key;\r
-\r
- for (Index = 0; Index < CmdMaxSize - 1;) {\r
- Status = EblGetCharKey (&Key, 0, NULL);\r
- if (EFI_ERROR (Status)) {\r
- Cmd[Index] = '\0';\r
- AsciiPrint ("\n");\r
- return Status;\r
- }\r
-\r
- Char = (CHAR8)Key.UnicodeChar;\r
- if ((Char == '\n') || (Char == '\r') || (Char == 0x7f)) {\r
- Cmd[Index] = '\0';\r
- if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho) == TRUE) {\r
- AsciiPrint ("\n\r");\r
- }\r
- return EFI_SUCCESS;\r
- } else if ((Char == '\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){\r
- if (Index != 0) {\r
- Index--;\r
- //\r
- // Update the display\r
- //\r
- AsciiPrint ("\b \b");\r
- }\r
- } else if ((Key.ScanCode == SCAN_UP) || Key.ScanCode == SCAN_DOWN) {\r
- History = GetCmdHistory (Key.ScanCode);\r
- //\r
- // Clear display line\r
- //\r
- for (Index2 = 0; Index2 < Index; Index2++) {\r
- AsciiPrint ("\b \b");\r
- }\r
- AsciiPrint (History);\r
- Index = AsciiStrLen (History);\r
- AsciiStrnCpyS (Cmd, CmdMaxSize, History, CmdMaxSize);\r
- } else {\r
- Cmd[Index++] = Char;\r
- if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho) == TRUE) {\r
- AsciiPrint ("%c", Char);\r
- }\r
- }\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
- Print the boot up banner for the EBL.\r
-**/\r
-VOID\r
-EblPrintStartupBanner (\r
- VOID\r
- )\r
-{\r
- AsciiPrint ("Embedded Boot Loader (");\r
- EblSetTextColor (EFI_YELLOW);\r
- AsciiPrint ("EBL");\r
- EblSetTextColor (0);\r
- AsciiPrint (") prototype. Built at %a on %a\n",__TIME__, __DATE__);\r
- AsciiPrint ("THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN 'AS IS' BASIS,\nWITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\n");\r
- AsciiPrint ("Please send feedback to edk2-devel@lists.sourceforge.net\n");\r
-}\r
-\r
-\r
-/**\r
- Send null requests to all removable media block IO devices so the a media add/remove/change\r
- can be detected in real before we execute a command.\r
-\r
- This is mainly due to the fact that the FAT driver does not do this today so you can get stale\r
- dir commands after an SD Card has been removed.\r
-**/\r
-VOID\r
-EblProbeRemovableMedia (\r
- VOID\r
- )\r
-{\r
- UINTN Index;\r
- UINTN Max;\r
- EFI_OPEN_FILE *File;\r
-\r
- //\r
- // Probe for media insertion/removal in removable media devices\r
- //\r
- Max = EfiGetDeviceCounts (EfiOpenBlockIo);\r
- if (Max != 0) {\r
- for (Index = 0; Index < Max; Index++) {\r
- File = EfiDeviceOpenByType (EfiOpenBlockIo, Index);\r
- if (File != NULL) {\r
- if (File->FsBlockIoMedia->RemovableMedia) {\r
- // Probe to see if media is present (or not) or media changed\r
- // this causes the ReinstallProtocolInterface() to fire in the\r
- // block io driver to update the system about media change events\r
- File->FsBlockIo->ReadBlocks (File->FsBlockIo, File->FsBlockIo->Media->MediaId, (EFI_LBA)0, 0, NULL);\r
- }\r
- EfiClose (File);\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-\r
-/**\r
- Print the prompt for the EBL.\r
-**/\r
-VOID\r
-EblPrompt (\r
- VOID\r
- )\r
-{\r
- EblSetTextColor (EFI_YELLOW);\r
- AsciiPrint ("%a %a",(CHAR8 *)PcdGetPtr (PcdEmbeddedPrompt), EfiGetCwd ());\r
- EblSetTextColor (0);\r
- AsciiPrint ("%a", ">");\r
-}\r
-\r
-\r
-\r
-/**\r
- Parse a command line and execute the commands. The ; separator allows\r
- multiple commands for each command line. Stop processing if one of the\r
- commands returns an error.\r
-\r
- @param CmdLine Command Line to process.\r
- @param MaxCmdLineSize MaxSize of the Command line\r
-\r
- @return EFI status of the Command\r
-\r
-**/\r
-EFI_STATUS\r
-ProcessCmdLine (\r
- IN CHAR8 *CmdLine,\r
- IN UINTN MaxCmdLineSize\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EBL_COMMAND_TABLE *Cmd;\r
- CHAR8 *Ptr;\r
- UINTN Argc;\r
- CHAR8 *Argv[MAX_ARGS];\r
-\r
- // Parse the command line. The loop processes commands separated by ;\r
- for (Ptr = CmdLine, Status = EFI_SUCCESS; Ptr != NULL;) {\r
- Ptr = ParseArguments (Ptr, &Argc, Argv);\r
- if (Argc != 0) {\r
- Cmd = EblGetCommand (Argv[0]);\r
- if (Cmd != NULL) {\r
- // Execute the Command!\r
- Status = Cmd->Command (Argc, Argv);\r
- if (Status == EFI_ABORTED) {\r
- // exit command so lets exit\r
- break;\r
- } else if (Status == EFI_TIMEOUT) {\r
- // pause command got input so don't process any more cmd on this cmd line\r
- break;\r
- } else if (EFI_ERROR (Status)) {\r
- AsciiPrint ("%a returned %r error\n", Cmd->Name, Status);\r
- // if any command fails stop processing CmdLine\r
- break;\r
- }\r
- } else {\r
- AsciiPrint ("The command '%a' is not supported.\n", Argv[0]);\r
- }\r
- }\r
- }\r
-\r
- return Status;\r
-}\r
-\r
-\r
-\r
-/**\r
- Embedded Boot Loader (EBL) - A simple EFI command line application for embedded\r
- devices. PcdEmbeddedAutomaticBootCommand is a complied in command line that\r
- gets executed automatically. The ; separator allows multiple commands\r
- for each command line.\r
-\r
- @param ImageHandle EFI ImageHandle for this application.\r
- @param SystemTable EFI system table\r
-\r
- @return EFI status of the application\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-EdkBootLoaderEntry (\r
- IN EFI_HANDLE ImageHandle,\r
- IN EFI_SYSTEM_TABLE *SystemTable\r
- )\r
-{\r
- EFI_STATUS Status;\r
- CHAR8 CmdLine[MAX_CMD_LINE];\r
- CHAR16 *CommandLineVariable = NULL;\r
- CHAR16 *CommandLineVariableName = L"default-cmdline";\r
- UINTN CommandLineVariableSize = 0;\r
- EFI_GUID VendorGuid;\r
-\r
- // Initialize tables of commands\r
- EblInitializeCmdTable ();\r
- EblInitializeDeviceCmd ();\r
- EblInitializemdHwDebugCmds ();\r
- EblInitializemdHwIoDebugCmds ();\r
- EblInitializeDirCmd ();\r
- EblInitializeHobCmd ();\r
- EblInitializeScriptCmd ();\r
- EblInitializeExternalCmd ();\r
- EblInitializeNetworkCmd();\r
- EblInitializeVariableCmds ();\r
-\r
- if (gST->ConOut == NULL) {\r
- DEBUG((EFI_D_ERROR,"Error: No Console Output\n"));\r
- return EFI_NOT_READY;\r
- }\r
-\r
- // Disable the 5 minute EFI watchdog time so we don't get automatically reset\r
- gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
-\r
- if (FeaturePcdGet (PcdEmbeddedMacBoot)) {\r
- // A MAC will boot in graphics mode, so turn it back to text here\r
- // This protocol was removed from edk2. It is only an edk thing. We need to make our own copy.\r
- // DisableQuietBoot ();\r
-\r
- // Enable the biggest output screen size possible\r
- gST->ConOut->SetMode (gST->ConOut, (UINTN)gST->ConOut->Mode->MaxMode - 1);\r
-\r
- }\r
-\r
- // Save current screen mode\r
- gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &gScreenColumns, &gScreenRows);\r
-\r
- EblPrintStartupBanner ();\r
-\r
- // Parse command line and handle commands separated by ;\r
- // The loop prints the prompt gets user input and saves history\r
-\r
- // Look for a variable with a default command line, otherwise use the Pcd\r
- ZeroMem(&VendorGuid, sizeof(EFI_GUID));\r
-\r
- Status = gRT->GetVariable(CommandLineVariableName, &VendorGuid, NULL, &CommandLineVariableSize, CommandLineVariable);\r
- if (Status == EFI_BUFFER_TOO_SMALL) {\r
- CommandLineVariable = AllocatePool(CommandLineVariableSize);\r
-\r
- Status = gRT->GetVariable(CommandLineVariableName, &VendorGuid, NULL, &CommandLineVariableSize, CommandLineVariable);\r
- if (!EFI_ERROR(Status)) {\r
- UnicodeStrToAsciiStrS (CommandLineVariable, CmdLine, MAX_CMD_LINE);\r
- }\r
-\r
- FreePool(CommandLineVariable);\r
- }\r
-\r
- if (EFI_ERROR(Status)) {\r
- AsciiStrCpyS (CmdLine, MAX_CMD_LINE, (CHAR8 *)PcdGetPtr (PcdEmbeddedAutomaticBootCommand));\r
- }\r
-\r
- for (;;) {\r
- Status = ProcessCmdLine (CmdLine, MAX_CMD_LINE);\r
- if (Status == EFI_ABORTED) {\r
- // if a command returns EFI_ABORTED then exit the EBL\r
- EblShutdownExternalCmdTable ();\r
- return EFI_SUCCESS;\r
- }\r
-\r
- // get the command line from the user\r
- EblPrompt ();\r
- GetCmd (CmdLine, MAX_CMD_LINE);\r
- SetCmdHistory (CmdLine);\r
-\r
- if (FeaturePcdGet (PcdEmbeddedProbeRemovable)) {\r
- // Probe removable media devices to see if media has been inserted or removed.\r
- EblProbeRemovableMedia ();\r
- }\r
- }\r
-}\r
-\r
-\r