You've already forked slimbootloader
mirror of
https://github.com/Dasharo/slimbootloader.git
synced 2026-03-06 15:26:20 -08:00
Convert the line endings stored for all text files in the repository to LF. The majority previously used DOS-style CRLF line endings. Add a .gitattributes file to enforce this and treat certain extensions as never being text files. Update PatchCheck.py to insist on LF line endings rather than CRLF. However, its other checks fail on this commit due to lots of pre-existing complaints that it only notices because the line endings have changed. Silicon/QemuSocPkg/FspBin/Patches/0001-Build-QEMU-FSP-2.0-binaries.patch needs to be treated as binary since it contains a mixture of line endings. This change has implications depending on the client platform you are using the repository from: * Windows The usual configuration for Git on Windows means that text files will be checked out to the work tree with DOS-style CRLF line endings. If that's not the case then you can configure Git to do so for the entire machine with: git config --global core.autocrlf true or for just the repository with: git config core.autocrlf true Line endings will be normalised to LF when they are committed to the repository. If you commit a text file with only LF line endings then it will be converted to CRLF line endings in your work tree. * Linux, MacOS and other Unices The usual configuration for Git on such platforms is to check files out of the repository with LF line endings. This is probably the right thing for you. In the unlikely even that you are using Git on Unix but editing or compiling on Windows for some reason then you may need to tweak your configuration to force the use of CRLF line endings as described above. * General For more information see https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings . Fixes: https://github.com/slimbootloader/slimbootloader/issues/1400 Signed-off-by: Mike Crowe <mac@mcrowe.com>
616 lines
14 KiB
C
616 lines
14 KiB
C
/** @file
|
|
A minimal command-line shell.
|
|
|
|
Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <Library/ShellLib.h>
|
|
#include <Library/ConsoleInLib.h>
|
|
#include <Library/ConsoleOutLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/TimerLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/SortLib.h>
|
|
#include "Shell.h"
|
|
#include "History.h"
|
|
#include "Parsing.h"
|
|
#include "ShellCmds.h"
|
|
|
|
#define ESC '\x1b'
|
|
|
|
/**
|
|
Wait for command line input from the serial port.
|
|
|
|
@param[in] Shell shell instance
|
|
@param[out] Buffer buffer to receive command line
|
|
@param[in] BufferSize size (in bytes) of the buffer
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_BUFFER_TOO_SMALL
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
ReadShellCommand (
|
|
IN SHELL *Shell,
|
|
OUT CHAR16 *Buffer,
|
|
IN CONST UINTN BufferSize
|
|
)
|
|
{
|
|
UINTN Count;
|
|
UINTN CurrCount;
|
|
UINT8 Char;
|
|
UINTN HistoryLineLen;
|
|
CHAR16 HistoryLine[MAX_COMMAND_LINE_LEN];
|
|
|
|
Count = 0;
|
|
|
|
ASSERT (Buffer != NULL);
|
|
ASSERT (BufferSize > sizeof (CHAR16));
|
|
|
|
while (1) {
|
|
// Check for buffer overflow
|
|
if (Count >= (BufferSize / sizeof (CHAR16) - 1)) {
|
|
Buffer[0] = CHAR_NULL; // Terminate String
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Get next character from serial port
|
|
Char = 0;
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
// Check for end of line
|
|
if ((Char == '\r') || (Char == '\n')) {
|
|
Buffer[Count] = CHAR_NULL; // Terminate String
|
|
break;
|
|
}
|
|
|
|
// Check for backspace/delete
|
|
if ((Char == 0x7f) || (Char == '\b')) {
|
|
if (Count > 0) {
|
|
// Move cursor back 1 char and clear
|
|
ShellPrint (L"\b \b");
|
|
Count--;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Handle key codes of the form: ESC [ x
|
|
if (Char == ESC) {
|
|
// Expect '[' character
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
if (Char == '[') {
|
|
// Expect command
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
HistoryLineLen = 0;
|
|
if (Char == 'A') { // Up
|
|
HistoryLineLen = HistoryUp (Shell, HistoryLine);
|
|
} else if (Char == 'B') { // Down
|
|
HistoryLineLen = HistoryDown (Shell, HistoryLine);
|
|
} else if (Char == 'C') { // Right
|
|
} else if (Char == 'D') { // Left
|
|
}
|
|
|
|
|
|
CurrCount = Count;
|
|
while (Count-- > 0) {
|
|
ShellPrint (L"\b");
|
|
}
|
|
if (HistoryLineLen > 0) {
|
|
StrCpyS (Buffer, Shell->CommandLineMaxLen, HistoryLine);
|
|
ShellPrint (L"%s", Buffer);
|
|
}
|
|
if (CurrCount > HistoryLineLen) {
|
|
CurrCount = CurrCount - HistoryLineLen;
|
|
|
|
Count = CurrCount;
|
|
while (Count-- > 0) {
|
|
ShellPrint (L" ");
|
|
}
|
|
|
|
Count = CurrCount;
|
|
while (Count-- > 0) {
|
|
ShellPrint (L"\b");
|
|
}
|
|
}
|
|
Count = HistoryLineLen;
|
|
|
|
// Key code consumed, continue getting input
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Echo character back and save character in buffer
|
|
ShellPrint (L"%c", Char);
|
|
Buffer[Count++] = Char;
|
|
}
|
|
|
|
ShellPrint (L"\n");
|
|
TrimSpaces (&Buffer);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Read a line of input from the serial port.
|
|
|
|
@param[in] Shell shell instance
|
|
@param[out] Buffer buffer to receive command line
|
|
@param[in] BufferSize size (in bytes) of the buffer
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_BUFFER_TOO_SMALL
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ShellReadLine (
|
|
IN SHELL *Shell,
|
|
OUT CHAR16 *Buffer,
|
|
IN CONST UINTN BufferSize
|
|
)
|
|
{
|
|
UINTN Count;
|
|
UINT8 Char;
|
|
|
|
Count = 0;
|
|
|
|
ASSERT (Buffer != NULL);
|
|
ASSERT (BufferSize > sizeof (CHAR16));
|
|
|
|
while (1) {
|
|
// Check for buffer overflow
|
|
if (Count >= (BufferSize / sizeof (CHAR16) - 1)) {
|
|
Buffer[0] = CHAR_NULL; // Terminate String
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Get next character from serial port
|
|
Char = 0;
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
// Check for end of line
|
|
if ((Char == '\r') || (Char == '\n')) {
|
|
Buffer[Count] = CHAR_NULL; // Terminate String
|
|
break;
|
|
}
|
|
|
|
// Check for backspace/delete
|
|
if ((Char == 0x7f) || (Char == '\b')) {
|
|
if (Count > 0) {
|
|
// Move cursor back 1 char and clear
|
|
ShellPrint (L"\b \b");
|
|
Count--;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Handle key codes of the form: ESC [ x
|
|
if (Char == ESC) {
|
|
// Expect '[' character
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
if (Char == '[') {
|
|
// Expect command
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
if (Char == 'A') { // Up
|
|
} else if (Char == 'B') { // Down
|
|
} else if (Char == 'C') { // Right
|
|
} else if (Char == 'D') { // Left
|
|
}
|
|
|
|
// Key code consumed, continue getting input
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Echo character back and save character in buffer
|
|
ShellPrint (L"%c", Char);
|
|
Buffer[Count++] = Char;
|
|
}
|
|
|
|
ShellPrint (L"\n");
|
|
TrimSpaces (&Buffer);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Read a UINT value from the serial port.
|
|
|
|
@param[in] Shell shell instance
|
|
@param[out] Buffer buffer to receive command line
|
|
@param[in] BufferSize size (in bytes) of the buffer
|
|
@param[out] IsHex determine if the UINT is hex or decimal format
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_BUFFER_TOO_SMALL
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ShellReadUintn (
|
|
IN SHELL *Shell,
|
|
OUT CHAR16 *Buffer,
|
|
IN CONST UINTN BufferSize,
|
|
OUT BOOLEAN *IsHex
|
|
)
|
|
{
|
|
UINTN Count;
|
|
UINT8 Char;
|
|
|
|
Count = 0;
|
|
|
|
ASSERT (Buffer != NULL);
|
|
ASSERT (BufferSize > sizeof (CHAR16));
|
|
|
|
*IsHex = FALSE;
|
|
ZeroMem (Buffer, BufferSize);
|
|
|
|
while (1) {
|
|
// Check for buffer overflow
|
|
if (Count >= (BufferSize / sizeof (CHAR16) - 1)) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Get next character from serial port
|
|
Char = 0;
|
|
if (ConsoleRead (&Char, 1) < 1) {
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
if ((Char == '\r') || (Char == '\n')) {
|
|
// Check for end of line
|
|
Buffer[Count] = CHAR_NULL; // Terminate String
|
|
break;
|
|
} else if ((Char == 0x7f) || (Char == '\b')) {
|
|
// Check for backspace/delete
|
|
if (Count > 0) {
|
|
if (Buffer[Count - 1] == 'x' || Buffer[Count - 1] == 'X') {
|
|
*IsHex = FALSE;
|
|
}
|
|
// Move cursor back 1 char and clear
|
|
ShellPrint (L"\b \b");
|
|
Count--;
|
|
}
|
|
continue;
|
|
} else if (Char == 'x' || Char == 'X') {
|
|
if (Count != 1) {
|
|
continue;
|
|
}
|
|
*IsHex = TRUE;
|
|
} else if (Char >= '0' && Char <= '9') {
|
|
;
|
|
} else if (*IsHex && ((Char >= 'a' && Char <= 'f') || (Char >= 'A' && Char <= 'F'))) {
|
|
;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// Echo character back and save character in buffer
|
|
ShellPrint (L"%c", Char);
|
|
Buffer[Count++] = Char;
|
|
}
|
|
|
|
ShellPrint (L"\n");
|
|
TrimSpaces (&Buffer);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Find shell command by name.
|
|
|
|
@param[in] Shell shell instance
|
|
@param[in] Name name of command
|
|
@param[in] Ptr pointer to string pointer
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_NOT_FOUND
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FindShellCommand (
|
|
IN SHELL *Shell,
|
|
IN CONST CHAR16 *Name,
|
|
IN CONST SHELL_COMMAND **Ptr
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
SHELL_COMMAND_LIST_ENTRY *Entry;
|
|
LIST_ENTRY *EntryList;
|
|
|
|
//
|
|
// Add '?' alias for help
|
|
//
|
|
if (Name[0] == '?') {
|
|
Name = L"help";
|
|
}
|
|
|
|
EntryList = &Shell->CommandEntryList;
|
|
for (Link = EntryList->ForwardLink; Link != EntryList; Link = Link->ForwardLink) {
|
|
Entry = CR (Link, SHELL_COMMAND_LIST_ENTRY, Link, SHELL_COMMAND_LIST_ENTRY_SIGNATURE);
|
|
if (StrCmp (Name, Entry->ShellCommand->Name) == 0) {
|
|
*Ptr = Entry->ShellCommand;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Process and run a command line.
|
|
|
|
@param[in] Shell shell instance
|
|
@param[in] CmdLine command line to parse
|
|
|
|
@retval EFI_SUCCESS command was completed
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
RunShellCommand (
|
|
IN SHELL *Shell,
|
|
IN CONST CHAR16 *CmdLine
|
|
)
|
|
{
|
|
CHAR16 Buf[MAX_COMMAND_LINE_LEN];
|
|
CHAR16 *Ptr;
|
|
CONST CHAR16 *Walker;
|
|
UINTN Index, Count, BufRemaining;
|
|
EFI_STATUS Status;
|
|
CONST SHELL_COMMAND *Cmd;
|
|
CHAR16 *Argv[8]; // FIXME: Use allocator once that's available
|
|
|
|
// Parse the command line parameters
|
|
BufRemaining = Shell->CommandLineMaxLen * sizeof(CHAR16);
|
|
Ptr = Buf;
|
|
Walker = CmdLine;
|
|
|
|
for (Count = 0; Walker != NULL && *Walker != CHAR_NULL; Count++) {
|
|
Status = GetNextParameter (&Walker, &Ptr, BufRemaining, TRUE);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
BufRemaining -= StrSize (Ptr);
|
|
Ptr += StrLen (Ptr) + 1;
|
|
}
|
|
|
|
if (Count == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
ASSERT (Count <= (sizeof (Argv) / sizeof (Argv[0])));
|
|
for (Index = 0, Ptr = Buf; Index < Count; Index++) {
|
|
Argv[Index] = Ptr;
|
|
Ptr += StrLen (Ptr) + 1;
|
|
}
|
|
|
|
// Locate and execute command
|
|
Cmd = NULL;
|
|
Status = FindShellCommand (Shell, Argv[0], &Cmd);
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_NOT_FOUND) {
|
|
ShellPrint (L"Command not found (try help)\n");
|
|
}
|
|
} else {
|
|
ASSERT (Cmd);
|
|
Status = Cmd->Entry (Shell, Count, Argv);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Prompt user for command, receive command, run command.
|
|
|
|
@param[in] Shell shell instance
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval RETURN_ABORTED
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
ShellPrompt (
|
|
IN SHELL *Shell
|
|
)
|
|
{
|
|
CHAR16 CommandLine[MAX_COMMAND_LINE_LEN];
|
|
EFI_STATUS Status;
|
|
|
|
ASSERT (Shell != NULL);
|
|
ShellPrint (L"\nShell> ");
|
|
|
|
Status = ReadShellCommand (Shell, CommandLine, Shell->CommandLineMaxLen * sizeof (CHAR16));
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
HistoryAdd (Shell, CommandLine);
|
|
|
|
return RunShellCommand (Shell, CommandLine);
|
|
}
|
|
|
|
/**
|
|
The function is called by PerformInsertionSortList to sort Shell commands by its name.
|
|
|
|
@param[in] Buffer1 The pointer to first list entry.
|
|
@param[in] Buffer2 The pointer to second list entry.
|
|
|
|
@retval 0 Buffer1 name is less than Buffer2 name.
|
|
@retval 1 Buffer1 name is greater than or equal to Buffer2 name.
|
|
|
|
**/
|
|
INTN
|
|
EFIAPI
|
|
CompareCommandEntry (
|
|
IN CONST VOID *Buffer1,
|
|
IN CONST VOID *Buffer2
|
|
)
|
|
{
|
|
SHELL_COMMAND_LIST_ENTRY *NewEntry;
|
|
SHELL_COMMAND_LIST_ENTRY *CurrEntry;
|
|
|
|
NewEntry = BASE_CR (Buffer1, SHELL_COMMAND_LIST_ENTRY, Link);
|
|
CurrEntry = BASE_CR (Buffer2, SHELL_COMMAND_LIST_ENTRY, Link);
|
|
|
|
//
|
|
// Ascending Order
|
|
//
|
|
return StrCmp (NewEntry->ShellCommand->Name, CurrEntry->ShellCommand->Name) >= 0 ? 1: 0;
|
|
}
|
|
|
|
/**
|
|
Register a Shell Command
|
|
|
|
@param[in] Shell Shell Context
|
|
@param[in] ShellCommand A Shell Command to be registered
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_OUT_OF_RESOURCES
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ShellCommandRegister (
|
|
IN SHELL *Shell,
|
|
IN CONST SHELL_COMMAND *ShellCommand
|
|
)
|
|
{
|
|
SHELL_COMMAND_LIST_ENTRY *Entry;
|
|
LIST_ENTRY *EntryList;
|
|
|
|
Entry = (SHELL_COMMAND_LIST_ENTRY *)AllocateZeroPool (sizeof (SHELL_COMMAND_LIST_ENTRY));
|
|
if (Entry == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
InitializeListHead (&Entry->Link);
|
|
Entry->ShellCommand = ShellCommand;
|
|
Entry->Signature = SHELL_COMMAND_LIST_ENTRY_SIGNATURE;
|
|
|
|
EntryList = &Shell->CommandEntryList;
|
|
PerformInsertionSortList (EntryList, &Entry->Link, CompareCommandEntry);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Unregister all Shell commands
|
|
|
|
@param[in] Shell Shell Context
|
|
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ShellCommandUnRegisterAll (
|
|
IN SHELL *Shell
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
SHELL_COMMAND_LIST_ENTRY *Entry;
|
|
LIST_ENTRY *EntryList;
|
|
|
|
EntryList = &Shell->CommandEntryList;
|
|
for (Link = EntryList->ForwardLink; Link != EntryList;) {
|
|
Entry = CR (Link, SHELL_COMMAND_LIST_ENTRY, Link, SHELL_COMMAND_LIST_ENTRY_SIGNATURE);
|
|
Link = Link->ForwardLink;
|
|
FreePool (Entry);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Begin a run-time interactive shell.
|
|
|
|
@param[in] Timeout seconds to wait for input before returning (0 for no timeout)
|
|
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Shell (
|
|
IN UINTN Timeout
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Start;
|
|
UINTN Index;
|
|
UINTN Index1;
|
|
SHELL Shell;
|
|
UINT8 Buffer;
|
|
|
|
ZeroMem (&Shell, sizeof (Shell));
|
|
InitializeListHead (&Shell.CommandEntryList);
|
|
|
|
Shell.CommandLineMaxLen = FeaturePcdGet (PcdMiniShellEnabled) ? \
|
|
MAX_COMMAND_LINE_LEN_MINI_SHELL : MAX_COMMAND_LINE_LEN;
|
|
|
|
LoadShellCommands (&Shell);
|
|
Shell.ShouldExit = FALSE;
|
|
|
|
if (Timeout != 0) {
|
|
ShellPrint (L"\n");
|
|
while (ConsolePoll ()) {
|
|
ConsoleRead (&Buffer, 1);
|
|
}
|
|
for (Index = Timeout; Index > 0; Index--) {
|
|
ShellPrint (L"Press any key within %d second(s) to enter the command shell", Index);
|
|
for (Index1 = 0; Index1 < 10; Index1++) {
|
|
Start = ConsolePoll ();
|
|
if (Start) {
|
|
break;
|
|
}
|
|
MicroSecondDelay (100 * 1000);
|
|
}
|
|
ShellPrint(L"\r");
|
|
if (Start) {
|
|
break;
|
|
}
|
|
}
|
|
ShellPrint (L"\n");
|
|
if (!Start) {
|
|
Status = EFI_ABORTED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
HistoryInit (&Shell, TRUE);
|
|
|
|
while (! (Shell.ShouldExit)) {
|
|
ShellPrompt (&Shell);
|
|
}
|
|
|
|
HistoryInit (&Shell, FALSE);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Exit:
|
|
ShellCommandUnRegisterAll (&Shell);
|
|
|
|
return Status;
|
|
}
|