2009-12-06 02:57:05 +01:00
/** @file
Basic command line parser for EBL ( Embedded Boot Loader )
2010-04-29 14:40:51 +02:00
Copyright ( c ) 2007 , Intel Corporation . All rights reserved . < BR >
Portions copyright ( c ) 2008 - 2009 , Apple Inc . All rights reserved . < BR >
2009-12-06 02:57:05 +01:00
2010-04-29 14:40:51 +02:00
This program and the accompanying materials
2009-12-06 02:57:05 +01:00
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution . The full text of the license may be found at
http : //opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN " AS IS " BASIS ,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND , EITHER EXPRESS OR IMPLIED .
* */
# include "Ebl.h"
// Globals for command history processing
INTN mCmdHistoryEnd = - 1 ;
INTN mCmdHistoryStart = - 1 ;
INTN mCmdHistoryCurrent = - 1 ;
CHAR8 mCmdHistory [ MAX_CMD_HISTORY ] [ MAX_CMD_LINE ] ;
CHAR8 * mCmdBlank = " " ;
// Globals to remember current screen geometry
UINTN gScreenColumns ;
UINTN gScreenRows ;
// Global to turn on/off breaking commands with prompts before they scroll the screen
BOOLEAN gPageBreak = TRUE ;
VOID
RingBufferIncrement (
IN INTN * Value
)
{
* Value = * Value + 1 ;
if ( * Value > = MAX_CMD_HISTORY ) {
* Value = 0 ;
}
}
VOID
RingBufferDecrement (
IN INTN * Value
)
{
* Value = * Value - 1 ;
if ( * Value < 0 ) {
* Value = MAX_CMD_HISTORY - 1 ;
}
}
/**
Save this command in the circular history buffer . Older commands are
overwritten with newer commands .
@ param Cmd Command line to archive the history of .
@ return None
* */
VOID
SetCmdHistory (
IN CHAR8 * Cmd
)
{
// Don't bother adding empty commands to the list
if ( AsciiStrLen ( Cmd ) ! = 0 ) {
// First entry
if ( mCmdHistoryStart = = - 1 ) {
mCmdHistoryStart = 0 ;
mCmdHistoryEnd = 0 ;
} else {
// Record the new command at the next index
RingBufferIncrement ( & mCmdHistoryStart ) ;
// If the next index runs into the end index, shuffle end back by one
if ( mCmdHistoryStart = = mCmdHistoryEnd ) {
RingBufferIncrement ( & mCmdHistoryEnd ) ;
}
}
// Copy the new command line into the ring buffer
AsciiStrnCpy ( & mCmdHistory [ mCmdHistoryStart ] [ 0 ] , Cmd , MAX_CMD_LINE ) ;
}
// Reset the command history for the next up arrow press
mCmdHistoryCurrent = mCmdHistoryStart ;
}
/**
Retreave data from the Command History buffer . Direction maps into up arrow
an down arrow on the command line
@ param Direction Command forward or back
@ return The Command history based on the Direction
* */
CHAR8 *
GetCmdHistory (
IN UINT16 Direction
)
{
CHAR8 * HistoricalCommand = NULL ;
// No history yet?
if ( mCmdHistoryCurrent = = - 1 ) {
HistoricalCommand = mCmdBlank ;
goto Exit ;
}
if ( Direction = = SCAN_UP ) {
HistoricalCommand = & mCmdHistory [ mCmdHistoryCurrent ] [ 0 ] ;
// if we just echoed the last command, hang out there, don't wrap around
if ( mCmdHistoryCurrent = = mCmdHistoryEnd ) {
goto Exit ;
}
// otherwise, back up by one
RingBufferDecrement ( & mCmdHistoryCurrent ) ;
} else if ( Direction = = SCAN_DOWN ) {
// if we last echoed the start command, put a blank prompt out
if ( mCmdHistoryCurrent = = mCmdHistoryStart ) {
HistoricalCommand = mCmdBlank ;
goto Exit ;
}
// otherwise increment the current pointer and return that command
RingBufferIncrement ( & mCmdHistoryCurrent ) ;
RingBufferIncrement ( & mCmdHistoryCurrent ) ;
HistoricalCommand = & mCmdHistory [ mCmdHistoryCurrent ] [ 0 ] ;
RingBufferDecrement ( & mCmdHistoryCurrent ) ;
}
Exit :
return HistoricalCommand ;
}
/**
Parse the CmdLine and break it up into Argc ( arg count ) and Argv ( array of
pointers to each argument ) . The Cmd buffer is altered and seperators are
converted to string terminators . This allows Argv to point into CmdLine .
A CmdLine can support multiple commands . The next command in the command line
is returned if it exists .
@ param CmdLine String to parse for a set of commands
@ param Argc Returns the number of arguments in the CmdLine current command
@ param Argv Argc pointers to each string in CmdLine
@ return Next Command in the command line or NULL if non exists
* */
CHAR8 *
ParseArguments (
IN CHAR8 * CmdLine ,
OUT UINTN * Argc ,
OUT CHAR8 * * Argv
)
{
UINTN Arg ;
CHAR8 * Char ;
BOOLEAN LookingForArg ;
BOOLEAN InQuote ;
* Argc = 0 ;
if ( AsciiStrLen ( CmdLine ) = = 0 ) {
return NULL ;
}
// Walk a single command line. A CMD_SEPERATOR allows mult commands on a single line
InQuote = FALSE ;
LookingForArg = TRUE ;
for ( Char = CmdLine , Arg = 0 ; * Char ! = ' \0 ' ; Char + + ) {
if ( ! InQuote & & * Char = = CMD_SEPERATOR ) {
break ;
}
2011-02-01 06:37:22 +01:00
// Perform any text conversion here
2009-12-06 02:57:05 +01:00
if ( * Char = = ' \t ' ) {
// TAB to space
* Char = ' ' ;
}
if ( LookingForArg ) {
// Look for the beging of an Argv[] entry
if ( * Char = = ' " ' ) {
Argv [ Arg + + ] = + + Char ;
LookingForArg = FALSE ;
InQuote = TRUE ;
} else if ( * Char ! = ' ' ) {
Argv [ Arg + + ] = Char ;
LookingForArg = FALSE ;
}
} else {
// Looking for the terminator of an Argv[] entry
2011-02-01 06:37:22 +01:00
if ( ! InQuote & & ( * Char = = ' ' ) ) {
2009-12-06 02:57:05 +01:00
* Char = ' \0 ' ;
LookingForArg = TRUE ;
2011-02-01 06:37:22 +01:00
} else if ( ! InQuote & & ( * Char = = ' " ' ) & & ( * ( Char - 1 ) ! = ' \\ ' ) ) {
InQuote = TRUE ;
} else if ( InQuote & & ( * Char = = ' " ' ) & & ( * ( Char - 1 ) ! = ' \\ ' ) ) {
* Char = ' \0 ' ;
InQuote = FALSE ;
2009-12-06 02:57:05 +01:00
}
}
}
* Argc = Arg ;
if ( * Char = = CMD_SEPERATOR ) {
// Replace the command delimeter with null and return pointer to next command line
* Char = ' \0 ' ;
return + + Char ;
}
return NULL ;
}
/**
Return a keypress or optionally timeout if a timeout value was passed in .
An optional callback funciton is called evey second when waiting for a
timeout .
@ param Key EFI Key information returned
@ param TimeoutInSec Number of seconds to wait to timeout
@ param CallBack Callback called every second during the timeout wait
@ return EFI_SUCCESS Key was returned
@ return EFI_TIMEOUT If the TimoutInSec expired
* */
EFI_STATUS
EblGetCharKey (
IN OUT EFI_INPUT_KEY * Key ,
IN UINTN TimeoutInSec ,
IN EBL_GET_CHAR_CALL_BACK CallBack OPTIONAL
)
{
EFI_STATUS Status ;
UINTN WaitCount ;
UINTN WaitIndex ;
EFI_EVENT WaitList [ 2 ] ;
WaitCount = 1 ;
WaitList [ 0 ] = gST - > ConIn - > WaitForKey ;
if ( TimeoutInSec ! = 0 ) {
// Create a time event for 1 sec duration if we have a timeout
gBS - > CreateEvent ( EVT_TIMER , 0 , NULL , NULL , & WaitList [ 1 ] ) ;
gBS - > SetTimer ( WaitList [ 1 ] , TimerPeriodic , EFI_SET_TIMER_TO_SECOND ) ;
WaitCount + + ;
}
for ( ; ; ) {
Status = gBS - > WaitForEvent ( WaitCount , WaitList , & WaitIndex ) ;
ASSERT_EFI_ERROR ( Status ) ;
switch ( WaitIndex ) {
case 0 :
// Key event signaled
Status = gST - > ConIn - > ReadKeyStroke ( gST - > ConIn , Key ) ;
if ( ! EFI_ERROR ( Status ) ) {
if ( WaitCount = = 2 ) {
gBS - > CloseEvent ( WaitList [ 1 ] ) ;
}
return EFI_SUCCESS ;
}
break ;
case 1 :
// Periodic 1 sec timer signaled
TimeoutInSec - - ;
if ( CallBack ! = NULL ) {
// Call the users callback function if registered
CallBack ( TimeoutInSec ) ;
}
if ( TimeoutInSec = = 0 ) {
gBS - > CloseEvent ( WaitList [ 1 ] ) ;
return EFI_TIMEOUT ;
}
break ;
default :
ASSERT ( FALSE ) ;
}
}
}
/**
This routine is used prevent command output data from scrolling off the end
of the screen . The global gPageBreak is used to turn on or off this feature .
If the CurrentRow is near the end of the screen pause and print out a prompt
If the use hits Q to quit return TRUE else for any other key return FALSE .
PrefixNewline is used to figure out if a newline is needed before the prompt
string . This depends on the last print done before calling this function .
CurrentRow is updated by one on a call or set back to zero if a prompt is
needed .
@ param CurrentRow Used to figure out if its the end of the page and updated
@ param PrefixNewline Did previous print issue a newline
@ return TRUE if Q was hit to quit , FALSE in all other cases .
* */
BOOLEAN
EblAnyKeyToContinueQtoQuit (
IN UINTN * CurrentRow ,
IN BOOLEAN PrefixNewline
)
{
EFI_INPUT_KEY InputKey ;
if ( ! gPageBreak ) {
// global disable for this feature
return FALSE ;
}
if ( * CurrentRow > = ( gScreenRows - 2 ) ) {
if ( PrefixNewline ) {
AsciiPrint ( " \n " ) ;
}
AsciiPrint ( " Any key to continue (Q to quit): " ) ;
EblGetCharKey ( & InputKey , 0 , NULL ) ;
AsciiPrint ( " \n " ) ;
// Time to promt to stop the screen. We have to leave space for the prompt string
* CurrentRow = 0 ;
if ( InputKey . UnicodeChar = = ' Q ' | | InputKey . UnicodeChar = = ' q ' ) {
return TRUE ;
}
} else {
* CurrentRow + = 1 ;
}
return FALSE ;
}
/**
Set the text color of the EFI Console . If a zero is passed in reset to
default text / background color .
@ param Attribute For text and background color
* */
VOID
EblSetTextColor (
UINTN Attribute
)
{
if ( Attribute = = 0 ) {
// Set the text color back to default
Attribute = ( UINTN ) PcdGet32 ( PcdEmbeddedDefaultTextColor ) ;
}
gST - > ConOut - > SetAttribute ( gST - > ConOut , Attribute ) ;
}
/**
Collect the keyboard input for a cmd line . Carage Return , New Line , or ESC
terminates the command line . You can edit the command line via left arrow ,
delete and backspace and they all back up and erase the command line .
No edit of commnad line is possible without deletion at this time !
The up arrow and down arrow fill Cmd with information from the history
buffer .
@ param Cmd Command line to return
@ param CmdMaxSize Maximum size of Cmd
@ return The Status of EblGetCharKey ( )
* */
EFI_STATUS
GetCmd (
IN OUT CHAR8 * Cmd ,
IN UINTN CmdMaxSize
)
{
EFI_STATUS Status ;
UINTN Index ;
UINTN Index2 ;
CHAR8 Char ;
CHAR8 * History ;
EFI_INPUT_KEY Key ;
for ( Index = 0 ; Index < CmdMaxSize - 1 ; ) {
Status = EblGetCharKey ( & Key , 0 , NULL ) ;
if ( EFI_ERROR ( Status ) ) {
Cmd [ Index ] = ' \0 ' ;
AsciiPrint ( " \n " ) ;
return Status ;
}
Char = ( CHAR8 ) Key . UnicodeChar ;
if ( ( Char = = ' \n ' ) | | ( Char = = ' \r ' ) | | ( Char = = 0x7f ) ) {
Cmd [ Index ] = ' \0 ' ;
if ( FixedPcdGetBool ( PcdEmbeddedShellCharacterEcho ) = = TRUE ) {
AsciiPrint ( " \n \r " ) ;
}
return EFI_SUCCESS ;
} else if ( ( Char = = ' \b ' ) | | ( Key . ScanCode = = SCAN_LEFT ) | | ( Key . ScanCode = = SCAN_DELETE ) ) {
if ( Index ! = 0 ) {
Index - - ;
//
// Update the display
//
AsciiPrint ( " \b \b " ) ;
}
} else if ( ( Key . ScanCode = = SCAN_UP ) | | Key . ScanCode = = SCAN_DOWN ) {
History = GetCmdHistory ( Key . ScanCode ) ;
//
// Clear display line
//
for ( Index2 = 0 ; Index2 < Index ; Index2 + + ) {
AsciiPrint ( " \b \b " ) ;
}
AsciiPrint ( History ) ;
Index = AsciiStrLen ( History ) ;
AsciiStrnCpy ( Cmd , History , CmdMaxSize ) ;
} else {
Cmd [ Index + + ] = Char ;
if ( FixedPcdGetBool ( PcdEmbeddedShellCharacterEcho ) = = TRUE ) {
AsciiPrint ( " %c " , Char ) ;
}
}
}
return EFI_SUCCESS ;
}
/**
Print the boot up banner for the EBL .
* */
VOID
EblPrintStartupBanner (
VOID
)
{
AsciiPrint ( " Embedded Boot Loader ( " ) ;
EblSetTextColor ( EFI_YELLOW ) ;
AsciiPrint ( " EBL " ) ;
EblSetTextColor ( 0 ) ;
AsciiPrint ( " ) prototype. Built at %a on %a \n " , __TIME__ , __DATE__ ) ;
AsciiPrint ( " THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN 'AS IS' BASIS, \n WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \n " ) ;
2010-01-31 02:46:17 +01:00
AsciiPrint ( " Please send feedback to edk2-devel@lists.sourceforge.net \n " ) ;
2009-12-06 02:57:05 +01:00
}
2010-05-11 02:09:48 +02:00
/**
Send null requests to all removable media block IO devices so the a media add / remove / change
can be detected in real before we execute a command .
This is mainly due to the fact that the FAT driver does not do this today so you can get stale
dir commands after an SD Card has been removed .
* */
VOID
EblProbeRemovableMedia (
VOID
)
{
UINTN Index ;
UINTN Max ;
EFI_OPEN_FILE * File ;
//
// Probe for media insertion/removal in removable media devices
//
Max = EfiGetDeviceCounts ( EfiOpenBlockIo ) ;
if ( Max ! = 0 ) {
for ( Index = 0 ; Index < Max ; Index + + ) {
File = EfiDeviceOpenByType ( EfiOpenBlockIo , Index ) ;
if ( File ! = NULL ) {
if ( File - > FsBlockIoMedia - > RemovableMedia ) {
// Probe to see if media is present (or not) or media changed
// this causes the ReinstallProtocolInterface() to fire in the
// block io driver to update the system about media change events
File - > FsBlockIo - > ReadBlocks ( File - > FsBlockIo , File - > FsBlockIo - > Media - > MediaId , ( EFI_LBA ) 0 , 0 , NULL ) ;
}
EfiClose ( File ) ;
}
}
}
}
2009-12-06 02:57:05 +01:00
/**
Print the prompt for the EBL .
* */
VOID
EblPrompt (
VOID
)
{
EblSetTextColor ( EFI_YELLOW ) ;
2010-02-10 01:46:41 +01:00
AsciiPrint ( ( CHAR8 * ) PcdGetPtr ( PcdEmbeddedPrompt ) , EfiGetCwd ( ) ) ;
2009-12-06 02:57:05 +01:00
EblSetTextColor ( 0 ) ;
AsciiPrint ( " %a " , " > " ) ;
}
/**
Parse a command line and execute the commands . The ; seperator allows
multiple commands for each command line . Stop processing if one of the
commands returns an error .
@ param CmdLine Command Line to process .
@ param MaxCmdLineSize MaxSize of the Command line
@ return EFI status of the Command
* */
EFI_STATUS
ProcessCmdLine (
IN CHAR8 * CmdLine ,
IN UINTN MaxCmdLineSize
)
{
EFI_STATUS Status ;
EBL_COMMAND_TABLE * Cmd ;
CHAR8 * Ptr ;
UINTN Argc ;
CHAR8 * Argv [ MAX_ARGS ] ;
// Parse the command line. The loop processes commands seperated by ;
for ( Ptr = CmdLine , Status = EFI_SUCCESS ; Ptr ! = NULL ; ) {
Ptr = ParseArguments ( Ptr , & Argc , Argv ) ;
if ( Argc ! = 0 ) {
Cmd = EblGetCommand ( Argv [ 0 ] ) ;
if ( Cmd ! = NULL ) {
// Execute the Command!
Status = Cmd - > Command ( Argc , Argv ) ;
if ( Status = = EFI_ABORTED ) {
// exit command so lets exit
break ;
} else if ( Status = = EFI_TIMEOUT ) {
// pause command got imput so don't process any more cmd on this cmd line
break ;
} else if ( EFI_ERROR ( Status ) ) {
AsciiPrint ( " %a returned %r error \n " , Cmd - > Name , Status ) ;
// if any command fails stop processing CmdLine
break ;
}
}
}
}
return Status ;
}
/**
Embedded Boot Loader ( EBL ) - A simple EFI command line application for embedded
devices . PcdEmbeddedAutomaticBootCommand is a complied in commnad line that
gets executed automatically . The ; seperator allows multiple commands
for each command line .
@ param ImageHandle EFI ImageHandle for this application .
@ param SystemTable EFI system table
@ return EFI status of the applicaiton
* */
EFI_STATUS
EFIAPI
EdkBootLoaderEntry (
IN EFI_HANDLE ImageHandle ,
IN EFI_SYSTEM_TABLE * SystemTable
)
{
EFI_STATUS Status ;
CHAR8 CmdLine [ MAX_CMD_LINE ] ;
CHAR16 * CommandLineVariable = NULL ;
CHAR16 * CommandLineVariableName = L " default-cmdline " ;
UINTN CommandLineVariableSize = 0 ;
EFI_GUID VendorGuid ;
// Initialize tables of commnads
EblInitializeCmdTable ( ) ;
EblInitializeDeviceCmd ( ) ;
EblInitializemdHwDebugCmds ( ) ;
EblInitializemdHwIoDebugCmds ( ) ;
EblInitializeDirCmd ( ) ;
EblInitializeHobCmd ( ) ;
EblInitializeScriptCmd ( ) ;
EblInitializeExternalCmd ( ) ;
EblInitializeNetworkCmd ( ) ;
2011-02-01 06:37:22 +01:00
EblInitializeVariableCmds ( ) ;
2009-12-06 02:57:05 +01:00
2010-02-10 01:46:41 +01:00
// Disable the 5 minute EFI watchdog time so we don't get automatically reset
gBS - > SetWatchdogTimer ( 0 , 0 , 0 , NULL ) ;
2009-12-06 02:57:05 +01:00
if ( FeaturePcdGet ( PcdEmbeddedMacBoot ) ) {
// A MAC will boot in graphics mode, so turn it back to text here
// This protocol was removed from edk2. It is only an edk thing. We need to make our own copy.
// DisableQuietBoot ();
// Enable the biggest output screen size possible
gST - > ConOut - > SetMode ( gST - > ConOut , ( UINTN ) gST - > ConOut - > Mode - > MaxMode - 1 ) ;
}
// Save current screen mode
gST - > ConOut - > QueryMode ( gST - > ConOut , gST - > ConOut - > Mode - > Mode , & gScreenColumns , & gScreenRows ) ;
EblPrintStartupBanner ( ) ;
// Parse command line and handle commands seperated by ;
// The loop prints the prompt gets user input and saves history
// Look for a variable with a default command line, otherwise use the Pcd
ZeroMem ( & VendorGuid , sizeof ( EFI_GUID ) ) ;
Status = gRT - > GetVariable ( CommandLineVariableName , & VendorGuid , NULL , & CommandLineVariableSize , CommandLineVariable ) ;
if ( Status = = EFI_BUFFER_TOO_SMALL ) {
CommandLineVariable = AllocatePool ( CommandLineVariableSize ) ;
Status = gRT - > GetVariable ( CommandLineVariableName , & VendorGuid , NULL , & CommandLineVariableSize , CommandLineVariable ) ;
if ( ! EFI_ERROR ( Status ) ) {
UnicodeStrToAsciiStr ( CommandLineVariable , CmdLine ) ;
}
FreePool ( CommandLineVariable ) ;
}
if ( EFI_ERROR ( Status ) ) {
AsciiStrCpy ( CmdLine , ( CHAR8 * ) PcdGetPtr ( PcdEmbeddedAutomaticBootCommand ) ) ;
}
for ( ; ; ) {
Status = ProcessCmdLine ( CmdLine , MAX_CMD_LINE ) ;
if ( Status = = EFI_ABORTED ) {
// if a command returns EFI_ABORTED then exit the EBL
EblShutdownExternalCmdTable ( ) ;
return EFI_SUCCESS ;
}
// get the command line from the user
EblPrompt ( ) ;
GetCmd ( CmdLine , MAX_CMD_LINE ) ;
SetCmdHistory ( CmdLine ) ;
2010-05-11 02:09:48 +02:00
if ( FeaturePcdGet ( PcdEmbeddedProbeRemovable ) ) {
// Probe removable media devices to see if media has been inserted or removed.
EblProbeRemovableMedia ( ) ;
}
2009-12-06 02:57:05 +01:00
}
}