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