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>
6 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
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
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.
19 #include <Protocol/DiskIo.h>
20 #include <Protocol/BlockIo.h>
22 UINTN mCmdTableMaxIndex
= EBL_MAX_COMMAND_COUNT
;
23 UINTN mCmdTableNextFreeIndex
= 0;
24 EBL_COMMAND_TABLE
*mCmdTable
[EBL_MAX_COMMAND_COUNT
];
27 Converts a lowercase Ascii character to upper one
29 If Chr is lowercase Ascii character, then converts it to upper one.
31 If Value >= 0xA0, then ASSERT().
32 If (Value & 0x0F) >= 0x0A, then ASSERT().
34 @param chr one Ascii character
36 @return The uppercase value of Ascii character
45 return (UINT8
) ((Chr
>= 'a' && Chr
<= 'z') ? Chr
- ('a' - 'A') : Chr
);
50 Case insensitive comparison of two Null-terminated Unicode strings with maximum
51 lengths, and returns the difference between the first mismatched Unicode
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.
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.
64 @retval 0 FirstString is identical to SecondString using case insensitive
66 @retval !=0 FirstString is not identical to SecondString using case
67 insensitive comparisons.
73 IN CONST CHAR8
*FirstString
,
74 IN CONST CHAR8
*SecondString
,
82 while ((AsciiToUpper (*FirstString
) != '\0') &&
83 (AsciiToUpper (*FirstString
) == AsciiToUpper (*SecondString
)) &&
90 return AsciiToUpper (*FirstString
) - AsciiToUpper (*SecondString
);
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.
100 @param Entry Command Entry to add to the CmdTable
106 IN
const EBL_COMMAND_TABLE
*Entry
111 if (mCmdTableNextFreeIndex
== EBL_MAX_COMMAND_COUNT
) {
113 // Ran out of space to store commands. Increase EBL_MAX_COMMAND_COUNT
120 // Add command and Insertion sort array in the process
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) {
129 mCmdTable
[Count
] = mCmdTable
[Count
- 1];
131 mCmdTable
[Count
] = (EBL_COMMAND_TABLE
*)Entry
;
134 mCmdTableNextFreeIndex
++;
139 Add an set of commands to the command table. Most commonly used on static
142 @param EntryArray Pointer to array of command entries
143 @param ArrayCount Number of command entries to add
149 IN
const EBL_COMMAND_TABLE
*EntryArray
,
155 for (Index
= 0; Index
< ArrayCount
; Index
++) {
156 EblAddCommand (&EntryArray
[Index
]);
161 EBL_ADD_COMMAND_PROTOCOL gEblAddCommand
= {
165 EblAnyKeyToContinueQtoQuit
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.
175 @param CommandName Name of command to search for
177 @return NULL CommandName did not match or was not unique
178 Other Pointer to EBL_COMMAND_TABLE entry for CommandName
183 IN CHAR8
*CommandName
187 UINTN BestMatchCount
;
189 EBL_COMMAND_TABLE
*Match
;
192 Length
= AsciiStrLen (CommandName
);
193 Str
= AsciiStrStr (CommandName
, ".");
195 // If the command includes a trailing . command extension skip it for the match.
196 // Example: hexdump.4
197 Length
= (UINTN
)(Str
- CommandName
);
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
];
206 if (AsciiStrniCmp (CommandName
, mCmdTable
[Index
]->Name
, Length
) == 0) {
207 // partial match, so keep looking to make sure there is only one partial match
209 Match
= mCmdTable
[Index
];
213 if (BestMatchCount
== 1) {
218 // We had no matches or too many matches
235 for (Count
= 0; *Str
!= '\0'; Str
++) {
236 if (Str
[Count
] == '\n') {
246 List out help information on all the commands or print extended information
247 about a specific passed in command.
250 Argv[1] - Command to display help about
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
268 UINTN CurrentRow
= 0;
271 // Print all the commands
272 AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");
274 for (Index
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
275 EblSetTextColor (EFI_YELLOW
);
276 AsciiPrint (" %a", mCmdTable
[Index
]->Name
);
278 AsciiPrint ("%a\n", mCmdTable
[Index
]->HelpSummary
);
279 // Handle multi line help summaries
280 CurrentRow
+= CountNewLines (mCmdTable
[Index
]->HelpSummary
);
281 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
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
)) {
305 Exit the EBL. If the command processor sees EFI_ABORTED return status it will
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
326 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
328 UINTN DescriptorSize
;
329 UINT32 DescriptorVersion
;
333 if (AsciiStriCmp (Argv
[1], "efi") != 0) {
336 } else if (Argc
== 1) {
343 Status
= gBS
->GetMemoryMap (
350 if (Status
== EFI_BUFFER_TOO_SMALL
) {
352 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
353 MemoryMap
= AllocatePages (Pages
);
356 // Get System MemoryMap
358 Status
= gBS
->GetMemoryMap (
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
);
375 } while (EFI_ERROR (Status
));
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.
384 // Should never get here, but makes the compiler happy
390 Update the screen by decrementing the timeout value.
391 This AsciiPrint has to match the AsciiPrint in
394 @param ElaspedTime Current timeout value remaining
403 AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b \b\b%3d seconds", ElapsedTime
);
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
413 Argv[1] - timeout value is decimal seconds
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
419 @return EFI_SUCCESS Timeout expired with no input
420 @return EFI_TIMEOUT Stop processing other commands on the same command line
434 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
436 AsciiPrint ("Hit any key to break. You have %3d seconds", Delay
);
437 Status
= EblGetCharKey (&Key
, Delay
, EblPauseCallback
);
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
;;
447 On a debug build issue a software breakpoint to enter the debugger
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
471 Reset the system. If no Argument do a Cold reset. If argument use that reset type
473 (S)hutdown = Shutdown Reset
476 Argv[1] - warm or shutdown reset type
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
492 EFI_RESET_TYPE ResetType
;
494 ResetType
= EfiResetCold
;
499 ResetType
= EfiResetWarm
;
503 ResetType
= EfiResetShutdown
;
507 gRT
->ResetSystem (ResetType
, EFI_SUCCESS
, 0, NULL
);
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
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
535 gPageBreak
= (gPageBreak
) ? FALSE
: TRUE
;
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')) {
541 } else if ((Argv
[1][1] == 'f') || (Argv
[1][1] == 'F')) {
544 return EFI_INVALID_PARAMETER
;
560 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
562 gBS
->Stall (Delay
* 1000000);
572 if (Character
< ' ' || Character
> '~') {
591 Result
= (Result
<< 8) + *Address
++;
594 Result
= (Result
<< 8) + *Address
++;
599 CHAR8 mBlanks
[] = " ";
611 CHAR8 TextLine
[0x11];
612 UINTN CurrentRow
= 0;
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
;
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
++);
632 AsciiPrint ("%08x ", GetBytes(Address
, Bytes
));
640 AsciiPrint ("%04x ", *((UINT16
*)Address
));
641 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
642 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
644 AsciiPrint ("%04x ", GetBytes(Address
, Bytes
));
651 AsciiPrint ("%02x ", *((UINT8
*)Address
));
652 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
656 AsciiPrint ("Width must be 1, 2, or 4!\n");
657 return EFI_INVALID_PARAMETER
;
665 Spaces
= 9 * ((0x10 - Line
)/4);
668 Spaces
= 5 * ((0x10 - Line
)/2);
671 Spaces
= 3 * (0x10 - Line
);
675 Blanks
[Spaces
] = '\0';
679 Blanks
[Spaces
] = ' ';
683 AsciiPrint ("|%a|\n", TextLine
);
685 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
686 return EFI_END_OF_FILE
;
690 if (Length
% Width
!= 0) {
691 AsciiPrint ("%08x\n", Offset
);
699 See if command contains .# where # is a number. Return # as the Width
700 or 1 as the default Width for commands.
702 Example hexdump.4 returns a width of 4.
704 @param Argv Argv[0] is the command name
706 @return Width of command
710 WidthFromCommandName (
718 //Hexdump.2 HexDump.4 mean use a different width
719 Str
= AsciiStrStr (Argv
, ".");
721 Width
= AsciiStrDecimalToUintn (Str
+ 1);
733 #define HEXDUMP_CHUNK 1024
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
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
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
764 UINTN Chunk
= HEXDUMP_CHUNK
;
766 if ((Argc
< 2) || (Argc
> 4)) {
767 return EFI_INVALID_PARAMETER
;
770 Width
= WidthFromCommandName (Argv
[0], 1);
771 if ((Width
!= 1) && (Width
!= 2) && (Width
!= 4)) {
772 return EFI_INVALID_PARAMETER
;
775 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
777 return EFI_NOT_FOUND
;
780 Location
= AllocatePool (Chunk
);
781 Size
= (Argc
> 3) ? AsciiStrHexToUintn (Argv
[3]) : EfiTell (File
, NULL
);
785 Offset
= AsciiStrHexToUintn (Argv
[2]);
787 // Make sure size includes the part of the file we have skipped
792 Status
= EfiSeek (File
, Offset
, EfiSeekStart
);
793 if (EFI_ERROR (Status
)) {
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");
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
;
816 Chunk
= Size
- Offset
;
817 Status
= EfiRead (File
, Location
, &Chunk
);
818 if (EFI_ERROR(Status
)) {
819 AsciiPrint ("Error reading file content\n");
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
;
841 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdTemplate
[] =
845 " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
857 " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
863 "; Generate debugging breakpoint",
869 " [on|off]]; toggle promting on command output larger than screen",
875 " [sec]; Pause for sec[10] seconds. ",
881 " [sec]; Sleep for sec[10] seconds. ",
887 "[.{1|2|4}] filename [Offset] [Size]; dump a file as hex .width",
894 EFI_HANDLE gExternalCmdHandle
= NULL
;
897 Initialize the commands in this in this file
900 EblInitializeCmdTable (
905 EblAddCommands (mCmdTemplate
, sizeof (mCmdTemplate
)/sizeof (EBL_COMMAND_TABLE
));
907 gBS
->InstallProtocolInterface (
909 &gEfiEblAddCommandProtocolGuid
,
910 EFI_NATIVE_INTERFACE
,
918 EblShutdownExternalCmdTable (
922 gBS
->UninstallProtocolInterface (gExternalCmdHandle
, &gEfiEblAddCommandProtocolGuid
, &gEblAddCommand
);