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
AsciiToUpper(IN CHAR8 Chr)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
AsciiStrniCmp(IN CONST CHAR8 * FirstString,IN CONST CHAR8 * SecondString,IN UINTN Length)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
EblAddCommand(IN const EBL_COMMAND_TABLE * Entry)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
EblAddCommands(IN const EBL_COMMAND_TABLE * EntryArray,IN UINTN ArrayCount)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 *
EblGetCommand(IN CHAR8 * CommandName)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
CountNewLines(IN CHAR8 * Str)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
EblHelpCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblExitCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblPauseCallback(IN UINTN ElapsedTime)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
EblPauseCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblBreakPointCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblResetCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblPageCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblSleepCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
ConvertToTextLine(IN CHAR8 Character)568 ConvertToTextLine (
569   IN CHAR8  Character
570   )
571 {
572   if (Character < ' ' || Character > '~') {
573     return '.';
574   } else {
575     return Character;
576   }
577 }
578 
579 UINTN
GetBytes(IN UINT8 * Address,IN UINTN Bytes)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
OutputData(IN UINT8 * Address,IN UINTN Length,IN UINTN Width,IN UINTN Offset)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   AsciiStrCpy (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
WidthFromCommandName(IN CHAR8 * Argv,IN UINTN Default)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
EblHexdumpCmd(IN UINTN Argc,IN CHAR8 ** Argv)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
EblInitializeCmdTable(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
EblShutdownExternalCmdTable(VOID)918 EblShutdownExternalCmdTable (
919   VOID
920   )
921 {
922   gBS->UninstallProtocolInterface (gExternalCmdHandle, &gEfiEblAddCommandProtocolGuid,  &gEblAddCommand);
923 }
924 
925 
926