Home | History | Annotate | Download | only in Ebl
      1 /** @file
      2   Basic commands and command processing infrastructure for EBL
      3 
      4   Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
      5   Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
      6   (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
      7 
      8   This program and the accompanying materials
      9   are licensed and made available under the terms and conditions of the BSD License
     10   which accompanies this distribution.  The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include "Ebl.h"
     19 #include <Protocol/DiskIo.h>
     20 #include <Protocol/BlockIo.h>
     21 
     22 UINTN             mCmdTableMaxIndex = EBL_MAX_COMMAND_COUNT;
     23 UINTN             mCmdTableNextFreeIndex = 0;
     24 EBL_COMMAND_TABLE *mCmdTable[EBL_MAX_COMMAND_COUNT];
     25 
     26 /**
     27   Converts a lowercase Ascii character to upper one
     28 
     29   If Chr is lowercase Ascii character, then converts it to upper one.
     30 
     31   If Value >= 0xA0, then ASSERT().
     32   If (Value & 0x0F) >= 0x0A, then ASSERT().
     33 
     34   @param  chr   one Ascii character
     35 
     36   @return The uppercase value of Ascii character
     37 
     38 **/
     39 STATIC
     40 CHAR8
     41 AsciiToUpper (
     42   IN      CHAR8                     Chr
     43   )
     44 {
     45   return (UINT8) ((Chr >= 'a' && Chr <= 'z') ? Chr - ('a' - 'A') : Chr);
     46 }
     47 
     48 
     49 /**
     50   Case insensitive comparison of two Null-terminated Unicode strings with maximum
     51   lengths, and returns the difference between the first mismatched Unicode
     52   characters.
     53   This function compares the Null-terminated Unicode string FirstString to the
     54   Null-terminated Unicode string SecondString. At most, Length Unicode
     55   characters will be compared. If Length is 0, then 0 is returned. If
     56   FirstString is identical to SecondString, then 0 is returned. Otherwise, the
     57   value returned is the first mismatched Unicode character in SecondString
     58   subtracted from the first mismatched Unicode character in FirstString.
     59 
     60   @param  FirstString   Pointer to a Null-terminated ASCII string.
     61   @param  SecondString  Pointer to a Null-terminated ASCII string.
     62   @param  Length        Max length to compare.
     63 
     64   @retval 0   FirstString is identical to SecondString using case insensitive
     65               comparisons.
     66   @retval !=0 FirstString is not identical to SecondString using case
     67               insensitive comparisons.
     68 
     69 **/
     70 INTN
     71 EFIAPI
     72 AsciiStrniCmp (
     73   IN      CONST CHAR8               *FirstString,
     74   IN      CONST CHAR8               *SecondString,
     75   IN      UINTN                     Length
     76   )
     77 {
     78   if (Length == 0) {
     79     return 0;
     80   }
     81 
     82   while ((AsciiToUpper (*FirstString) != '\0') &&
     83          (AsciiToUpper (*FirstString) == AsciiToUpper (*SecondString)) &&
     84          (Length > 1)) {
     85     FirstString++;
     86     SecondString++;
     87     Length--;
     88   }
     89 
     90   return AsciiToUpper (*FirstString) - AsciiToUpper (*SecondString);
     91 }
     92 
     93 
     94 
     95 /**
     96   Add a command to the mCmdTable. If there is no free space in the command
     97   table ASSERT. The mCmdTable is maintained in alphabetical order and the
     98   new entry is inserted into its sorted position.
     99 
    100   @param  Entry   Command Entry to add to the CmdTable
    101 
    102 **/
    103 VOID
    104 EFIAPI
    105 EblAddCommand (
    106   IN const EBL_COMMAND_TABLE   *Entry
    107   )
    108 {
    109   UINTN               Count;
    110 
    111   if (mCmdTableNextFreeIndex == EBL_MAX_COMMAND_COUNT) {
    112     //
    113     // Ran out of space to store commands. Increase EBL_MAX_COMMAND_COUNT
    114     //
    115     ASSERT (FALSE);
    116     return;
    117   }
    118 
    119   //
    120   // Add command and Insertion sort array in the process
    121   //
    122   mCmdTable[mCmdTableNextFreeIndex] = (EBL_COMMAND_TABLE *)Entry;
    123   if (mCmdTableNextFreeIndex != 0) {
    124     for (Count = mCmdTableNextFreeIndex; Count > 0; Count--) {
    125       if (AsciiStriCmp (mCmdTable[Count - 1]->Name, Entry->Name) <= 0) {
    126         break;
    127       }
    128 
    129       mCmdTable[Count] = mCmdTable[Count - 1];
    130     }
    131     mCmdTable[Count] = (EBL_COMMAND_TABLE *)Entry;
    132   }
    133 
    134   mCmdTableNextFreeIndex++;
    135 }
    136 
    137 
    138 /**
    139   Add an set of commands to the command table. Most commonly used on static
    140   array of commands.
    141 
    142   @param  EntryArray   Pointer to array of command entries
    143   @param  ArrayCount   Number of command entries to add
    144 
    145 **/
    146 VOID
    147 EFIAPI
    148 EblAddCommands (
    149   IN const EBL_COMMAND_TABLE   *EntryArray,
    150   IN UINTN                     ArrayCount
    151   )
    152 {
    153   UINTN   Index;
    154 
    155   for (Index = 0; Index < ArrayCount; Index++) {
    156     EblAddCommand (&EntryArray[Index]);
    157   }
    158 }
    159 
    160 
    161 EBL_ADD_COMMAND_PROTOCOL gEblAddCommand = {
    162   EblAddCommand,
    163   EblAddCommands,
    164   EblGetCharKey,
    165   EblAnyKeyToContinueQtoQuit
    166 };
    167 
    168 
    169 
    170 /**
    171   Return the best matching command for the passed in command name. The match
    172   does not have to be exact, it just needs to be unique. This enables commands
    173   to be shortened to the smallest set of starting characters that is unique.
    174 
    175   @param  CommandName   Name of command to search for
    176 
    177   @return NULL  CommandName did not match or was not unique
    178           Other Pointer to EBL_COMMAND_TABLE entry for CommandName
    179 
    180 **/
    181 EBL_COMMAND_TABLE *
    182 EblGetCommand (
    183   IN CHAR8    *CommandName
    184   )
    185 {
    186   UINTN               Index;
    187   UINTN               BestMatchCount;
    188   UINTN               Length;
    189   EBL_COMMAND_TABLE   *Match;
    190   CHAR8               *Str;
    191 
    192   Length = AsciiStrLen (CommandName);
    193   Str = AsciiStrStr (CommandName, ".");
    194   if (Str != NULL) {
    195     // If the command includes a trailing . command extension skip it for the match.
    196     // Example: hexdump.4
    197     Length = (UINTN)(Str - CommandName);
    198   }
    199 
    200   for (Index = 0, BestMatchCount = 0, Match = NULL; Index < mCmdTableNextFreeIndex; Index++) {
    201     if (AsciiStriCmp (mCmdTable[Index]->Name,  CommandName) == 0) {
    202       // match a command exactly
    203       return mCmdTable[Index];
    204     }
    205 
    206     if (AsciiStrniCmp (CommandName, mCmdTable[Index]->Name, Length) == 0)  {
    207       // partial match, so keep looking to make sure there is only one partial match
    208       BestMatchCount++;
    209       Match = mCmdTable[Index];
    210     }
    211   }
    212 
    213   if (BestMatchCount == 1) {
    214     return Match;
    215   }
    216 
    217   //
    218   // We had no matches or too many matches
    219   //
    220   return NULL;
    221 }
    222 
    223 
    224 UINTN
    225 CountNewLines (
    226   IN CHAR8  *Str
    227   )
    228 {
    229   UINTN Count;
    230 
    231   if (Str == NULL) {
    232     return 0;
    233   }
    234 
    235   for (Count = 0; *Str != '\0'; Str++) {
    236     if (Str[Count] == '\n') {
    237       Count++;
    238     }
    239   }
    240 
    241   return Count;
    242 }
    243 
    244 
    245 /**
    246   List out help information on all the commands or print extended information
    247   about a specific passed in command.
    248 
    249   Argv[0] - "help"
    250   Argv[1] - Command to display help about
    251 
    252   @param  Argc   Number of command arguments in Argv
    253   @param  Argv   Array of strings that represent the parsed command line.
    254                  Argv[0] is the command name
    255 
    256   @return EFI_SUCCESS
    257 
    258 **/
    259 EFI_STATUS
    260 EFIAPI
    261 EblHelpCmd (
    262   IN UINTN  Argc,
    263   IN CHAR8  **Argv
    264   )
    265 {
    266   UINTN   Index;
    267   CHAR8   *Ptr;
    268   UINTN   CurrentRow = 0;
    269 
    270   if (Argc == 1) {
    271     // Print all the commands
    272     AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");
    273     CurrentRow++;
    274     for (Index = 0; Index < mCmdTableNextFreeIndex; Index++) {
    275       EblSetTextColor (EFI_YELLOW);
    276       AsciiPrint (" %a", mCmdTable[Index]->Name);
    277       EblSetTextColor (0);
    278       AsciiPrint ("%a\n", mCmdTable[Index]->HelpSummary);
    279       // Handle multi line help summaries
    280       CurrentRow += CountNewLines (mCmdTable[Index]->HelpSummary);
    281       if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) {
    282         break;
    283       }
    284     }
    285   } else if (Argv[1] != NULL) {
    286     // Print specific help
    287     for (Index = 0, CurrentRow = 0; Index < mCmdTableNextFreeIndex; Index++) {
    288       if (AsciiStriCmp (Argv[1], mCmdTable[Index]->Name) == 0) {
    289         Ptr = (mCmdTable[Index]->Help == NULL) ? mCmdTable[Index]->HelpSummary : mCmdTable[Index]->Help;
    290         AsciiPrint ("%a%a\n", Argv[1], Ptr);
    291         // Handle multi line help summaries
    292         CurrentRow += CountNewLines (Ptr);
    293         if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) {
    294           break;
    295         }
    296       }
    297     }
    298   }
    299 
    300   return EFI_SUCCESS;
    301 }
    302 
    303 
    304 /**
    305   Exit the EBL. If the command processor sees EFI_ABORTED return status it will
    306   exit the EBL.
    307 
    308   Argv[0] - "exit"
    309 
    310   @param  Argc   Number of command arguments in Argv
    311   @param  Argv   Array of strings that represent the parsed command line.
    312                  Argv[0] is the command name
    313 
    314   @return EFI_ABORTED
    315 
    316 **/
    317 EFI_STATUS
    318 EFIAPI
    319 EblExitCmd (
    320   IN UINTN  Argc,
    321   IN CHAR8  **Argv
    322   )
    323 {
    324   EFI_STATUS              Status;
    325   UINTN                   MemoryMapSize;
    326   EFI_MEMORY_DESCRIPTOR   *MemoryMap;
    327   UINTN                   MapKey;
    328   UINTN                   DescriptorSize;
    329   UINT32                  DescriptorVersion;
    330   UINTN                   Pages;
    331 
    332   if (Argc > 1) {
    333     if (AsciiStriCmp (Argv[1], "efi") != 0) {
    334       return EFI_ABORTED;
    335     }
    336   } else if (Argc == 1) {
    337     return EFI_ABORTED;
    338   }
    339 
    340   MemoryMap = NULL;
    341   MemoryMapSize = 0;
    342   do {
    343     Status = gBS->GetMemoryMap (
    344                     &MemoryMapSize,
    345                     MemoryMap,
    346                     &MapKey,
    347                     &DescriptorSize,
    348                     &DescriptorVersion
    349                     );
    350     if (Status == EFI_BUFFER_TOO_SMALL) {
    351 
    352       Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;
    353       MemoryMap = AllocatePages (Pages);
    354 
    355       //
    356       // Get System MemoryMap
    357       //
    358       Status = gBS->GetMemoryMap (
    359                       &MemoryMapSize,
    360                       MemoryMap,
    361                       &MapKey,
    362                       &DescriptorSize,
    363                       &DescriptorVersion
    364                       );
    365       // Don't do anything between the GetMemoryMap() and ExitBootServices()
    366       if (!EFI_ERROR (Status)) {
    367         Status = gBS->ExitBootServices (gImageHandle, MapKey);
    368         if (EFI_ERROR (Status)) {
    369           FreePages (MemoryMap, Pages);
    370           MemoryMap = NULL;
    371           MemoryMapSize = 0;
    372         }
    373       }
    374     }
    375   } while (EFI_ERROR (Status));
    376 
    377   //
    378   // At this point it is very dangerous to do things EFI as most of EFI is now gone.
    379   // This command is useful if you are working with a debugger as it will shutdown
    380   // DMA and other things that could break a soft resets.
    381   //
    382   CpuDeadLoop ();
    383 
    384   // Should never get here, but makes the compiler happy
    385   return EFI_ABORTED;
    386 }
    387 
    388 
    389 /**
    390   Update the screen by decrementing the timeout value.
    391   This AsciiPrint has to match the AsciiPrint in
    392   EblPauseCmd.
    393 
    394   @param  ElaspedTime   Current timeout value remaining
    395 
    396 **/
    397 VOID
    398 EFIAPI
    399 EblPauseCallback (
    400   IN  UINTN   ElapsedTime
    401   )
    402 {
    403   AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b   \b\b%3d seconds", ElapsedTime);
    404 }
    405 
    406 /**
    407   Pause until a key is pressed and abort the remaining commands on the command
    408   line. If no key is pressed continue processing the command line. This command
    409   allows the user to stop an operation from happening and return control to the
    410   command prompt.
    411 
    412   Argv[0] - "pause"
    413   Argv[1] - timeout value is decimal seconds
    414 
    415   @param  Argc   Number of command arguments in Argv
    416   @param  Argv   Array of strings that represent the parsed command line.
    417                  Argv[0] is the command name
    418 
    419   @return EFI_SUCCESS  Timeout expired with no input
    420   @return EFI_TIMEOUT  Stop processing other commands on the same command line
    421 
    422 **/
    423 EFI_STATUS
    424 EFIAPI
    425 EblPauseCmd (
    426   IN UINTN  Argc,
    427   IN CHAR8  **Argv
    428   )
    429 {
    430   EFI_STATUS      Status;
    431   UINTN           Delay;
    432   EFI_INPUT_KEY   Key;
    433 
    434   Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]);
    435 
    436   AsciiPrint ("Hit any key to break. You have %3d seconds", Delay);
    437   Status = EblGetCharKey (&Key, Delay, EblPauseCallback);
    438   AsciiPrint ("\n");
    439 
    440   // If we timeout then the pause succeeded thus return success
    441   // If we get a key return timeout to stop other command on this cmd line
    442   return (Status == EFI_SUCCESS) ? EFI_TIMEOUT : EFI_SUCCESS;;
    443 }
    444 
    445 
    446 /**
    447   On a debug build issue a software breakpoint to enter the debugger
    448 
    449   Argv[0] - "break"
    450 
    451   @param  Argc   Number of command arguments in Argv
    452   @param  Argv   Array of strings that represent the parsed command line.
    453                  Argv[0] is the command name
    454 
    455   @return EFI_SUCCESS
    456 
    457 **/
    458 EFI_STATUS
    459 EFIAPI
    460 EblBreakPointCmd (
    461   IN UINTN  Argc,
    462   IN CHAR8  **Argv
    463   )
    464 {
    465   CpuBreakpoint ();
    466   return EFI_SUCCESS;
    467 }
    468 
    469 
    470 /**
    471   Reset the system. If no Argument do a Cold reset. If argument use that reset type
    472   (W)arm = Warm Reset
    473   (S)hutdown = Shutdown Reset
    474 
    475   Argv[0] - "reset"
    476   Argv[1] - warm or shutdown reset type
    477 
    478   @param  Argc   Number of command arguments in Argv
    479   @param  Argv   Array of strings that represent the parsed command line.
    480                  Argv[0] is the command name
    481 
    482   @return EFI_SUCCESS
    483 
    484 **/
    485 EFI_STATUS
    486 EFIAPI
    487 EblResetCmd (
    488   IN UINTN  Argc,
    489   IN CHAR8  **Argv
    490   )
    491 {
    492   EFI_RESET_TYPE    ResetType;
    493 
    494   ResetType = EfiResetCold;
    495   if (Argc > 1) {
    496     switch (*Argv[1]) {
    497     case 'W':
    498     case 'w':
    499       ResetType = EfiResetWarm;
    500       break;
    501     case 'S':
    502     case 's':
    503       ResetType = EfiResetShutdown;
    504     }
    505   }
    506 
    507   gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);
    508   return EFI_SUCCESS;
    509 }
    510 
    511 
    512 /**
    513   Toggle page break global. This turns on and off prompting to Quit or hit any
    514   key to continue when a command is about to scroll the screen with its output
    515 
    516   Argv[0] - "page"
    517   Argv[1] - on or off
    518 
    519   @param  Argc   Number of command arguments in Argv
    520   @param  Argv   Array of strings that represent the parsed command line.
    521                  Argv[0] is the command name
    522 
    523   @return EFI_SUCCESS
    524 
    525 **/
    526 EFI_STATUS
    527 EFIAPI
    528 EblPageCmd (
    529   IN UINTN  Argc,
    530   IN CHAR8  **Argv
    531   )
    532 {
    533   if (Argc <= 1) {
    534     // toggle setting
    535     gPageBreak = (gPageBreak) ? FALSE : TRUE;
    536   } else {
    537     // use argv to set the value
    538     if ((Argv[1][0] == 'o') || (Argv[1][0] == 'O')) {
    539       if ((Argv[1][1] == 'n') || (Argv[1][1] == 'N')) {
    540         gPageBreak = TRUE;
    541       } else if ((Argv[1][1] == 'f') || (Argv[1][1] == 'F')) {
    542         gPageBreak = FALSE;
    543       } else {
    544         return EFI_INVALID_PARAMETER;
    545       }
    546     }
    547   }
    548   return EFI_SUCCESS;
    549 }
    550 
    551 EFI_STATUS
    552 EFIAPI
    553 EblSleepCmd (
    554   IN UINTN Argc,
    555   IN CHAR8 **Argv
    556   )
    557 {
    558   UINTN Delay;
    559 
    560   Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]);
    561 
    562   gBS->Stall (Delay * 1000000);
    563 
    564   return EFI_SUCCESS;
    565 }
    566 
    567 CHAR8
    568 ConvertToTextLine (
    569   IN CHAR8  Character
    570   )
    571 {
    572   if (Character < ' ' || Character > '~') {
    573     return '.';
    574   } else {
    575     return Character;
    576   }
    577 }
    578 
    579 UINTN
    580 GetBytes (
    581   IN UINT8  *Address,
    582   IN UINTN  Bytes
    583   )
    584 {
    585   UINTN Result = 0;
    586 
    587   if (Bytes >= 1) {
    588     Result = *Address++;
    589   }
    590   if (Bytes >= 2) {
    591     Result = (Result << 8) + *Address++;
    592   }
    593   if (Bytes >= 3) {
    594     Result = (Result << 8) + *Address++;
    595   }
    596   return Result;
    597 }
    598 
    599 CHAR8 mBlanks[] = "                                           ";
    600 
    601 EFI_STATUS
    602 OutputData (
    603   IN UINT8  *Address,
    604   IN UINTN  Length,
    605   IN UINTN  Width,
    606   IN UINTN  Offset
    607   )
    608 {
    609   UINT8 *EndAddress;
    610   UINTN Line;
    611   CHAR8 TextLine[0x11];
    612   UINTN CurrentRow = 0;
    613   UINTN Bytes;
    614   UINTN Spaces   = 0;
    615   CHAR8 Blanks[80];
    616 
    617   AsciiStrCpyS (Blanks, sizeof Blanks, mBlanks);
    618   for (EndAddress = Address + Length; Address < EndAddress; Offset += Line) {
    619     AsciiPrint ("%08x: ", Offset);
    620     for (Line = 0; (Line < 0x10) && (Address < EndAddress);) {
    621       Bytes = EndAddress - Address;
    622 
    623       switch (Width) {
    624         case 4:
    625           if (Bytes >= 4) {
    626             AsciiPrint ("%08x ", *((UINT32 *)Address));
    627             TextLine[Line++] = ConvertToTextLine(*Address++);
    628             TextLine[Line++] = ConvertToTextLine(*Address++);
    629             TextLine[Line++] = ConvertToTextLine(*Address++);
    630             TextLine[Line++] = ConvertToTextLine(*Address++);
    631           } else {
    632             AsciiPrint ("%08x ", GetBytes(Address, Bytes));
    633             Address += Bytes;
    634             Line    += Bytes;
    635           }
    636           break;
    637 
    638         case 2:
    639           if (Bytes >= 2) {
    640             AsciiPrint ("%04x ", *((UINT16 *)Address));
    641             TextLine[Line++] = ConvertToTextLine(*Address++);
    642             TextLine[Line++] = ConvertToTextLine(*Address++);
    643           } else {
    644             AsciiPrint ("%04x ", GetBytes(Address, Bytes));
    645             Address += Bytes;
    646             Line    += Bytes;
    647           }
    648           break;
    649 
    650         case 1:
    651           AsciiPrint ("%02x ", *((UINT8 *)Address));
    652           TextLine[Line++] = ConvertToTextLine(*Address++);
    653           break;
    654 
    655         default:
    656           AsciiPrint ("Width must be 1, 2, or 4!\n");
    657           return EFI_INVALID_PARAMETER;
    658       }
    659     }
    660 
    661     // Pad spaces
    662     if (Line < 0x10) {
    663       switch (Width) {
    664         case 4:
    665           Spaces = 9 * ((0x10 - Line)/4);
    666           break;
    667         case 2:
    668           Spaces = 5 * ((0x10 - Line)/2);
    669           break;
    670         case 1:
    671           Spaces = 3 * (0x10 - Line);
    672           break;
    673       }
    674 
    675       Blanks[Spaces] = '\0';
    676 
    677       AsciiPrint(Blanks);
    678 
    679       Blanks[Spaces] = ' ';
    680     }
    681 
    682     TextLine[Line] = 0;
    683     AsciiPrint ("|%a|\n", TextLine);
    684 
    685     if (EblAnyKeyToContinueQtoQuit (&CurrentRow, FALSE)) {
    686       return EFI_END_OF_FILE;
    687     }
    688   }
    689 
    690   if (Length % Width != 0) {
    691     AsciiPrint ("%08x\n", Offset);
    692   }
    693 
    694   return EFI_SUCCESS;
    695 }
    696 
    697 
    698 /**
    699   See if command contains .# where # is a number. Return # as the Width
    700   or 1 as the default Width for commands.
    701 
    702   Example hexdump.4 returns a width of 4.
    703 
    704   @param  Argv   Argv[0] is the command name
    705 
    706   @return Width of command
    707 
    708 **/
    709 UINTN
    710 WidthFromCommandName (
    711   IN CHAR8  *Argv,
    712   IN UINTN  Default
    713   )
    714 {
    715   CHAR8         *Str;
    716   UINTN         Width;
    717 
    718   //Hexdump.2 HexDump.4 mean use a different width
    719   Str = AsciiStrStr (Argv, ".");
    720   if (Str != NULL) {
    721     Width = AsciiStrDecimalToUintn (Str + 1);
    722     if (Width == 0) {
    723       Width = Default;
    724     }
    725   } else {
    726     // Default answer
    727     return Default;
    728   }
    729 
    730   return Width;
    731 }
    732 
    733 #define HEXDUMP_CHUNK 1024
    734 
    735 /**
    736   Toggle page break global. This turns on and off prompting to Quit or hit any
    737   key to continue when a command is about to scroll the screen with its output
    738 
    739   Argv[0] - "hexdump"[.#]  # is optional 1,2, or 4 for width
    740   Argv[1] - Device or File to dump.
    741   Argv[2] - Optional offset to start dumping
    742   Argv[3] - Optional number of bytes to dump
    743 
    744   @param  Argc   Number of command arguments in Argv
    745   @param  Argv   Array of strings that represent the parsed command line.
    746                  Argv[0] is the command name
    747 
    748   @return EFI_SUCCESS
    749 
    750 **/
    751 EFI_STATUS
    752 EFIAPI
    753 EblHexdumpCmd (
    754   IN UINTN  Argc,
    755   IN CHAR8  **Argv
    756   )
    757 {
    758   EFI_OPEN_FILE *File;
    759   VOID          *Location;
    760   UINTN         Size;
    761   UINTN         Width;
    762   UINTN         Offset = 0;
    763   EFI_STATUS    Status;
    764   UINTN         Chunk = HEXDUMP_CHUNK;
    765 
    766   if ((Argc < 2) || (Argc > 4)) {
    767     return EFI_INVALID_PARAMETER;
    768   }
    769 
    770   Width = WidthFromCommandName (Argv[0], 1);
    771   if ((Width != 1) && (Width != 2) && (Width != 4)) {
    772     return EFI_INVALID_PARAMETER;
    773   }
    774 
    775   File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0);
    776   if (File == NULL) {
    777     return EFI_NOT_FOUND;
    778   }
    779 
    780   Location = AllocatePool (Chunk);
    781   Size     = (Argc > 3) ? AsciiStrHexToUintn (Argv[3]) : EfiTell (File, NULL);
    782 
    783   Offset = 0;
    784   if (Argc > 2) {
    785     Offset = AsciiStrHexToUintn (Argv[2]);
    786     if (Offset > 0) {
    787       // Make sure size includes the part of the file we have skipped
    788       Size += Offset;
    789     }
    790   }
    791 
    792   Status = EfiSeek (File, Offset, EfiSeekStart);
    793   if (EFI_ERROR (Status)) {
    794     goto Exit;
    795   }
    796 
    797   for (; Offset + HEXDUMP_CHUNK <= Size; Offset += Chunk) {
    798     Chunk = HEXDUMP_CHUNK;
    799     Status = EfiRead (File, Location, &Chunk);
    800     if (EFI_ERROR(Status)) {
    801       AsciiPrint ("Error reading file content\n");
    802       goto Exit;
    803     }
    804 
    805     Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset);
    806     if (EFI_ERROR(Status)) {
    807       if (Status == EFI_END_OF_FILE) {
    808         Status = EFI_SUCCESS;
    809       }
    810       goto Exit;
    811     }
    812   }
    813 
    814   // Any left over?
    815   if (Offset < Size) {
    816     Chunk = Size - Offset;
    817     Status = EfiRead (File, Location, &Chunk);
    818     if (EFI_ERROR(Status)) {
    819       AsciiPrint ("Error reading file content\n");
    820       goto Exit;
    821     }
    822 
    823     Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset);
    824     if (EFI_ERROR(Status)) {
    825       if (Status == EFI_END_OF_FILE) {
    826         Status = EFI_SUCCESS;
    827       }
    828       goto Exit;
    829     }
    830   }
    831 
    832 Exit:
    833   EfiClose (File);
    834 
    835   FreePool (Location);
    836 
    837   return EFI_SUCCESS;
    838 }
    839 
    840 
    841 GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mCmdTemplate[] =
    842 {
    843   {
    844     "reset",
    845     " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
    846     NULL,
    847     EblResetCmd
    848   },
    849   {
    850     "exit",
    851     "; Exit EBL",
    852     NULL,
    853     EblExitCmd
    854   },
    855   {
    856     "help",
    857     " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
    858     NULL,
    859     EblHelpCmd
    860   },
    861   {
    862     "break",
    863     "; Generate debugging breakpoint",
    864     NULL,
    865     EblBreakPointCmd
    866   },
    867   {
    868     "page",
    869     " [on|off]]; toggle promting on command output larger than screen",
    870     NULL,
    871     EblPageCmd
    872   },
    873   {
    874     "pause",
    875     " [sec]; Pause for sec[10] seconds. ",
    876     NULL,
    877     EblPauseCmd
    878   },
    879   {
    880     "sleep",
    881     " [sec]; Sleep for sec[10] seconds. ",
    882     NULL,
    883     EblSleepCmd
    884   },
    885   {
    886     "hexdump",
    887     "[.{1|2|4}] filename [Offset] [Size]; dump a file as hex .width",
    888     NULL,
    889     EblHexdumpCmd
    890   }
    891 };
    892 
    893 
    894 EFI_HANDLE  gExternalCmdHandle = NULL;
    895 
    896 /**
    897   Initialize the commands in this in this file
    898 **/
    899 VOID
    900 EblInitializeCmdTable (
    901   VOID
    902   )
    903 {
    904 
    905   EblAddCommands (mCmdTemplate, sizeof (mCmdTemplate)/sizeof (EBL_COMMAND_TABLE));
    906 
    907   gBS->InstallProtocolInterface (
    908         &gExternalCmdHandle,
    909         &gEfiEblAddCommandProtocolGuid,
    910         EFI_NATIVE_INTERFACE,
    911         &gEblAddCommand
    912         );
    913 
    914 }
    915 
    916 
    917 VOID
    918 EblShutdownExternalCmdTable (
    919   VOID
    920   )
    921 {
    922   gBS->UninstallProtocolInterface (gExternalCmdHandle, &gEfiEblAddCommandProtocolGuid,  &gEblAddCommand);
    923 }
    924 
    925 
    926