2 Basic commands and command processing infrastructure for EBL
4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
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
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.
18 #include <Protocol/DiskIo.h>
19 #include <Protocol/BlockIo.h>
21 UINTN mCmdTableMaxIndex
= EBL_MAX_COMMAND_COUNT
;
22 UINTN mCmdTableNextFreeIndex
= 0;
23 EBL_COMMAND_TABLE
*mCmdTable
[EBL_MAX_COMMAND_COUNT
];
26 Converts a lowercase Ascii character to upper one
28 If Chr is lowercase Ascii character, then converts it to upper one.
30 If Value >= 0xA0, then ASSERT().
31 If (Value & 0x0F) >= 0x0A, then ASSERT().
33 @param chr one Ascii character
35 @return The uppercase value of Ascii character
44 return (UINT8
) ((Chr
>= 'a' && Chr
<= 'z') ? Chr
- ('a' - 'A') : Chr
);
49 Case insensitive comparison of two Null-terminated Unicode strings with maximum
50 lengths, and returns the difference between the first mismatched Unicode
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.
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.
63 @retval 0 FirstString is identical to SecondString using case insensitive
65 @retval !=0 FirstString is not identical to SecondString using case
66 insensitive comparisons.
72 IN CONST CHAR8
*FirstString
,
73 IN CONST CHAR8
*SecondString
,
81 while ((AsciiToUpper (*FirstString
) != '\0') &&
82 (AsciiToUpper (*FirstString
) == AsciiToUpper (*SecondString
)) &&
89 return AsciiToUpper (*FirstString
) - AsciiToUpper (*SecondString
);
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.
99 @param Entry Command Entry to add to the CmdTable
105 IN
const EBL_COMMAND_TABLE
*Entry
110 if (mCmdTableNextFreeIndex
== EBL_MAX_COMMAND_COUNT
) {
112 // Ran out of space to store commands. Increase EBL_MAX_COMMAND_COUNT
119 // Add command and Insertion sort array in the process
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) {
128 mCmdTable
[Count
] = mCmdTable
[Count
- 1];
130 mCmdTable
[Count
] = (EBL_COMMAND_TABLE
*)Entry
;
133 mCmdTableNextFreeIndex
++;
138 Add an set of commands to the command table. Most commonly used on static
141 @param EntryArray Pointer to array of command entries
142 @param ArrayCount Number of command entries to add
148 IN
const EBL_COMMAND_TABLE
*EntryArray
,
154 for (Index
= 0; Index
< ArrayCount
; Index
++) {
155 EblAddCommand (&EntryArray
[Index
]);
160 EBL_ADD_COMMAND_PROTOCOL gEblAddCommand
= {
164 EblAnyKeyToContinueQtoQuit
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.
174 @param CommandName Name of command to search for
176 @return NULL CommandName did not match or was not unique
177 Other Pointer to EBL_COMMAND_TABLE entry for CommandName
182 IN CHAR8
*CommandName
186 UINTN BestMatchCount
;
188 EBL_COMMAND_TABLE
*Match
;
191 Length
= AsciiStrLen (CommandName
);
192 Str
= AsciiStrStr (CommandName
, ".");
194 // If the command includes a trailing . command extension skip it for the match.
195 // Example: hexdump.4
196 Length
= (UINTN
)(Str
- CommandName
);
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
];
205 if (AsciiStrniCmp (CommandName
, mCmdTable
[Index
]->Name
, Length
) == 0) {
206 // partial match, so keep looking to make sure there is only one partial match
208 Match
= mCmdTable
[Index
];
212 if (BestMatchCount
== 1) {
217 // We had no matches or too many matches
234 for (Count
= 0; *Str
!= '\0'; Str
++) {
235 if (Str
[Count
] == '\n') {
245 List out help information on all the commands or print extended information
246 about a specific passed in command.
249 Argv[1] - Command to display help about
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
266 UINTN CurrentRow
= 0;
269 // Print all the commands
270 AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");
272 for (Index
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
273 EblSetTextColor (EFI_YELLOW
);
274 AsciiPrint (" %a", mCmdTable
[Index
]->Name
);
276 AsciiPrint ("%a\n", mCmdTable
[Index
]->HelpSummary
);
277 // Handle multi line help summaries
278 CurrentRow
+= CountNewLines (mCmdTable
[Index
]->HelpSummary
);
279 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
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
)) {
303 Exit the EBL. If the command processor sees EFI_ABORTED return status it will
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
323 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
325 UINTN DescriptorSize
;
326 UINT32 DescriptorVersion
;
330 if (AsciiStriCmp (Argv
[1], "efi") != 0) {
333 } else if (Argc
== 1) {
340 Status
= gBS
->GetMemoryMap (
347 if (Status
== EFI_BUFFER_TOO_SMALL
) {
349 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
350 MemoryMap
= AllocatePages (Pages
);
353 // Get System MemoryMap
355 Status
= gBS
->GetMemoryMap (
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
);
372 } while (EFI_ERROR (Status
));
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.
381 // Should never get here, but makes the compiler happy
387 Update the screen by decrementing the timeout value.
388 This AsciiPrint has to match the AsciiPrint in
391 @param ElaspedTime Current timeout value remaining
400 AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b \b\b%3d seconds", ElapsedTime
);
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
410 Argv[1] - timeout value is decimal seconds
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
416 @return EFI_SUCCESS Timeout expired with no input
417 @return EFI_TIMEOUT Stop processing other commands on the same command line
430 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
432 AsciiPrint ("Hit any key to break. You have %3d seconds", Delay
);
433 Status
= EblGetCharKey (&Key
, Delay
, EblPauseCallback
);
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
;;
443 On a debug build issue a software breakpoint to enter the debugger
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
466 Reset the system. If no Argument do a Cold reset. If argument use that reset type
468 (S)hutdown = Shutdown Reset
471 Argv[1] - warm or shutdown reset type
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
486 EFI_RESET_TYPE ResetType
;
488 ResetType
= EfiResetCold
;
493 ResetType
= EfiResetWarm
;
497 ResetType
= EfiResetShutdown
;
501 gRT
->ResetSystem (ResetType
, EFI_SUCCESS
, 0, NULL
);
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
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
528 gPageBreak
= (gPageBreak
) ? FALSE
: TRUE
;
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')) {
534 } else if ((Argv
[1][1] == 'f') || (Argv
[1][1] == 'F')) {
537 return EFI_INVALID_PARAMETER
;
552 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
554 gBS
->Stall (Delay
* 1000000);
564 if (Character
< ' ' || Character
> '~') {
583 Result
= (Result
<< 8) + *Address
++;
586 Result
= (Result
<< 8) + *Address
++;
591 CHAR8 mBlanks
[] = " ";
603 CHAR8 TextLine
[0x11];
604 UINTN CurrentRow
= 0;
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
;
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
++);
624 AsciiPrint ("%08x ", GetBytes(Address
, Bytes
));
632 AsciiPrint ("%04x ", *((UINT16
*)Address
));
633 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
634 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
636 AsciiPrint ("%04x ", GetBytes(Address
, Bytes
));
643 AsciiPrint ("%02x ", *((UINT8
*)Address
));
644 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
648 AsciiPrint ("Width must be 1, 2, or 4!\n");
649 return EFI_INVALID_PARAMETER
;
657 Spaces
= 9 * ((0x10 - Line
)/4);
660 Spaces
= 5 * ((0x10 - Line
)/2);
663 Spaces
= 3 * (0x10 - Line
);
667 Blanks
[Spaces
] = '\0';
671 Blanks
[Spaces
] = ' ';
675 AsciiPrint ("|%a|\n", TextLine
);
677 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
678 return EFI_END_OF_FILE
;
682 if (Length
% Width
!= 0) {
683 AsciiPrint ("%08x\n", Offset
);
691 See if command contains .# where # is a number. Return # as the Width
692 or 1 as the default Width for commands.
694 Example hexdump.4 returns a width of 4.
696 @param Argv Argv[0] is the command name
698 @return Width of command
702 WidthFromCommandName (
710 //Hexdump.2 HexDump.4 mean use a different width
711 Str
= AsciiStrStr (Argv
, ".");
713 Width
= AsciiStrDecimalToUintn (Str
+ 1);
725 #define HEXDUMP_CHUNK 1024
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
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
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
755 UINTN Chunk
= HEXDUMP_CHUNK
;
757 if ((Argc
< 2) || (Argc
> 4)) {
758 return EFI_INVALID_PARAMETER
;
761 Width
= WidthFromCommandName (Argv
[0], 1);
762 if ((Width
!= 1) && (Width
!= 2) && (Width
!= 4)) {
763 return EFI_INVALID_PARAMETER
;
766 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
768 return EFI_NOT_FOUND
;
771 Location
= AllocatePool (Chunk
);
772 Size
= (Argc
> 3) ? AsciiStrHexToUintn (Argv
[3]) : EfiTell (File
, NULL
);
776 Offset
= AsciiStrHexToUintn (Argv
[2]);
778 // Make sure size includes the part of the file we have skipped
783 Status
= EfiSeek (File
, Offset
, EfiSeekStart
);
784 if (EFI_ERROR (Status
)) {
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");
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
;
807 Chunk
= Size
- Offset
;
808 Status
= EfiRead (File
, Location
, &Chunk
);
809 if (EFI_ERROR(Status
)) {
810 AsciiPrint ("Error reading file content\n");
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
;
832 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdTemplate
[] =
836 " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
848 " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
854 "; Generate debugging breakpoint",
860 " [on|off]]; toggle promting on command output larger than screen",
866 " [sec]; Pause for sec[10] seconds. ",
872 " [sec]; Sleep for sec[10] seconds. ",
878 "[.{1|2|4}] filename [Offset] [Size]; dump a file as hex .width",
885 EFI_HANDLE gExternalCmdHandle
= NULL
;
888 Initialize the commands in this in this file
891 EblInitializeCmdTable (
896 EblAddCommands (mCmdTemplate
, sizeof (mCmdTemplate
)/sizeof (EBL_COMMAND_TABLE
));
898 gBS
->InstallProtocolInterface (
900 &gEfiEblAddCommandProtocolGuid
,
901 EFI_NATIVE_INTERFACE
,
909 EblShutdownExternalCmdTable (
913 gBS
->UninstallProtocolInterface (gExternalCmdHandle
, &gEfiEblAddCommandProtocolGuid
, &gEblAddCommand
);