2 Basic commands and command processing infrastructure for EBL
4 Copyright (c) 2007, Intel Corporation<BR>
5 Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.
7 All rights reserved. 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 insensitve 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 possition.
99 @param Entry Commnad 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 commnad 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 unqiue. This enables commands
172 to be shortend 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
225 List out help information on all the commands or print extended information
226 about a specific passed in command.
229 Argv[1] - Command to display help about
231 @param Argc Number of command arguments in Argv
232 @param Argv Array of strings that represent the parsed command line.
233 Argv[0] is the comamnd name
249 // Print all the commands
250 AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");
251 for (Index
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
252 EblSetTextColor (EFI_YELLOW
);
253 AsciiPrint (" %a", mCmdTable
[Index
]->Name
);
255 AsciiPrint ("%a\n", mCmdTable
[Index
]->HelpSummary
);
257 } else if (Argv
[1] != NULL
) {
258 // Print specific help
259 for (Index
= 0, CurrentRow
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
260 if (AsciiStriCmp (Argv
[1], mCmdTable
[Index
]->Name
) == 0) {
261 Ptr
= (mCmdTable
[Index
]->Help
== NULL
) ? mCmdTable
[Index
]->HelpSummary
: mCmdTable
[Index
]->Help
;
262 AsciiPrint ("%a%a\n", Argv
[1], Ptr
);
263 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
275 Exit the EBL. If the commnad processor sees EFI_ABORTED return status it will
280 @param Argc Number of command arguments in Argv
281 @param Argv Array of strings that represent the parsed command line.
282 Argv[0] is the comamnd name
295 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
297 UINTN DescriptorSize
;
298 UINT32 DescriptorVersion
;
302 if (AsciiStriCmp (Argv
[1], "efi") != 0) {
305 } else if (Argc
== 1) {
312 Status
= gBS
->GetMemoryMap (
319 if (Status
== EFI_BUFFER_TOO_SMALL
) {
321 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
322 MemoryMap
= AllocatePages (Pages
);
325 // Get System MemoryMap
327 Status
= gBS
->GetMemoryMap (
334 // Don't do anything between the GetMemoryMap() and ExitBootServices()
335 if (!EFI_ERROR (Status
)) {
336 Status
= gBS
->ExitBootServices (gImageHandle
, MapKey
);
337 if (EFI_ERROR (Status
)) {
338 FreePages (MemoryMap
, Pages
);
344 } while (EFI_ERROR (Status
));
347 // At this point it is very dangerous to do things EFI as most of EFI is now gone.
348 // This command is useful if you are working with a debugger as it will shutdown
349 // DMA and other things that could break a soft resets.
353 // Should never get here, but makes the compiler happy
359 Update the screen by decrementing the timeout value.
360 This AsciiPrint has to match the AsciiPrint in
363 @param ElaspedTime Current timout value remaining
372 AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b \b\b%3d seconds", ElapsedTime
);
376 Pause until a key is pressed and abort the remaining commands on the command
377 line. If no key is pressed continue processing the command line. This command
378 allows the user to stop an operation from happening and return control to the
382 Argv[1] - timeout value is decimal seconds
384 @param Argc Number of command arguments in Argv
385 @param Argv Array of strings that represent the parsed command line.
386 Argv[0] is the comamnd name
388 @return EFI_SUCCESS Timeout expired with no input
389 @return EFI_TIMEOUT Stop procesing other commands on the same command line
402 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
404 AsciiPrint ("Hit any key to break. You have %3d seconds", Delay
);
405 Status
= EblGetCharKey (&Key
, Delay
, EblPauseCallback
);
408 // If we timeout then the pause succeded thus return success
409 // If we get a key return timout to stop other commnad on this cmd line
410 return (Status
== EFI_SUCCESS
) ? EFI_TIMEOUT
: EFI_SUCCESS
;;
415 On a debug build issue a software breakpoint to enter the debugger
419 @param Argc Number of command arguments in Argv
420 @param Argv Array of strings that represent the parsed command line.
421 Argv[0] is the comamnd name
438 Reset the system. If no Argument do a Cold reset. If argument use that reset type
440 (S)hutdown = Shutdown Reset
443 Argv[1] - warm or shutdown reset type
445 @param Argc Number of command arguments in Argv
446 @param Argv Array of strings that represent the parsed command line.
447 Argv[0] is the comamnd name
458 EFI_RESET_TYPE ResetType
;
460 ResetType
= EfiResetCold
;
465 ResetType
= EfiResetWarm
;
469 ResetType
= EfiResetShutdown
;
473 gRT
->ResetSystem (ResetType
, EFI_SUCCESS
, 0, NULL
);
479 Toggle page break global. This turns on and off prompting to Quit or hit any
480 key to continue when a command is about to scroll the screen with its output
485 @param Argc Number of command arguments in Argv
486 @param Argv Array of strings that represent the parsed command line.
487 Argv[0] is the comamnd name
500 gPageBreak
= (gPageBreak
) ? FALSE
: TRUE
;
502 // use argv to set the value
503 if ((Argv
[1][0] == 'o') || (Argv
[1][0] == 'O')) {
504 if ((Argv
[1][1] == 'n') || (Argv
[1][1] == 'N')) {
506 } else if ((Argv
[1][1] == 'f') || (Argv
[1][1] == 'F')) {
509 return EFI_INVALID_PARAMETER
;
524 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
526 gBS
->Stall (Delay
* 1000000);
536 if (Character
< ' ' || Character
> '~') {
555 Result
= (Result
<< 8) + *Address
++;
558 Result
= (Result
<< 8) + *Address
++;
563 CHAR8 mBlanks
[] = " ";
575 CHAR8 TextLine
[0x11];
576 UINTN CurrentRow
= 0;
581 AsciiStrCpy (Blanks
, mBlanks
);
582 for (EndAddress
= Address
+ Length
; Address
< EndAddress
; Offset
+= Line
) {
583 AsciiPrint ("%08x: ", Offset
);
584 for (Line
= 0; (Line
< 0x10) && (Address
< EndAddress
);) {
585 Bytes
= EndAddress
- Address
;
590 AsciiPrint ("%08x ", *((UINT32
*)Address
));
591 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
592 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
593 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
594 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
596 AsciiPrint ("%08x ", GetBytes(Address
, Bytes
));
604 AsciiPrint ("%04x ", *((UINT16
*)Address
));
605 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
606 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
608 AsciiPrint ("%04x ", GetBytes(Address
, Bytes
));
615 AsciiPrint ("%02x ", *((UINT8
*)Address
));
616 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
620 AsciiPrint ("Width must be 1, 2, or 4!\n");
621 return EFI_INVALID_PARAMETER
;
629 Spaces
= 9 * ((0x10 - Line
)/4);
632 Spaces
= 5 * ((0x10 - Line
)/2);
635 Spaces
= 3 * (0x10 - Line
);
639 Blanks
[Spaces
] = '\0';
643 Blanks
[Spaces
] = ' ';
647 AsciiPrint ("|%a|\n", TextLine
);
649 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
650 return EFI_END_OF_FILE
;
654 if (Length
% Width
!= 0) {
655 AsciiPrint ("%08x\n", Offset
);
663 See if command contains .# where # is a number. Return # as the Width
664 or 1 as the default Width for commands.
666 Example hexdump.4 returns a width of 4.
668 @param Argv Argv[0] is the comamnd name
670 @return Width of command
674 WidthFromCommandName (
682 //Hexdump.2 HexDump.4 mean use a different width
683 Str
= AsciiStrStr (Argv
, ".");
685 Width
= AsciiStrDecimalToUintn (Str
+ 1);
697 #define HEXDUMP_CHUNK 1024
700 Toggle page break global. This turns on and off prompting to Quit or hit any
701 key to continue when a command is about to scroll the screen with its output
703 Argv[0] - "hexdump"[.#] # is optional 1,2, or 4 for width
704 Argv[1] - Device or File to dump.
705 Argv[2] - Optional offset to start dumping
706 Argv[3] - Optional number of bytes to dump
708 @param Argc Number of command arguments in Argv
709 @param Argv Array of strings that represent the parsed command line.
710 Argv[0] is the comamnd name
727 UINTN Chunk
= HEXDUMP_CHUNK
;
729 if ((Argc
< 2) || (Argc
> 4)) {
730 return EFI_INVALID_PARAMETER
;
733 Width
= WidthFromCommandName (Argv
[0], 1);
734 if ((Width
!= 1) && (Width
!= 2) && (Width
!= 4)) {
735 return EFI_INVALID_PARAMETER
;
738 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
740 return EFI_NOT_FOUND
;
743 Location
= AllocatePool (Chunk
);
744 Size
= (Argc
> 3) ? AsciiStrHexToUintn (Argv
[3]) : EfiTell (File
, NULL
);
748 Offset
= AsciiStrHexToUintn (Argv
[2]);
750 // Make sure size includes the part of the file we have skipped
755 Status
= EfiSeek (File
, Offset
, EfiSeekStart
);
756 if (EFI_ERROR (Status
)) {
760 for (; Offset
+ HEXDUMP_CHUNK
<= Size
; Offset
+= Chunk
) {
761 Chunk
= HEXDUMP_CHUNK
;
762 Status
= EfiRead (File
, Location
, &Chunk
);
763 if (EFI_ERROR(Status
)) {
764 AsciiPrint ("Error reading file content\n");
768 Status
= OutputData (Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
769 if (EFI_ERROR(Status
)) {
770 if (Status
== EFI_END_OF_FILE
) {
771 Status
= EFI_SUCCESS
;
779 Chunk
= Size
- Offset
;
780 Status
= EfiRead (File
, Location
, &Chunk
);
781 if (EFI_ERROR(Status
)) {
782 AsciiPrint ("Error reading file content\n");
786 Status
= OutputData (Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
787 if (EFI_ERROR(Status
)) {
788 if (Status
== EFI_END_OF_FILE
) {
789 Status
= EFI_SUCCESS
;
804 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdTemplate
[] =
808 " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
820 " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
826 "; Generate debugging breakpoint",
832 " [on|off]]; toggle promting on command output larger than screen",
838 " [sec]; Pause for sec[10] seconds. ",
844 " [sec]; Sleep for sec[10] seconds. ",
850 "[.{1|2|4}] filename [Offset] [Size]; dump a file as hex bytes at a given width",
857 EFI_HANDLE gExternalCmdHandle
= NULL
;
860 Initialize the commands in this in this file
863 EblInitializeCmdTable (
868 EblAddCommands (mCmdTemplate
, sizeof (mCmdTemplate
)/sizeof (EBL_COMMAND_TABLE
));
870 gBS
->InstallProtocolInterface (
872 &gEfiEblAddCommandProtocolGuid
,
873 EFI_NATIVE_INTERFACE
,
881 EblShutdownExternalCmdTable (
885 gBS
->UninstallProtocolInterface (gExternalCmdHandle
, &gEfiEblAddCommandProtocolGuid
, &gEblAddCommand
);