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 UINTN 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
> '~')
550 Result
= (Result
<< 8) + *Address
++;
553 Result
= (Result
<< 8) + *Address
++;
558 CHAR8 mBlanks
[] = " ";
570 CHAR8 TextLine
[0x11];
571 UINTN CurrentRow
= 0;
576 AsciiStrCpy (Blanks
, mBlanks
);
577 for (EndAddress
= Address
+ Length
; Address
< EndAddress
; Offset
+= Line
)
579 AsciiPrint ("%08x: ", Offset
);
580 for (Line
= 0; (Line
< 0x10) && (Address
< EndAddress
);)
582 Bytes
= EndAddress
- Address
;
589 AsciiPrint ("%08x ", *((UINT32
*)Address
));
590 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
591 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
592 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
593 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
597 AsciiPrint ("%08x ", GetBytes(Address
, Bytes
));
606 AsciiPrint ("%04x ", *((UINT16
*)Address
));
607 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
608 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
612 AsciiPrint ("%04x ", GetBytes(Address
, Bytes
));
619 AsciiPrint ("%02x ", *((UINT8
*)Address
));
620 TextLine
[Line
++] = ConvertToTextLine(*Address
++);
624 AsciiPrint ("Width must be 1, 2, or 4!\n");
625 return EFI_INVALID_PARAMETER
;
635 Spaces
= 9 * ((0x10 - Line
)/4);
638 Spaces
= 5 * ((0x10 - Line
)/2);
641 Spaces
= 3 * (0x10 - Line
);
645 Blanks
[Spaces
] = '\0';
649 Blanks
[Spaces
] = ' ';
653 AsciiPrint ("|%a|\n", TextLine
);
655 if (EblAnyKeyToContinueQtoQuit(&CurrentRow
, FALSE
))
657 return EFI_END_OF_FILE
;
661 if (Length
% Width
!= 0)
663 AsciiPrint ("%08x\n", Offset
);
669 #define HEXDUMP_CHUNK 1024
683 UINTN Chunk
= HEXDUMP_CHUNK
;
685 if ((Argc
< 2) || (Argc
> 3))
687 return EFI_INVALID_PARAMETER
;
692 Width
= AsciiStrDecimalToUintn(Argv
[2]);
695 if ((Width
!= 1) && (Width
!= 2) && (Width
!= 4))
697 return EFI_INVALID_PARAMETER
;
700 File
= EfiOpen(Argv
[1], EFI_FILE_MODE_READ
, 0);
703 return EFI_NOT_FOUND
;
706 Location
= AllocatePool(Chunk
);
707 Size
= EfiTell(File
, NULL
);
709 for (Offset
= 0; Offset
+ HEXDUMP_CHUNK
<= Size
; Offset
+= Chunk
)
711 Chunk
= HEXDUMP_CHUNK
;
713 Status
= EfiRead(File
, Location
, &Chunk
);
714 if (EFI_ERROR(Status
))
716 AsciiPrint ("Error reading file content\n");
720 Status
= OutputData(Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
721 if (EFI_ERROR(Status
))
723 if (Status
== EFI_END_OF_FILE
) {
724 Status
= EFI_SUCCESS
;
733 Chunk
= Size
- Offset
;
734 Status
= EfiRead(File
, Location
, &Chunk
);
735 if (EFI_ERROR(Status
))
737 AsciiPrint ("Error reading file content\n");
741 Status
= OutputData(Location
, Chunk
, Width
, File
->BaseOffset
+ Offset
);
742 if (EFI_ERROR(Status
))
744 if (Status
== EFI_END_OF_FILE
) {
745 Status
= EFI_SUCCESS
;
774 CHAR8 TextLine
[0x11];
776 EFI_DISK_IO_PROTOCOL
*DiskIo
;
778 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
782 if (AsciiStrCmp(Argv
[1], "r") == 0)
784 Offset
= AsciiStrHexToUintn(Argv
[2]);
785 Length
= AsciiStrHexToUintn(Argv
[3]);
788 Status
= gBS
->LocateProtocol(&gEfiDiskIoProtocolGuid
, NULL
, (VOID
**)&DiskIo
);
789 if (EFI_ERROR(Status
))
791 AsciiPrint("Did not locate DiskIO\n");
795 Buffer
= AllocatePool(Length
);
796 BufferOffset
= Buffer
;
798 Status
= DiskIo
->ReadDisk(DiskIo
, SIGNATURE_32('f','l','s','h'), Offset
, Length
, Buffer
);
799 if (EFI_ERROR(Status
))
801 AsciiPrint("DiskIO read failed\n");
802 gBS
->FreePool(Buffer
);
806 Status
= gBS
->LocateProtocol(&gEfiBlockIoProtocolGuid
, NULL
, (VOID
**)&BlockIo
);
807 if (EFI_ERROR(Status
))
809 AsciiPrint("Did not locate BlockIo\n");
813 Length
= BlockIo
->Media
->BlockSize
;
814 Buffer
= AllocatePool(Length
);
815 BufferOffset
= Buffer
;
816 Lba
= Offset
/BlockIo
->Media
->BlockSize
;
818 Status
= BlockIo
->ReadBlocks(BlockIo
, BlockIo
->Media
->MediaId
, Lba
, Length
, Buffer
);
819 if (EFI_ERROR(Status
))
821 AsciiPrint("BlockIo read failed\n");
822 gBS
->FreePool(Buffer
);
826 // Whack offset to what we actually read from
827 Offset
= Lba
* BlockIo
->Media
->BlockSize
;
832 for (EndOffset
= BufferOffset
+ Length
; BufferOffset
< EndOffset
; Offset
+= 0x10)
834 AsciiPrint ("%08x: ", Offset
);
836 for (Line
= 0; Line
< 0x10; Line
++)
838 AsciiPrint ("%02x ", *BufferOffset
);
840 if (*BufferOffset
< ' ' || *BufferOffset
> '~')
841 TextLine
[Line
] = '.';
843 TextLine
[Line
] = *BufferOffset
;
848 TextLine
[Line
] = '\0';
849 AsciiPrint ("|%a|\n", TextLine
);
852 gBS
->FreePool(Buffer
);
856 else if (AsciiStrCmp(Argv
[1], "w") == 0)
858 Offset
= AsciiStrHexToUintn(Argv
[2]);
859 Length
= AsciiStrHexToUintn(Argv
[3]);
860 Buffer
= (UINT8
*)AsciiStrHexToUintn(Argv
[4]);
863 Status
= gBS
->LocateProtocol(&gEfiDiskIoProtocolGuid
, NULL
, (VOID
**)&DiskIo
);
864 if (EFI_ERROR(Status
))
866 AsciiPrint("Did not locate DiskIO\n");
870 Status
= DiskIo
->WriteDisk(DiskIo
, SIGNATURE_32('f','l','s','h'), Offset
, Length
, Buffer
);
871 if (EFI_ERROR(Status
))
873 AsciiPrint("DiskIO write failed\n");
884 return EFI_INVALID_PARAMETER
;
888 GLOBAL_REMOVE_IF_UNREFERENCED
const EBL_COMMAND_TABLE mCmdTemplate
[] =
892 " [type]; Reset system. type = [warm] [shutdown] default is cold reset",
904 " [cmd]; Help on cmd or a list of all commands if cmd is ommited",
910 "; Generate debugging breakpoint",
916 " [on|off]]; toggle promting on command output larger than screen",
922 " [sec]; Pause for sec[10] seconds. ",
928 " [sec]; Sleep for sec[10] seconds. ",
934 " filename ; dump a file as hex bytes",
940 " [r|w] offset [length [dataptr]]; do a DiskIO read or write ",
947 EFI_HANDLE gExternalCmdHandle
= NULL
;
950 Initialize the commands in this in this file
953 EblInitializeCmdTable (
958 EblAddCommands (mCmdTemplate
, sizeof (mCmdTemplate
)/sizeof (EBL_COMMAND_TABLE
));
960 gBS
->InstallProtocolInterface (
962 &gEfiEblAddCommandProtocolGuid
,
963 EFI_NATIVE_INTERFACE
,
971 EblShutdownExternalCmdTable (
975 gBS
->UninstallProtocolInterface (gExternalCmdHandle
, &gEfiEblAddCommandProtocolGuid
, &gEblAddCommand
);