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
;
190 Length
= AsciiStrLen (CommandName
);
191 for (Index
= 0, BestMatchCount
= 0, Match
= NULL
; Index
< mCmdTableNextFreeIndex
; Index
++) {
192 if (AsciiStriCmp (mCmdTable
[Index
]->Name
, CommandName
) == 0) {
193 // match a command exactly
194 return mCmdTable
[Index
];
197 if (AsciiStrniCmp (CommandName
, mCmdTable
[Index
]->Name
, Length
) == 0) {
198 // partial match, so keep looking to make sure there is only one partial match
200 Match
= mCmdTable
[Index
];
204 if (BestMatchCount
== 1) {
209 // We had no matches or too many matches
217 List out help information on all the commands or print extended information
218 about a specific passed in command.
221 Argv[1] - Command to display help about
223 @param Argc Number of command arguments in Argv
224 @param Argv Array of strings that represent the parsed command line.
225 Argv[0] is the comamnd name
241 // Print all the commands
242 AsciiPrint ("Embedded Boot Loader (EBL) commands (help command for more info):\n");
243 for (Index
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
244 EblSetTextColor (EFI_YELLOW
);
245 AsciiPrint (" %a", mCmdTable
[Index
]->Name
);
247 AsciiPrint ("%a\n", mCmdTable
[Index
]->HelpSummary
);
249 } else if (Argv
[1] != NULL
) {
250 // Print specific help
251 for (Index
= 0, CurrentRow
= 0; Index
< mCmdTableNextFreeIndex
; Index
++) {
252 if (AsciiStriCmp (Argv
[1], mCmdTable
[Index
]->Name
) == 0) {
253 Ptr
= (mCmdTable
[Index
]->Help
== NULL
) ? mCmdTable
[Index
]->HelpSummary
: mCmdTable
[Index
]->Help
;
254 AsciiPrint ("%a%a\n", Argv
[1], Ptr
);
255 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
267 Exit the EBL. If the commnad processor sees EFI_ABORTED return status it will
272 @param Argc Number of command arguments in Argv
273 @param Argv Array of strings that represent the parsed command line.
274 Argv[0] is the comamnd name
287 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
289 UINTN DescriptorSize
;
290 UINT32 DescriptorVersion
;
294 if (AsciiStriCmp (Argv
[1], "efi") != 0) {
297 } else if (Argc
== 1) {
304 Status
= gBS
->GetMemoryMap (
311 if (Status
== EFI_BUFFER_TOO_SMALL
) {
313 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
314 MemoryMap
= AllocatePages (Pages
);
317 // Get System MemoryMap
319 Status
= gBS
->GetMemoryMap (
326 // Don't do anything between the GetMemoryMap() and ExitBootServices()
327 if (!EFI_ERROR (Status
)) {
328 Status
= gBS
->ExitBootServices (gImageHandle
, MapKey
);
329 if (EFI_ERROR (Status
)) {
330 FreePages (MemoryMap
, Pages
);
336 } while (EFI_ERROR (Status
));
339 // At this point it is very dangerous to do things EFI as most of EFI is now gone.
340 // This command is useful if you are working with a debugger as it will shutdown
341 // DMA and other things that could break a soft resets.
345 // Should never get here, but makes the compiler happy
351 Update the screen by decrementing the timeout value.
352 This AsciiPrint has to match the AsciiPrint in
355 @param ElaspedTime Current timout value remaining
364 AsciiPrint ("\b\b\b\b\b\b\b\b\b\b\b\b \b\b%3d seconds", ElapsedTime
);
368 Pause until a key is pressed and abort the remaining commands on the command
369 line. If no key is pressed continue processing the command line. This command
370 allows the user to stop an operation from happening and return control to the
374 Argv[1] - timeout value is decimal seconds
376 @param Argc Number of command arguments in Argv
377 @param Argv Array of strings that represent the parsed command line.
378 Argv[0] is the comamnd name
380 @return EFI_SUCCESS Timeout expired with no input
381 @return EFI_TIMEOUT Stop procesing other commands on the same command line
394 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
396 AsciiPrint ("Hit any key to break. You have %3d seconds", Delay
);
397 Status
= EblGetCharKey (&Key
, Delay
, EblPauseCallback
);
400 // If we timeout then the pause succeded thus return success
401 // If we get a key return timout to stop other commnad on this cmd line
402 return (Status
== EFI_SUCCESS
) ? EFI_TIMEOUT
: EFI_SUCCESS
;;
407 On a debug build issue a software breakpoint to enter the debugger
411 @param Argc Number of command arguments in Argv
412 @param Argv Array of strings that represent the parsed command line.
413 Argv[0] is the comamnd name
430 Reset the system. If no Argument do a Cold reset. If argument use that reset type
432 (S)hutdown = Shutdown Reset
435 Argv[1] - warm or shutdown reset type
437 @param Argc Number of command arguments in Argv
438 @param Argv Array of strings that represent the parsed command line.
439 Argv[0] is the comamnd name
450 EFI_RESET_TYPE ResetType
;
452 ResetType
= EfiResetCold
;
457 ResetType
= EfiResetWarm
;
461 ResetType
= EfiResetShutdown
;
465 gRT
->ResetSystem (ResetType
, EFI_SUCCESS
, 0, NULL
);
471 Toggle page break global. This turns on and off prompting to Quit or hit any
472 key to continue when a command is about to scroll the screen with its output
477 @param Argc Number of command arguments in Argv
478 @param Argv Array of strings that represent the parsed command line.
479 Argv[0] is the comamnd name
492 gPageBreak
= (gPageBreak
) ? FALSE
: TRUE
;
494 // use argv to set the value
495 if ((Argv
[1][0] == 'o') || (Argv
[1][0] == 'O')) {
496 if ((Argv
[1][1] == 'n') || (Argv
[1][1] == 'N')) {
498 } else if ((Argv
[1][1] == 'f') || (Argv
[1][1] == 'F')) {
501 return EFI_INVALID_PARAMETER
;
516 Delay
= (Argc
== 1)? 10 : AsciiStrDecimalToUintn (Argv
[1]);
518 gBS
->Stall (Delay
* 1000000);
528 if (Character
< ' ' || Character
> '~') {
547 Result
= (Result
<< 8) + *Address
++;
550 Result
= (Result
<< 8) + *Address
++;
555 CHAR8 mBlanks
[] = " ";
567 CHAR8 TextLine
[0x11];
568 UINTN CurrentRow
= 0;
573 AsciiStrCpy (Blanks
, mBlanks
);
574 for (EndAddress
= Address
+ Length
; Address
< EndAddress
; Offset
+= Line
) {
575 AsciiPrint ("%08x: ", Offset
);
576 for (Line
= 0; (Line
< 0x10) && (Address
< EndAddress
);) {
577 Bytes
= EndAddress
- Address
;
582 AsciiPrint ("%08x ", *((UINT32
*)Address
));
583 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
584 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
585 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
586 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
588 AsciiPrint ("%08x ", GetBytes(Address
, Bytes
));
596 AsciiPrint ("%04x ", *((UINT16
*)Address
));
597 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
598 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
600 AsciiPrint ("%04x ", GetBytes(Address
, Bytes
));
607 AsciiPrint ("%02x ", *((UINT8
*)Address
));
608 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
612 AsciiPrint ("Width must be 1, 2, or 4!\n");
613 return EFI_INVALID_PARAMETER
;
621 Spaces
= 9 * ((0x10 - Line
)/4);
624 Spaces
= 5 * ((0x10 - Line
)/2);
627 Spaces
= 3 * (0x10 - Line
);
631 Blanks
[Spaces
] = '\0';
635 Blanks
[Spaces
] = ' ';
639 AsciiPrint ("|%a|\n", TextLine
);
641 if (EblAnyKeyToContinueQtoQuit (&CurrentRow
, FALSE
)) {
642 return EFI_END_OF_FILE
;
646 if (Length
% Width
!= 0) {
647 AsciiPrint ("%08x\n", Offset
);
653 #define HEXDUMP_CHUNK 1024
667 UINTN Chunk
= HEXDUMP_CHUNK
;
669 if ((Argc
< 2) || (Argc
> 3)) {
670 return EFI_INVALID_PARAMETER
;
674 Width
= AsciiStrDecimalToUintn(Argv
[2]);
677 if ((Width
!= 1) && (Width
!= 2) && (Width
!= 4)) {
678 return EFI_INVALID_PARAMETER
;
681 File
= EfiOpen (Argv
[1], EFI_FILE_MODE_READ
, 0);
683 return EFI_NOT_FOUND
;
686 Location
= AllocatePool (Chunk
);
687 Size
= EfiTell(File
, NULL
);
689 for (Offset
= 0; Offset
+ HEXDUMP_CHUNK
<= Size
; Offset
+= Chunk
) {
690 Chunk
= HEXDUMP_CHUNK
;
692 Status
= EfiRead (File
, Location
, &Chunk
);
693 if (EFI_ERROR(Status
))
695 AsciiPrint ("Error reading file content\n");
699 Status
= OutputData (Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
700 if (EFI_ERROR(Status
)) {
701 if (Status
== EFI_END_OF_FILE
) {
702 Status
= EFI_SUCCESS
;
710 Chunk
= Size
- Offset
;
711 Status
= EfiRead (File
, Location
, &Chunk
);
712 if (EFI_ERROR(Status
)) {
713 AsciiPrint ("Error reading file content\n");
717 Status
= OutputData (Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
718 if (EFI_ERROR(Status
)) {
719 if (Status
== EFI_END_OF_FILE
) {
720 Status
= EFI_SUCCESS
;
735 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdTemplate
[] =
739 " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
751 " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
757 "; Generate debugging breakpoint",
763 " [on|off]]; toggle promting on command output larger than screen",
769 " [sec]; Pause for sec[10] seconds. ",
775 " [sec]; Sleep for sec[10] seconds. ",
781 " filename ; dump a file as hex bytes",
788 EFI_HANDLE gExternalCmdHandle
= NULL
;
791 Initialize the commands in this in this file
794 EblInitializeCmdTable (
799 EblAddCommands (mCmdTemplate
, sizeof (mCmdTemplate
)/sizeof (EBL_COMMAND_TABLE
));
801 gBS
->InstallProtocolInterface (
803 &gEfiEblAddCommandProtocolGuid
,
804 EFI_NATIVE_INTERFACE
,
812 EblShutdownExternalCmdTable (
816 gBS
->UninstallProtocolInterface (gExternalCmdHandle
, &gEfiEblAddCommandProtocolGuid
, &gEblAddCommand
);