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