slimbootloader/PayloadPkg/OsLoader/BootConfig.c

370 lines
8.7 KiB
C

/** @file
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
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 "OsLoader.h"
/**
Get next line start pointer.
@param[in] Start The pointer for current line.
@param[out] Length The UINT32 pointer to receive the length of current line.
@retval The pointer for next line.
NULL if no more line is available.
**/
STATIC
CHAR8 *
GetNextLine (
IN CHAR8 *Start,
OUT UINT32 *Length
)
{
CHAR8 *Ptr;
Ptr = Start;
while ((Ptr[0] != 0) && (Ptr[0] != '\n')) {
Ptr++;
}
*Length = Ptr - Start;
if (Ptr[0] == 0) {
return NULL;
} else {
return Ptr + 1;
}
}
/**
Trim leading white space for a line.
@param[in] Line The pointer to the line buffer.
@retval The pointer to the trimmed line.
**/
STATIC
CHAR8 *
TrimLeft (
IN CHAR8 *Line
)
{
while ((Line[0] == ' ') || (Line[0] == '\t')) {
Line++;
}
return Line;
}
/**
Get next space for the start of a line.
@param[in] LineStart The pointer to the start of the line buffer.
@param[in] LineEnd The pointer to the end of the line buffer.
@retval The pointer to the trimmed line.
**/
STATIC
CHAR8 *
GetNextSpace (
IN CHAR8 *LineStart,
IN CHAR8 *LineEnd
)
{
CHAR8 *Line;
Line = LineStart;
while ((Line[0] != 0) && (Line[0] != ' ') && (Line[0] != '\t') && (Line < LineEnd)) {
Line++;
}
return Line;
}
/**
Trim trailing white space for a line.
@param[in] Line The pointer to the line buffer.
@retval The pointer to the trimmed line.
**/
STATIC
CHAR8 *
TrimRight (
IN CHAR8 *Line
)
{
while ((Line[0] == ' ') || (Line[0] == '\t') || (Line[0] == '\r')) {
Line--;
}
return Line;
}
/**
Check if a line starts with a known keyword.
@param[in] Line The pointer to the line buffer.
@param[in] Keyword The pointer to the keyword.
@retval Length of the matched keyword.
0 if there is no match.
**/
STATIC
UINT32
MatchKeyWord (
IN CHAR8 *Line,
IN CHAR8 *Keyword
)
{
UINT32 KeywordLen;
KeywordLen = AsciiStrLen (Keyword);
if (AsciiStrnCmp (Line, Keyword, KeywordLen) == 0) {
// A separator is required to follow the keyword
if (((Line[KeywordLen] == 0)) || (Line[KeywordLen] == ' ') || (Line[KeywordLen] == '\t')) {
return KeywordLen;
}
}
return 0;
}
/**
Check if a line matches varaible assignment syntax.
@param[in] Line The pointer to the line buffer.
@param[in] Variable The pointer to the variable name.
@retval Length of the matched variable name.
0 if there is no match.
**/
STATIC
UINT32
MatchAssignment (
IN CHAR8 *Line,
IN CHAR8 *Variable
)
{
UINT32 VarLen;
VarLen = AsciiStrLen (Variable);
if (AsciiStrnCmp (Line, Variable, VarLen) == 0) {
if (Line[VarLen] == '=') {
return VarLen;
}
}
return 0;
}
/**
Parse the Linux boot configuration file.
@param[in] CfgBuffer The configuration buffer.
@param[out] LinuxBootCfg The pointer to hold the parse results.
**/
VOID
ParseLinuxBootConfig (
IN CHAR8 *CfgBuffer,
OUT LINUX_BOOT_CFG *LinuxBootCfg
)
{
CHAR8 *CurrLine;
CHAR8 *NextLine;
CHAR8 *StartLine;
CHAR8 *EndLine;
MENU_ENTRY *MenuEntry;
UINT32 Idx;
UINT32 LineLen;
INT32 EntryNum;
MenuEntry = LinuxBootCfg->MenuEntry;
EntryNum = -1;
CurrLine = CfgBuffer;
while (CurrLine != NULL) {
NextLine = GetNextLine (CurrLine, &LineLen);
EndLine = CurrLine + LineLen;
CurrLine = TrimLeft (CurrLine);
if (MatchKeyWord (CurrLine, "set") > 0) {
CurrLine += 3;
// Handle "set" varaible statement
// Only support "timeout" and "default" now
CurrLine = TrimLeft (CurrLine);
StartLine = CurrLine;
while ((CurrLine[0] != 0) && (CurrLine[0] != '=') && (CurrLine < EndLine)) {
CurrLine++;
}
if (CurrLine[0] == '=') {
CurrLine++;
if (MatchAssignment (StartLine, "timeout") > 0) {
LinuxBootCfg->Settings.Timeout = AsciiStrDecimalToUintn (CurrLine);
} else if (MatchAssignment (StartLine, "default") > 0) {
if (CurrLine[0] == '"') {
CurrLine++;
}
// Support boot option number string only
LinuxBootCfg->Settings.Default = AsciiStrDecimalToUintn (CurrLine);
}
}
} else if (MatchKeyWord (CurrLine, "menuentry") > 0) {
if (EntryNum >= MAX_BOOT_MENU_ENTRY) {
NextLine = NULL;
break;
}
// Mark boot option name
EntryNum++;
CurrLine += 9;
CurrLine = TrimLeft (CurrLine);
for (Idx = 0; Idx < 2; Idx++) {
while ((CurrLine[0] != 0) && (CurrLine[0] != '"') && (CurrLine[0] != '\'') && (CurrLine < EndLine)) {
CurrLine++;
}
if ((CurrLine[0] == '"') || (CurrLine[0] == '\'')) {
if (Idx == 0) {
MenuEntry[EntryNum].Name.Pos = CurrLine - CfgBuffer + 1;
} else {
MenuEntry[EntryNum].Name.Len = CurrLine - CfgBuffer - MenuEntry[EntryNum].Name.Pos;
}
CurrLine++;
}
}
} else if (MatchKeyWord (CurrLine, "linux") > 0) {
CurrLine += 5;
// Mark kernel path
CurrLine = TrimLeft (CurrLine);
MenuEntry[EntryNum].Kernel.Pos = CurrLine - CfgBuffer;
CurrLine = GetNextSpace (CurrLine, EndLine);
MenuEntry[EntryNum].Kernel.Len = CurrLine - CfgBuffer - MenuEntry[EntryNum].Kernel.Pos;
// Mark command line
CurrLine = TrimLeft (CurrLine);
MenuEntry[EntryNum].Command.Pos = CurrLine - CfgBuffer;
EndLine = TrimRight (EndLine);
MenuEntry[EntryNum].Command.Len = EndLine - CfgBuffer - MenuEntry[EntryNum].Command.Pos;
} else if (MatchKeyWord (CurrLine, "initrd") > 0) {
CurrLine += 6;
// Mark initrd path
CurrLine = TrimLeft (CurrLine);
MenuEntry[EntryNum].InitRd.Pos = CurrLine - CfgBuffer;
CurrLine = GetNextSpace (CurrLine, EndLine);
MenuEntry[EntryNum].InitRd.Len = CurrLine - CfgBuffer - MenuEntry[EntryNum].InitRd.Pos;
}
CurrLine = NextLine;
}
if (EntryNum >= 0) {
EntryNum++;
} else {
EntryNum = 0;
}
// Make sure the settings are reasonable
LinuxBootCfg->EntryNum = EntryNum;
if (LinuxBootCfg->Settings.Default >= (UINT32)EntryNum) {
LinuxBootCfg->Settings.Default = 0;
}
if (LinuxBootCfg->Settings.Timeout >= 60) {
LinuxBootCfg->Settings.Timeout = 3;
}
}
/**
Print Linux boot options.
@param[in] CfgBuffer The configuration buffer.
@param[out] LinuxBootCfg The pointer to hold the parse results.
**/
VOID
PrintLinuxBootConfig (
CHAR8 *CfgBuffer,
LINUX_BOOT_CFG *LinuxBootCfg
)
{
UINTN Idx;
CHAR8 *CurrLine;
UINT32 LineLen;
CHAR8 Val;
ShellPrint (L"\nBoot Menu:\n");
for (Idx = 0; Idx < LinuxBootCfg->EntryNum; Idx++) {
if (LinuxBootCfg->MenuEntry[Idx].Name.Buf[0] == 0) {
CurrLine = CfgBuffer + LinuxBootCfg->MenuEntry[Idx].Name.Pos;
} else {
CurrLine = LinuxBootCfg->MenuEntry[Idx].Name.Buf;
}
LineLen = LinuxBootCfg->MenuEntry[Idx].Name.Len;
Val = CfgBuffer[LineLen];
CurrLine[LineLen] = 0;
ShellPrint (L" %d - %c %a\n", Idx, (Idx == LinuxBootCfg->Settings.Default) ? '*' : ' ', CurrLine);
CurrLine[LineLen] = Val;
}
}
/**
Get Linux boot option from user input.
@param[in] CfgBuffer The configuration buffer.
@param[out] LinuxBootCfg The pointer to hold the parse results.
@retval 0 based boot option index.
**/
UINT32
GetLinuxBootOption (
CHAR8 *CfgBuffer,
LINUX_BOOT_CFG *LinuxBootCfg
)
{
UINT32 EntryIdx;
UINT32 Timeout;
UINT32 Index;
UINT8 Key;
EntryIdx = LinuxBootCfg->Settings.Default;
Timeout = LinuxBootCfg->Settings.Timeout;
if ((Timeout > 0) && (LinuxBootCfg->EntryNum > 0)) {
ShellPrint (L"Press '0' ~ '%d' to select boot option: ", LinuxBootCfg->EntryNum - 1);
for (Index = 0; Index < Timeout * 100; Index++) {
if (ConsolePoll ()) {
if (ConsoleRead (&Key, 1) > 0) {
if ((Key >= '0') && (Key < '0' + LinuxBootCfg->EntryNum)) {
ShellPrint (L"%c", Key);
EntryIdx = Key - '0';
break;
}
}
}
MicroSecondDelay (10 * 1000);
}
ShellPrint (L"\nBoot from option %d\n", EntryIdx);
}
return EntryIdx;
}