]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/Ebl/Command.c
Adding support for BeagleBoard.
[mirror_edk2.git] / EmbeddedPkg / Ebl / Command.c
diff --git a/EmbeddedPkg/Ebl/Command.c b/EmbeddedPkg/Ebl/Command.c
new file mode 100644 (file)
index 0000000..7899a0f
--- /dev/null
@@ -0,0 +1,978 @@
+/** @file\r
+  Basic commands and command processing infrastructure for EBL\r
+\r
+  Copyright (c) 2007, Intel Corporation<BR>\r
+  Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.\r
+\r
+  All rights reserved. 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
+#include "Ebl.h"\r
+#include <Protocol/DiskIo.h>\r
+#include <Protocol/BlockIo.h>\r
+\r
+UINTN             mCmdTableMaxIndex = EBL_MAX_COMMAND_COUNT;\r
+UINTN             mCmdTableNextFreeIndex = 0;\r
+EBL_COMMAND_TABLE *mCmdTable[EBL_MAX_COMMAND_COUNT];\r
+\r
+/**\r
+  Converts a lowercase Ascii character to upper one\r
+\r
+  If Chr is lowercase Ascii character, then converts it to upper one.\r
+\r
+  If Value >= 0xA0, then ASSERT().\r
+  If (Value & 0x0F) >= 0x0A, then ASSERT().\r
+\r
+  @param  chr   one Ascii character\r
+\r
+  @return The uppercase value of Ascii character \r
+\r
+**/\r
+STATIC\r
+CHAR8\r
+AsciiToUpper (\r
+  IN      CHAR8                     Chr\r
+  )\r
+{\r
+  return (UINT8) ((Chr >= 'a' && Chr <= 'z') ? Chr - ('a' - 'A') : Chr);\r
+}\r
+\r
+\r
+/**\r
+  Case insensitve comparison of two Null-terminated Unicode strings with maximum\r
+  lengths, and returns the difference between the first mismatched Unicode\r
+  characters.\r
+  This function compares the Null-terminated Unicode string FirstString to the\r
+  Null-terminated Unicode string SecondString. At most, Length Unicode\r
+  characters will be compared. If Length is 0, then 0 is returned. If\r
+  FirstString is identical to SecondString, then 0 is returned. Otherwise, the\r
+  value returned is the first mismatched Unicode character in SecondString\r
+  subtracted from the first mismatched Unicode character in FirstString.\r
+  \r
+  @param  FirstString   Pointer to a Null-terminated ASCII string.  \r
+  @param  SecondString  Pointer to a Null-terminated ASCII string.\r
+  @param  Length        Max length to compare.\r
+  \r
+  @retval 0   FirstString is identical to SecondString using case insensitive\r
+              comparisons.\r
+  @retval !=0 FirstString is not identical to SecondString using case\r
+              insensitive comparisons.\r
+\r
+**/\r
+INTN\r
+EFIAPI\r
+AsciiStrniCmp (\r
+  IN      CONST CHAR8               *FirstString,\r
+  IN      CONST CHAR8               *SecondString,\r
+  IN      UINTN                     Length\r
+  )\r
+{\r
+  if (Length == 0) {\r
+    return 0;\r
+  }\r
+\r
+  while ((AsciiToUpper (*FirstString) != '\0') &&\r
+         (AsciiToUpper (*FirstString) == AsciiToUpper (*SecondString)) &&\r
+         (Length > 1)) {\r
+    FirstString++;\r
+    SecondString++;\r
+    Length--;\r
+  }\r
+  \r
+  return AsciiToUpper (*FirstString) - AsciiToUpper (*SecondString);\r
+}\r
+\r
+\r
+\r
+/**\r
+  Add a command to the mCmdTable. If there is no free space in the command \r
+  table ASSERT. The mCmdTable is maintained in alphabetical order and the \r
+  new entry is inserted into its sorted possition.\r
+\r
+  @param  Entry   Commnad Entry to add to the CmdTable\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+EblAddCommand (\r
+  IN const EBL_COMMAND_TABLE   *Entry\r
+  )\r
+{\r
+  UINTN               Count;\r
+\r
+  if (mCmdTableNextFreeIndex == EBL_MAX_COMMAND_COUNT) {\r
+    //\r
+    // Ran out of space to store commands. Increase EBL_MAX_COMMAND_COUNT\r
+    //\r
+    ASSERT (FALSE);\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Add command and Insertion sort array in the process\r
+  //\r
+  mCmdTable[mCmdTableNextFreeIndex] = (EBL_COMMAND_TABLE *)Entry;\r
+  if (mCmdTableNextFreeIndex != 0) {\r
+    for (Count = mCmdTableNextFreeIndex; Count > 0; Count--) {\r
+      if (AsciiStriCmp (mCmdTable[Count - 1]->Name, Entry->Name) <= 0) {\r
+        break;\r
+      }\r
+      \r
+      mCmdTable[Count] = mCmdTable[Count - 1];\r
+    }\r
+    mCmdTable[Count] = (EBL_COMMAND_TABLE *)Entry;\r
+  }\r
+\r
+  mCmdTableNextFreeIndex++;\r
+}\r
+\r
+\r
+/**\r
+  Add an set of commands to the command table. Most commonly used on static \r
+  array of commands.\r
+\r
+  @param  EntryArray   Pointer to array of command entries\r
+  @param  ArrayCount   Number of commnad entries to add\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+EblAddCommands (\r
+  IN const EBL_COMMAND_TABLE   *EntryArray,\r
+  IN UINTN                     ArrayCount\r
+  )\r
+{\r
+  UINTN   Index;\r
+\r
+  for (Index = 0; Index < ArrayCount; Index++) {\r
+    EblAddCommand (&EntryArray[Index]);\r
+  }\r
+}\r
+\r
+\r
+EBL_ADD_COMMAND_PROTOCOL gEblAddCommand = {\r
+  EblAddCommand,\r
+  EblAddCommands,\r
+  EblGetCharKey,\r
+  EblAnyKeyToContinueQtoQuit\r
+};\r
+\r
+\r
+\r
+/**\r
+  Return the best matching command for the passed in command name. The match \r
+  does not have to be exact, it just needs to be unqiue. This enables commands\r
+  to be shortend to the smallest set of starting characters that is unique.\r
+\r
+  @param  CommandName   Name of command to search for\r
+\r
+  @return NULL  CommandName did not match or was not unique\r
+          Other Pointer to EBL_COMMAND_TABLE entry for CommandName\r
+\r
+**/\r
+EBL_COMMAND_TABLE *\r
+EblGetCommand (\r
+  IN CHAR8    *CommandName\r
+  )\r
+{\r
+  UINTN               Index;\r
+  UINTN               BestMatchCount;\r
+  UINTN               Length;\r
+  EBL_COMMAND_TABLE   *Match;\r
+\r
+  Length = AsciiStrLen (CommandName);\r
+  for (Index = 0, BestMatchCount = 0, Match = NULL; Index < mCmdTableNextFreeIndex; Index++) {\r
+    if (AsciiStriCmp (mCmdTable[Index]->Name,  CommandName) == 0) {\r
+      // match a command exactly\r
+      return mCmdTable[Index];\r
+    }\r
+\r
+    if (AsciiStrniCmp (CommandName, mCmdTable[Index]->Name, Length) == 0)  {\r
+      // partial match, so keep looking to make sure there is only one partial match\r
+      BestMatchCount++;\r
+      Match = mCmdTable[Index];\r
+    }\r
+  }\r
+\r
+  if (BestMatchCount == 1) {\r
+    return Match;\r
+  }\r
+\r
+  //\r
+  // We had no matches or too many matches\r
+  //\r
+  return NULL;\r
+}\r
+\r
+\r
+\r
+/**\r
+  List out help information on all the commands or print extended information \r
+  about a specific passed in command.\r
+\r
+  Argv[0] - "help"\r
+  Argv[1] - Command to display help about\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+EblHelpCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  UINTN   Index;\r
+  CHAR8   *Ptr;\r
+  UINTN   CurrentRow;\r
+\r
+  if (Argc == 1) {\r
+    // Print all the commands\r
+    AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");\r
+    for (Index = 0; Index < mCmdTableNextFreeIndex; Index++) {\r
+      EblSetTextColor (EFI_YELLOW);\r
+      AsciiPrint (" %a", mCmdTable[Index]->Name);\r
+      EblSetTextColor (0);\r
+      AsciiPrint ("%a\n", mCmdTable[Index]->HelpSummary);\r
+    }\r
+  } else if (Argv[1] != NULL) {\r
+    // Print specific help \r
+    for (Index = 0, CurrentRow = 0; Index < mCmdTableNextFreeIndex; Index++) {\r
+      if (AsciiStriCmp (Argv[1], mCmdTable[Index]->Name) == 0) {\r
+        Ptr = (mCmdTable[Index]->Help == NULL) ? mCmdTable[Index]->HelpSummary : mCmdTable[Index]->Help;\r
+        AsciiPrint ("%a%a\n", Argv[1], Ptr);\r
+        if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) {\r
+          break;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Exit the EBL. If the commnad processor sees EFI_ABORTED return status it will\r
+  exit the EBL.\r
+\r
+  Argv[0] - "exit"\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_ABORTED\r
+\r
+**/\r
+EFI_STATUS\r
+EblExitCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  EFI_STATUS              Status;
+  UINTN                   MemoryMapSize;
+  EFI_MEMORY_DESCRIPTOR   *MemoryMap;
+  UINTN                   MapKey;
+  UINTN                   DescriptorSize;
+  UINTN                   DescriptorVersion;
+  UINTN                   Pages;
+\r
+  if (Argc > 1) { \r
+    if (AsciiStriCmp (Argv[1], "efi") != 0) {\r
+      return EFI_ABORTED;\r
+    }\r
+  } else if (Argc == 1) {\r
+    return EFI_ABORTED;\r
+  }\r
+  \r
+  MemoryMap = NULL;
+  MemoryMapSize = 0;
+  do {
+    Status = gBS->GetMemoryMap (
+                    &MemoryMapSize,
+                    MemoryMap,
+                    &MapKey,
+                    &DescriptorSize,
+                    &DescriptorVersion
+                    );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+
+      Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;
+      MemoryMap = AllocatePages (Pages);
+    
+      //
+      // Get System MemoryMap
+      //
+      Status = gBS->GetMemoryMap (
+                      &MemoryMapSize,
+                      MemoryMap,
+                      &MapKey,
+                      &DescriptorSize,
+                      &DescriptorVersion
+                      );
+      // Don't do anything between the GetMemoryMap() and ExitBootServices()
+      if (!EFI_ERROR (Status)) {
+        Status = gBS->ExitBootServices (gImageHandle, MapKey);
+        if (EFI_ERROR (Status)) {
+          FreePages (MemoryMap, Pages);
+          MemoryMap = NULL;
+          MemoryMapSize = 0;
+        }
+      }
+    }
+  } while (EFI_ERROR (Status));
+\r
+  //\r
+  // At this point it is very dangerous to do things EFI as most of EFI is now gone.\r
+  // This command is useful if you are working with a debugger as it will shutdown\r
+  // DMA and other things that could break a soft resets.\r
+  //  \r
+  CpuDeadLoop ();\r
+  \r
+  // Should never get here, but makes the compiler happy\r
+  return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+  Update the screen by decrementing the timeout value.\r
+  This AsciiPrint has to match the AsciiPrint in \r
+  EblPauseCmd. \r
+\r
+  @param  ElaspedTime   Current timout value remaining\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+EblPauseCallback (\r
+  IN  UINTN   ElapsedTime\r
+  )\r
+{\r
+  AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b   \b\b%3d seconds", ElapsedTime);\r
+}\r
+\r
+/**\r
+  Pause until a key is pressed and abort the remaining commands on the command\r
+  line. If no key is pressed continue processing the command line. This command\r
+  allows the user to stop an operation from happening and return control to the\r
+  command prompt.\r
+\r
+  Argv[0] - "pause"\r
+  Argv[1] - timeout value is decimal seconds\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_SUCCESS  Timeout expired with no input\r
+  @return EFI_TIMEOUT  Stop procesing other commands on the same command line\r
+\r
+**/\r
+EFI_STATUS\r
+EblPauseCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINTN           Delay;\r
+  EFI_INPUT_KEY   Key;\r
+\r
+  Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]);\r
+\r
+  AsciiPrint ("Hit any key to break. You have %3d seconds", Delay);\r
+  Status = EblGetCharKey (&Key, Delay, EblPauseCallback);\r
+  AsciiPrint ("\n");\r
+\r
+  // If we timeout then the pause succeded thus return success\r
+  // If we get a key return timout to stop other commnad on this cmd line\r
+  return (Status == EFI_SUCCESS) ? EFI_TIMEOUT : EFI_SUCCESS;;\r
+}\r
+\r
+\r
+/**\r
+  On a debug build issue a software breakpoint to enter the debugger\r
+\r
+  Argv[0] - "break"\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+EblBreakPointCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  CpuBreakpoint ();\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Reset the system. If no Argument do a Cold reset. If argument use that reset type\r
+  (W)arm = Warm Reset\r
+  (S)hutdown = Shutdown Reset\r
+\r
+  Argv[0] - "reset"\r
+  Argv[1] - warm or shutdown reset type\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+EblResetCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  EFI_RESET_TYPE    ResetType;\r
+\r
+  ResetType = EfiResetCold;\r
+  if (Argc > 1) {\r
+    switch (*Argv[1]) {\r
+    case 'W':\r
+    case 'w':\r
+      ResetType = EfiResetWarm;\r
+      break;\r
+    case 'S':\r
+    case 's':\r
+      ResetType = EfiResetShutdown;\r
+    }\r
+  } \r
+\r
+  gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Toggle page break global. This turns on and off prompting to Quit or hit any\r
+  key to continue when a command is about to scroll the screen with its output\r
+\r
+  Argv[0] - "page"\r
+  Argv[1] - on or off\r
+\r
+  @param  Argc   Number of command arguments in Argv\r
+  @param  Argv   Array of strings that represent the parsed command line. \r
+                 Argv[0] is the comamnd name\r
+\r
+  @return EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+EblPageCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  if (Argc <= 1) {\r
+    // toggle setting   \r
+    gPageBreak = (gPageBreak) ? FALSE : TRUE;\r
+  } else {\r
+    // use argv to set the value\r
+    if ((Argv[1][0] == 'o') || (Argv[1][0] == 'O')) {\r
+      if ((Argv[1][1] == 'n') || (Argv[1][1] == 'N')) {\r
+        gPageBreak = TRUE;\r
+      } else if ((Argv[1][1] == 'f') || (Argv[1][1] == 'F')) {\r
+        gPageBreak = FALSE;\r
+      } else {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EblSleepCmd (\r
+  IN UINTN Argc,\r
+  IN CHAR8 **Argv\r
+  )\r
+{\r
+  UINTN Delay;\r
+\r
+  Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]);\r
+\r
+  gBS->Stall (Delay * 1000000);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+CHAR8\r
+ConvertToTextLine (\r
+  IN CHAR8  Character\r
+  )\r
+{\r
+  if (Character < ' ' || Character > '~')\r
+  {\r
+    return '.';\r
+  }\r
+  else\r
+  {\r
+    return Character;\r
+  }\r
+}\r
+\r
+UINTN\r
+GetBytes (\r
+  IN UINT8  *Address,\r
+  IN UINTN  Bytes\r
+  )\r
+{\r
+  UINTN Result = 0;\r
+\r
+  if (Bytes >= 1)\r
+    Result = *Address++;\r
+    \r
+  if (Bytes >= 2)\r
+    Result = (Result << 8) + *Address++;\r
+    \r
+  if (Bytes >= 3)\r
+    Result = (Result << 8) + *Address++;\r
+\r
+  return Result;\r
+}\r
+\r
+CHAR8 mBlanks[] = "                                           ";\r
+\r
+EFI_STATUS\r
+OutputData (\r
+  IN UINT8  *Address,\r
+  IN UINTN  Length,\r
+  IN UINTN  Width,\r
+  IN UINTN  Offset\r
+  )\r
+{\r
+  UINT8 *EndAddress;\r
+  UINTN Line;\r
+  CHAR8 TextLine[0x11];\r
+  UINTN CurrentRow = 0;\r
+  UINTN Bytes;\r
+  UINTN Spaces   = 0;\r
+  CHAR8 Blanks[80];\r
+\r
+  AsciiStrCpy (Blanks, mBlanks);\r
+  for (EndAddress = Address + Length; Address < EndAddress; Offset += Line)\r
+  {\r
+    AsciiPrint ("%08x: ", Offset);\r
+    for (Line = 0; (Line < 0x10) && (Address < EndAddress);)\r
+    {\r
+      Bytes = EndAddress - Address;\r
+            \r
+      switch (Width)\r
+      {\r
+        case 4:\r
+          if (Bytes >= 4)\r
+          {\r
+            AsciiPrint ("%08x ", *((UINT32 *)Address));\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+          }\r
+          else\r
+          {\r
+            AsciiPrint ("%08x ", GetBytes(Address, Bytes));\r
+            Address += Bytes;\r
+            Line    += Bytes;\r
+          }\r
+          break;\r
+\r
+        case 2:\r
+          if (Bytes >= 2)\r
+          {\r
+            AsciiPrint ("%04x ", *((UINT16 *)Address));\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+            TextLine[Line++] = ConvertToTextLine(*Address++);\r
+          }\r
+          else\r
+          {\r
+            AsciiPrint ("%04x ", GetBytes(Address, Bytes));\r
+            Address += Bytes;\r
+            Line    += Bytes;\r
+          }\r
+          break;\r
+\r
+        case 1:\r
+          AsciiPrint ("%02x ", *((UINT8 *)Address));\r
+          TextLine[Line++] = ConvertToTextLine(*Address++);\r
+          break;\r
+\r
+                         default:\r
+                                 AsciiPrint ("Width must be 1, 2, or 4!\n");\r
+                                 return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    // Pad spaces\r
+    if (Line < 0x10)\r
+    {\r
+      switch (Width)\r
+      {\r
+        case 4:\r
+          Spaces = 9 * ((0x10 - Line)/4);\r
+          break;\r
+        case 2:\r
+          Spaces = 5 * ((0x10 - Line)/2);\r
+          break;\r
+        case 1:\r
+          Spaces = 3 * (0x10 - Line);\r
+          break;\r
+      }\r
+\r
+      Blanks[Spaces] = '\0';\r
+\r
+      AsciiPrint(Blanks);\r
+      \r
+      Blanks[Spaces] = ' ';\r
+    }\r
+\r
+    TextLine[Line] = 0;\r
+    AsciiPrint ("|%a|\n", TextLine);\r
+\r
+    if (EblAnyKeyToContinueQtoQuit(&CurrentRow, FALSE))\r
+    {\r
+      return EFI_END_OF_FILE;\r
+    }\r
+  }\r
+\r
+  if (Length % Width != 0)\r
+  {\r
+    AsciiPrint ("%08x\n", Offset);\r
+  }\r
+  \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+#define HEXDUMP_CHUNK 1024\r
+\r
+EFI_STATUS\r
+EblHexdumpCmd (\r
+  IN UINTN  Argc,\r
+  IN CHAR8  **Argv\r
+  )\r
+{\r
+  EFI_OPEN_FILE *File;\r
+  VOID          *Location;\r
+  UINTN         Size;\r
+  UINTN         Width = 1;\r
+  UINTN         Offset = 0;\r
+  EFI_STATUS    Status;\r
+  UINTN         Chunk = HEXDUMP_CHUNK;\r
+\r
+  if ((Argc < 2) || (Argc > 3))\r
+  {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  if (Argc == 3)\r
+  {\r
+      Width = AsciiStrDecimalToUintn(Argv[2]);\r
+  }\r
+  \r
+  if ((Width != 1) && (Width != 2) && (Width != 4))\r
+  {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  File = EfiOpen(Argv[1], EFI_FILE_MODE_READ, 0);\r
+  if (File == NULL)\r
+  {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Location = AllocatePool(Chunk);\r
+  Size     = EfiTell(File, NULL);\r
+\r
+  for (Offset = 0; Offset + HEXDUMP_CHUNK <= Size; Offset += Chunk)\r
+  {\r
+    Chunk = HEXDUMP_CHUNK;\r
+    \r
+    Status = EfiRead(File, Location, &Chunk);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint ("Error reading file content\n");\r
+      goto Exit;\r
+    }\r
+\r
+    Status = OutputData(Location, Chunk, Width, File->BaseOffset + Offset);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      if (Status == EFI_END_OF_FILE) {\r
+        Status = EFI_SUCCESS;\r
+      }\r
+      goto Exit;\r
+    }\r
+  }\r
+  \r
+  // Any left over?\r
+  if (Offset < Size)\r
+  {\r
+    Chunk = Size - Offset;\r
+    Status = EfiRead(File, Location, &Chunk);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint ("Error reading file content\n");\r
+      goto Exit;\r
+    }\r
+\r
+    Status = OutputData(Location, Chunk, Width, File->BaseOffset + Offset);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      if (Status == EFI_END_OF_FILE) {\r
+        Status = EFI_SUCCESS;\r
+      }\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+Exit:\r
+  EfiClose(File);\r
+\r
+  FreePool(Location);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+#define USE_DISKIO 1\r
+\r
+EFI_STATUS\r
+EblDiskIoCmd (\r
+  IN UINTN Argc,\r
+  IN CHAR8 **Argv\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+  UINTN Offset;\r
+  UINT8 *EndOffset;\r
+  UINTN Length;\r
+  UINTN Line;\r
+  UINT8 *Buffer;\r
+  UINT8 *BufferOffset;\r
+  CHAR8 TextLine[0x11];\r
+#if USE_DISKIO\r
+  EFI_DISK_IO_PROTOCOL  *DiskIo;\r
+#else\r
+  EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+  UINTN                 Lba;\r
+#endif  \r
+\r
+  if (AsciiStrCmp(Argv[1], "r") == 0)\r
+  {  \r
+    Offset = AsciiStrHexToUintn(Argv[2]);\r
+    Length = AsciiStrHexToUintn(Argv[3]);\r
+\r
+#if USE_DISKIO\r
+    Status = gBS->LocateProtocol(&gEfiDiskIoProtocolGuid, NULL, (VOID **)&DiskIo);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("Did not locate DiskIO\n");\r
+      return Status;\r
+    }\r
+\r
+    Buffer = AllocatePool(Length);\r
+    BufferOffset = Buffer;\r
+    \r
+    Status = DiskIo->ReadDisk(DiskIo, SIGNATURE_32('f','l','s','h'), Offset, Length, Buffer);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("DiskIO read failed\n");\r
+      gBS->FreePool(Buffer);\r
+      return Status;\r
+    }    \r
+#else\r
+    Status = gBS->LocateProtocol(&gEfiBlockIoProtocolGuid, NULL, (VOID **)&BlockIo);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("Did not locate BlockIo\n");\r
+      return Status;\r
+    }\r
+    \r
+    Length = BlockIo->Media->BlockSize;\r
+    Buffer = AllocatePool(Length);\r
+    BufferOffset = Buffer;\r
+    Lba = Offset/BlockIo->Media->BlockSize;\r
+    \r
+    Status = BlockIo->ReadBlocks(BlockIo, BlockIo->Media->MediaId, Lba, Length, Buffer);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("BlockIo read failed\n");\r
+      gBS->FreePool(Buffer);\r
+      return Status;\r
+    }\r
+    \r
+    // Whack offset to what we actually read from\r
+    Offset = Lba * BlockIo->Media->BlockSize;\r
+    \r
+    Length = 0x100;\r
+#endif\r
+\r
+    for (EndOffset = BufferOffset + Length; BufferOffset < EndOffset; Offset += 0x10)\r
+    {\r
+      AsciiPrint ("%08x: ", Offset);\r
+      \r
+      for (Line = 0; Line < 0x10; Line++)\r
+      {\r
+        AsciiPrint ("%02x ", *BufferOffset);\r
+\r
+        if (*BufferOffset < ' ' || *BufferOffset > '~')\r
+          TextLine[Line] = '.';\r
+        else\r
+          TextLine[Line] = *BufferOffset;\r
+          \r
+        BufferOffset++;\r
+      }\r
+\r
+      TextLine[Line] = '\0';\r
+      AsciiPrint ("|%a|\n", TextLine);\r
+    }\r
+    \r
+    gBS->FreePool(Buffer);\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  else if (AsciiStrCmp(Argv[1], "w") == 0)\r
+  {\r
+    Offset = AsciiStrHexToUintn(Argv[2]);\r
+    Length = AsciiStrHexToUintn(Argv[3]);\r
+    Buffer = (UINT8 *)AsciiStrHexToUintn(Argv[4]);\r
+    \r
+#if USE_DISKIO\r
+    Status = gBS->LocateProtocol(&gEfiDiskIoProtocolGuid, NULL, (VOID **)&DiskIo);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("Did not locate DiskIO\n");\r
+      return Status;\r
+    }\r
+\r
+    Status = DiskIo->WriteDisk(DiskIo, SIGNATURE_32('f','l','s','h'), Offset, Length, Buffer);\r
+    if (EFI_ERROR(Status))\r
+    {\r
+      AsciiPrint("DiskIO write failed\n");\r
+      return Status;\r
+    }\r
+\r
+#else\r
+#endif\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  else\r
+  {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+}\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mCmdTemplate[] =\r
+{\r
+  {\r
+    "reset",\r
+    " [type]; Reset system. type = [warm] [shutdown] default is cold reset",\r
+    NULL,\r
+    EblResetCmd\r
+  },\r
+  {\r
+    "exit",\r
+    "; Exit EBL",\r
+    NULL,\r
+    EblExitCmd\r
+  },\r
+  {\r
+    "help",\r
+    " [cmd]; Help on cmd or a list of all commands if cmd is ommited",\r
+    NULL,\r
+    EblHelpCmd\r
+  },\r
+  {\r
+    "break",\r
+    "; Generate debugging breakpoint",\r
+    NULL,\r
+    EblBreakPointCmd\r
+  },\r
+  {\r
+    "page",\r
+    " [on|off]]; toggle promting on command output larger than screen",\r
+    NULL,\r
+    EblPageCmd\r
+  },\r
+  {\r
+    "pause",\r
+    " [sec]; Pause for sec[10] seconds. ",\r
+    NULL,\r
+    EblPauseCmd\r
+  },\r
+  {\r
+    "sleep",\r
+    " [sec]; Sleep for sec[10] seconds. ",\r
+    NULL,\r
+    EblSleepCmd\r
+  },\r
+  {\r
+    "hexdump",\r
+    " filename ; dump a file as hex bytes",\r
+    NULL,\r
+    EblHexdumpCmd\r
+  },\r
+  {\r
+    "diskio",\r
+    " [r|w] offset [length [dataptr]]; do a DiskIO read or write ",\r
+    NULL,\r
+    EblDiskIoCmd\r
+  }  \r
+};\r
+\r
+\r
+EFI_HANDLE  gExternalCmdHandle = NULL;\r
+\r
+/**\r
+  Initialize the commands in this in this file\r
+**/\r
+VOID\r
+EblInitializeCmdTable (\r
+  VOID\r
+  )\r
+{\r
+\r
+  EblAddCommands (mCmdTemplate, sizeof (mCmdTemplate)/sizeof (EBL_COMMAND_TABLE));\r
+  \r
+  gBS->InstallProtocolInterface (\r
+        &gExternalCmdHandle,\r
+        &gEfiEblAddCommandProtocolGuid,\r
+        EFI_NATIVE_INTERFACE,\r
+        &gEblAddCommand\r
+        );\r
+\r
+}\r
+\r
+\r
+VOID\r
+EblShutdownExternalCmdTable (\r
+  VOID\r
+  )\r
+{\r
+  gBS->UninstallProtocolInterface (gExternalCmdHandle, &gEfiEblAddCommandProtocolGuid,  &gEblAddCommand);\r
+}\r
+\r
+\r