2 Basic command line parser for EBL (Embedded Boot Loader)
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.
21 // Globals for command history processing
22 INTN mCmdHistoryEnd
= -1;
23 INTN mCmdHistoryStart
= -1;
24 INTN mCmdHistoryCurrent
= -1;
25 CHAR8 mCmdHistory
[MAX_CMD_HISTORY
][MAX_CMD_LINE
];
26 CHAR8
*mCmdBlank
= "";
28 // Globals to remember current screen geometry
32 // Global to turn on/off breaking commands with prompts before they scroll the screen
33 BOOLEAN gPageBreak
= TRUE
;
42 if (*Value
>= MAX_CMD_HISTORY
) {
55 *Value
= MAX_CMD_HISTORY
- 1;
60 Save this command in the circular history buffer. Older commands are
61 overwritten with newer commands.
63 @param Cmd Command line to archive the history of.
73 // Don't bother adding empty commands to the list
74 if (AsciiStrLen(Cmd
) != 0) {
77 if (mCmdHistoryStart
== -1) {
81 // Record the new command at the next index
82 RingBufferIncrement(&mCmdHistoryStart
);
84 // If the next index runs into the end index, shuffle end back by one
85 if (mCmdHistoryStart
== mCmdHistoryEnd
) {
86 RingBufferIncrement(&mCmdHistoryEnd
);
90 // Copy the new command line into the ring buffer
91 AsciiStrnCpyS (&mCmdHistory
[mCmdHistoryStart
][0], MAX_CMD_LINE
, Cmd
, MAX_CMD_LINE
);
94 // Reset the command history for the next up arrow press
95 mCmdHistoryCurrent
= mCmdHistoryStart
;
100 Retreave data from the Command History buffer. Direction maps into up arrow
101 an down arrow on the command line
103 @param Direction Command forward or back
105 @return The Command history based on the Direction
113 CHAR8
*HistoricalCommand
= NULL
;
116 if (mCmdHistoryCurrent
== -1) {
117 HistoricalCommand
= mCmdBlank
;
121 if (Direction
== SCAN_UP
) {
122 HistoricalCommand
= &mCmdHistory
[mCmdHistoryCurrent
][0];
124 // if we just echoed the last command, hang out there, don't wrap around
125 if (mCmdHistoryCurrent
== mCmdHistoryEnd
) {
129 // otherwise, back up by one
130 RingBufferDecrement(&mCmdHistoryCurrent
);
132 } else if (Direction
== SCAN_DOWN
) {
134 // if we last echoed the start command, put a blank prompt out
135 if (mCmdHistoryCurrent
== mCmdHistoryStart
) {
136 HistoricalCommand
= mCmdBlank
;
140 // otherwise increment the current pointer and return that command
141 RingBufferIncrement(&mCmdHistoryCurrent
);
142 RingBufferIncrement(&mCmdHistoryCurrent
);
144 HistoricalCommand
= &mCmdHistory
[mCmdHistoryCurrent
][0];
145 RingBufferDecrement(&mCmdHistoryCurrent
);
149 return HistoricalCommand
;
154 Parse the CmdLine and break it up into Argc (arg count) and Argv (array of
155 pointers to each argument). The Cmd buffer is altered and separators are
156 converted to string terminators. This allows Argv to point into CmdLine.
157 A CmdLine can support multiple commands. The next command in the command line
158 is returned if it exists.
160 @param CmdLine String to parse for a set of commands
161 @param Argc Returns the number of arguments in the CmdLine current command
162 @param Argv Argc pointers to each string in CmdLine
164 @return Next Command in the command line or NULL if non exists
175 BOOLEAN LookingForArg
;
179 if (AsciiStrLen (CmdLine
) == 0) {
183 // Walk a single command line. A CMD_SEPARATOR allows multiple commands on a single line
185 LookingForArg
= TRUE
;
186 for (Char
= CmdLine
, Arg
= 0; *Char
!= '\0'; Char
++) {
187 if (!InQuote
&& *Char
== CMD_SEPARATOR
) {
191 // Perform any text conversion here
198 // Look for the beginning of an Argv[] entry
200 Argv
[Arg
++] = ++Char
;
201 LookingForArg
= FALSE
;
203 } else if (*Char
!= ' ') {
205 LookingForArg
= FALSE
;
208 // Looking for the terminator of an Argv[] entry
209 if (!InQuote
&& (*Char
== ' ')) {
211 LookingForArg
= TRUE
;
212 } else if (!InQuote
&& (*Char
== '"') && (*(Char
-1) != '\\')) {
214 } else if (InQuote
&& (*Char
== '"') && (*(Char
-1) != '\\')) {
223 if (*Char
== CMD_SEPARATOR
) {
224 // Replace the command delimiter with null and return pointer to next command line
234 Return a keypress or optionally timeout if a timeout value was passed in.
235 An optional callback function is called every second when waiting for a
238 @param Key EFI Key information returned
239 @param TimeoutInSec Number of seconds to wait to timeout
240 @param CallBack Callback called every second during the timeout wait
242 @return EFI_SUCCESS Key was returned
243 @return EFI_TIMEOUT If the TimoutInSec expired
249 IN OUT EFI_INPUT_KEY
*Key
,
250 IN UINTN TimeoutInSec
,
251 IN EBL_GET_CHAR_CALL_BACK CallBack OPTIONAL
257 EFI_EVENT WaitList
[2];
260 WaitList
[0] = gST
->ConIn
->WaitForKey
;
261 if (TimeoutInSec
!= 0) {
262 // Create a time event for 1 sec duration if we have a timeout
263 gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &WaitList
[1]);
264 gBS
->SetTimer (WaitList
[1], TimerPeriodic
, EFI_SET_TIMER_TO_SECOND
);
269 Status
= gBS
->WaitForEvent (WaitCount
, WaitList
, &WaitIndex
);
270 ASSERT_EFI_ERROR (Status
);
274 // Key event signaled
275 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
276 if (!EFI_ERROR (Status
)) {
277 if (WaitCount
== 2) {
278 gBS
->CloseEvent (WaitList
[1]);
285 // Periodic 1 sec timer signaled
287 if (CallBack
!= NULL
) {
288 // Call the users callback function if registered
289 CallBack (TimeoutInSec
);
291 if (TimeoutInSec
== 0) {
292 gBS
->CloseEvent (WaitList
[1]);
304 This routine is used prevent command output data from scrolling off the end
305 of the screen. The global gPageBreak is used to turn on or off this feature.
306 If the CurrentRow is near the end of the screen pause and print out a prompt
307 If the use hits Q to quit return TRUE else for any other key return FALSE.
308 PrefixNewline is used to figure out if a newline is needed before the prompt
309 string. This depends on the last print done before calling this function.
310 CurrentRow is updated by one on a call or set back to zero if a prompt is
313 @param CurrentRow Used to figure out if its the end of the page and updated
314 @param PrefixNewline Did previous print issue a newline
316 @return TRUE if Q was hit to quit, FALSE in all other cases.
321 EblAnyKeyToContinueQtoQuit (
322 IN UINTN
*CurrentRow
,
323 IN BOOLEAN PrefixNewline
326 EFI_INPUT_KEY InputKey
;
329 // global disable for this feature
333 if (*CurrentRow
>= (gScreenRows
- 2)) {
337 AsciiPrint ("Any key to continue (Q to quit): ");
338 EblGetCharKey (&InputKey
, 0, NULL
);
341 // Time to promt to stop the screen. We have to leave space for the prompt string
343 if (InputKey
.UnicodeChar
== 'Q' || InputKey
.UnicodeChar
== 'q') {
355 Set the text color of the EFI Console. If a zero is passed in reset to
356 default text/background color.
358 @param Attribute For text and background color
366 if (Attribute
== 0) {
367 // Set the text color back to default
368 Attribute
= (UINTN
)PcdGet32 (PcdEmbeddedDefaultTextColor
);
371 gST
->ConOut
->SetAttribute (gST
->ConOut
, Attribute
);
376 Collect the keyboard input for a cmd line. Carriage Return, New Line, or ESC
377 terminates the command line. You can edit the command line via left arrow,
378 delete and backspace and they all back up and erase the command line.
379 No edit of command line is possible without deletion at this time!
380 The up arrow and down arrow fill Cmd with information from the history
383 @param Cmd Command line to return
384 @param CmdMaxSize Maximum size of Cmd
386 @return The Status of EblGetCharKey()
402 for (Index
= 0; Index
< CmdMaxSize
- 1;) {
403 Status
= EblGetCharKey (&Key
, 0, NULL
);
404 if (EFI_ERROR (Status
)) {
410 Char
= (CHAR8
)Key
.UnicodeChar
;
411 if ((Char
== '\n') || (Char
== '\r') || (Char
== 0x7f)) {
413 if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho
) == TRUE
) {
417 } else if ((Char
== '\b') || (Key
.ScanCode
== SCAN_LEFT
) || (Key
.ScanCode
== SCAN_DELETE
)){
421 // Update the display
423 AsciiPrint ("\b \b");
425 } else if ((Key
.ScanCode
== SCAN_UP
) || Key
.ScanCode
== SCAN_DOWN
) {
426 History
= GetCmdHistory (Key
.ScanCode
);
428 // Clear display line
430 for (Index2
= 0; Index2
< Index
; Index2
++) {
431 AsciiPrint ("\b \b");
433 AsciiPrint (History
);
434 Index
= AsciiStrLen (History
);
435 AsciiStrnCpyS (Cmd
, CmdMaxSize
, History
, CmdMaxSize
);
438 if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho
) == TRUE
) {
439 AsciiPrint ("%c", Char
);
449 Print the boot up banner for the EBL.
452 EblPrintStartupBanner (
456 AsciiPrint ("Embedded Boot Loader (");
457 EblSetTextColor (EFI_YELLOW
);
460 AsciiPrint (") prototype. Built at %a on %a\n",__TIME__
, __DATE__
);
461 AsciiPrint ("THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN 'AS IS' BASIS,\nWITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\n");
462 AsciiPrint ("Please send feedback to edk2-devel@lists.sourceforge.net\n");
467 Send null requests to all removable media block IO devices so the a media add/remove/change
468 can be detected in real before we execute a command.
470 This is mainly due to the fact that the FAT driver does not do this today so you can get stale
471 dir commands after an SD Card has been removed.
474 EblProbeRemovableMedia (
483 // Probe for media insertion/removal in removable media devices
485 Max
= EfiGetDeviceCounts (EfiOpenBlockIo
);
487 for (Index
= 0; Index
< Max
; Index
++) {
488 File
= EfiDeviceOpenByType (EfiOpenBlockIo
, Index
);
490 if (File
->FsBlockIoMedia
->RemovableMedia
) {
491 // Probe to see if media is present (or not) or media changed
492 // this causes the ReinstallProtocolInterface() to fire in the
493 // block io driver to update the system about media change events
494 File
->FsBlockIo
->ReadBlocks (File
->FsBlockIo
, File
->FsBlockIo
->Media
->MediaId
, (EFI_LBA
)0, 0, NULL
);
506 Print the prompt for the EBL.
513 EblSetTextColor (EFI_YELLOW
);
514 AsciiPrint ("%a %a",(CHAR8
*)PcdGetPtr (PcdEmbeddedPrompt
), EfiGetCwd ());
516 AsciiPrint ("%a", ">");
522 Parse a command line and execute the commands. The ; separator allows
523 multiple commands for each command line. Stop processing if one of the
524 commands returns an error.
526 @param CmdLine Command Line to process.
527 @param MaxCmdLineSize MaxSize of the Command line
529 @return EFI status of the Command
535 IN UINTN MaxCmdLineSize
539 EBL_COMMAND_TABLE
*Cmd
;
542 CHAR8
*Argv
[MAX_ARGS
];
544 // Parse the command line. The loop processes commands separated by ;
545 for (Ptr
= CmdLine
, Status
= EFI_SUCCESS
; Ptr
!= NULL
;) {
546 Ptr
= ParseArguments (Ptr
, &Argc
, Argv
);
548 Cmd
= EblGetCommand (Argv
[0]);
550 // Execute the Command!
551 Status
= Cmd
->Command (Argc
, Argv
);
552 if (Status
== EFI_ABORTED
) {
553 // exit command so lets exit
555 } else if (Status
== EFI_TIMEOUT
) {
556 // pause command got input so don't process any more cmd on this cmd line
558 } else if (EFI_ERROR (Status
)) {
559 AsciiPrint ("%a returned %r error\n", Cmd
->Name
, Status
);
560 // if any command fails stop processing CmdLine
564 AsciiPrint ("The command '%a' is not supported.\n", Argv
[0]);
575 Embedded Boot Loader (EBL) - A simple EFI command line application for embedded
576 devices. PcdEmbeddedAutomaticBootCommand is a complied in command line that
577 gets executed automatically. The ; separator allows multiple commands
578 for each command line.
580 @param ImageHandle EFI ImageHandle for this application.
581 @param SystemTable EFI system table
583 @return EFI status of the application
589 IN EFI_HANDLE ImageHandle
,
590 IN EFI_SYSTEM_TABLE
*SystemTable
594 CHAR8 CmdLine
[MAX_CMD_LINE
];
595 CHAR16
*CommandLineVariable
= NULL
;
596 CHAR16
*CommandLineVariableName
= L
"default-cmdline";
597 UINTN CommandLineVariableSize
= 0;
600 // Initialize tables of commands
601 EblInitializeCmdTable ();
602 EblInitializeDeviceCmd ();
603 EblInitializemdHwDebugCmds ();
604 EblInitializemdHwIoDebugCmds ();
605 EblInitializeDirCmd ();
606 EblInitializeHobCmd ();
607 EblInitializeScriptCmd ();
608 EblInitializeExternalCmd ();
609 EblInitializeNetworkCmd();
610 EblInitializeVariableCmds ();
612 if (gST
->ConOut
== NULL
) {
613 DEBUG((EFI_D_ERROR
,"Error: No Console Output\n"));
614 return EFI_NOT_READY
;
617 // Disable the 5 minute EFI watchdog time so we don't get automatically reset
618 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
620 if (FeaturePcdGet (PcdEmbeddedMacBoot
)) {
621 // A MAC will boot in graphics mode, so turn it back to text here
622 // This protocol was removed from edk2. It is only an edk thing. We need to make our own copy.
623 // DisableQuietBoot ();
625 // Enable the biggest output screen size possible
626 gST
->ConOut
->SetMode (gST
->ConOut
, (UINTN
)gST
->ConOut
->Mode
->MaxMode
- 1);
630 // Save current screen mode
631 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &gScreenColumns
, &gScreenRows
);
633 EblPrintStartupBanner ();
635 // Parse command line and handle commands separated by ;
636 // The loop prints the prompt gets user input and saves history
638 // Look for a variable with a default command line, otherwise use the Pcd
639 ZeroMem(&VendorGuid
, sizeof(EFI_GUID
));
641 Status
= gRT
->GetVariable(CommandLineVariableName
, &VendorGuid
, NULL
, &CommandLineVariableSize
, CommandLineVariable
);
642 if (Status
== EFI_BUFFER_TOO_SMALL
) {
643 CommandLineVariable
= AllocatePool(CommandLineVariableSize
);
645 Status
= gRT
->GetVariable(CommandLineVariableName
, &VendorGuid
, NULL
, &CommandLineVariableSize
, CommandLineVariable
);
646 if (!EFI_ERROR(Status
)) {
647 UnicodeStrToAsciiStrS (CommandLineVariable
, CmdLine
, MAX_CMD_LINE
);
650 FreePool(CommandLineVariable
);
653 if (EFI_ERROR(Status
)) {
654 AsciiStrCpyS (CmdLine
, MAX_CMD_LINE
, (CHAR8
*)PcdGetPtr (PcdEmbeddedAutomaticBootCommand
));
658 Status
= ProcessCmdLine (CmdLine
, MAX_CMD_LINE
);
659 if (Status
== EFI_ABORTED
) {
660 // if a command returns EFI_ABORTED then exit the EBL
661 EblShutdownExternalCmdTable ();
665 // get the command line from the user
667 GetCmd (CmdLine
, MAX_CMD_LINE
);
668 SetCmdHistory (CmdLine
);
670 if (FeaturePcdGet (PcdEmbeddedProbeRemovable
)) {
671 // Probe removable media devices to see if media has been inserted or removed.
672 EblProbeRemovableMedia ();