diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 63738e0c97b..385ffa53fc1 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -6920,7 +6920,8 @@ \subsubsection{Configuration} \begin{itemize} \tightlist \item \texttt{LINUX\_BOOT\_ADD\_RW}, - \item \texttt{LINUX\_BOOT\_LOG\_VERBOSE} and + \item \texttt{LINUX\_BOOT\_LOG\_VERBOSE}, + \item \texttt{LINUX\_BOOT\_LOG\_GRUB\_VARS} and \item \texttt{LINUX\_BOOT\_ADD\_DEBUG\_INFO}. \end{itemize} \medskip @@ -7001,6 +7002,17 @@ \subsubsection{Configuration} partition's unique partition uuid, to each generated entry name. Can help with debugging the origin of entries generated by the driver when there are multiple Linux installs on one system. + \item \texttt{0x00010000} (bit \texttt{16}) --- \texttt{LINUX\_BOOT\_LOG\_GRUB\_VARS}, + When a \texttt{BootLoaderSpecByDefault} setup is detected, log available GRUB variables + found in \texttt{grub2/grubenv} and \texttt{grub2/grub.cfg}. + \item \texttt{0x00020000} (bit \texttt{17}) --- \texttt{LINUX\_BOOT\_FIX\_TUNED}, + In some circumstances, such as after upgrades which add TuneD to existing systems, the TuneD + system tuning plugin may add its GRUB variables to \texttt{loader/entries/*.conf} files but not + initialise them in \texttt{grub2/grub.cfg}. In order to avoid incorrect boots, OpenLinuxBoot + treats used non-initialised GRUB variables as an error. When this flag is set, empty values + are added for the TuneD variables \texttt{tuned\_params} and \texttt{tuned\_initrd} if they + are not present. This is required for OpenLinuxBoot on TuneD systems with this problem, and + harmless otherwise. \end{itemize} \medskip Flag values can be specified in hexadecimal beginning with \texttt{0x} or in decimal, @@ -7048,7 +7060,7 @@ \subsubsection{Additional information} OpenLinuxBoot can detect the \texttt{loader/entries/*.conf} files created according to the \href{https://systemd.io/BOOT_LOADER_SPECIFICATION/}{Boot Loader Specification} or the closely related -\href{https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault}{systemd BootLoaderSpecByDefault}. The +\href{https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault}{Fedora BootLoaderSpecByDefault}. The former is specific to systemd-boot and is used by Arch Linux, the latter applies to most Fedora-related distros including Fedora itself, RHEL and variants. @@ -7064,13 +7076,13 @@ \subsubsection{Additional information} \texttt{autoopts:\{partuuid\}=...} (\texttt{+=} variants of these options will not work, as these only add additional arguments). -BootLoaderSpecByDefault (but not pure Boot Loader Specification) can expand GRUB variables +Fedora \texttt{BootLoaderSpecByDefault} (but not pure Boot Loader Specification) can expand GRUB variables in the \texttt{*.conf} files -- and this is used in practice in certain distros such as CentOS. In order to handle this correctly, when this situation is detected OpenLinuxBoot extracts all variables from \texttt{\{boot\}/grub2/grubenv} and also any unconditionally set variables from \texttt{\{boot\}/grub2/grub.cfg}, and then expands these where required in \texttt{*.conf} file entries. -The only currently supported method of starting Linux kernels relies on their being compiled with EFISTUB. +The only currently supported method of starting Linux kernels from OpenLinuxBoot relies on their being compiled with EFISTUB. This applies to almost all modern distros, particularly those which use systemd. Note that most modern distros use systemd as their system manager, even though most do not use systemd-boot as their bootloader. diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index 9c49cec5129..e1f717d7654 100644 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -1926,7 +1926,7 @@ OcParseLoadOptions ( /** Parse Unix-style var file or string. Parses a couple of useful ASCII - GRUB config files (multi-line, name=var, with optinal comments) and + GRUB config files (multi-line, name=var, with optional comments) and defines a standard format for Unicode UEFI LoadOptions. Assumes CHAR_NULL terminated Unicode string of space separated options, @@ -1940,9 +1940,10 @@ OcParseLoadOptions ( @param[in] StrVars Raw var string. @param[out] ParsedVars Parsed variables if successful, NULL otherwise. - Caller may free after use with OcFlexArrayFree - if required. + Caller may free after use with OcFlexArrayFree. @param[in] StringFormat Are option names and values Unicode or ASCII? + @param[in] TokensOnly If TRUE parse as a sequence of token values only, + rather than as a sequence of name[=[value]] pairs. @retval EFI_SUCCESS Success. @retval EFI_NOT_FOUND Missing or empty load options. @@ -1953,7 +1954,8 @@ EFI_STATUS OcParseVars ( IN VOID *StrVars, OUT OC_FLEX_ARRAY **ParsedVars, - IN CONST OC_STRING_FORMAT StringFormat + IN CONST OC_STRING_FORMAT StringFormat, + IN CONST BOOLEAN TokensOnly ); /** diff --git a/Include/Acidanthera/Library/OcStringLib.h b/Include/Acidanthera/Library/OcStringLib.h index 45e22d41d3c..caf25e7efb1 100644 --- a/Include/Acidanthera/Library/OcStringLib.h +++ b/Include/Acidanthera/Library/OcStringLib.h @@ -742,4 +742,16 @@ OcIsSpace ( CHAR16 Ch ); +/** + Determine if a particular character is whitespace or CHAR_NULL. + + @param[in] Ch The character to check. + + @return Returns TRUE if Ch is a whitespace character or CHAR_NULL. +**/ +BOOLEAN +OcIsSpaceOrNull ( + CHAR16 Ch + ); + #endif // OC_STRING_LIB_H diff --git a/Library/OcBootManagementLib/BootArguments.c b/Library/OcBootManagementLib/BootArguments.c index 9b6e02f7786..ba170c5889c 100644 --- a/Library/OcBootManagementLib/BootArguments.c +++ b/Library/OcBootManagementLib/BootArguments.c @@ -1,5 +1,5 @@ /** @file - Copyright (C) 2019-2021, vit9696, mikebeaton. All rights reserved.
+ Copyright (C) 2019-2024, vit9696, mikebeaton. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause **/ @@ -24,19 +24,19 @@ typedef enum PARSE_VARS_STATE_ { PARSE_VARS_WHITE_SPACE, PARSE_VARS_COMMENT, PARSE_VARS_NAME, - PARSE_VARS_VALUE_FIRST, PARSE_VARS_VALUE, PARSE_VARS_QUOTED_VALUE, PARSE_VARS_SHELL_EXPANSION } PARSE_VARS_STATE; // -// Shift from token start to current position forwards by offset characters. +// Shift memory from token start to current position forwards by offset bytes +// and update token to point to shifted start (thereby discarding offset bytes +// fromec the end of the token). // #define SHIFT_TOKEN(pos, token, offset) do {\ CopyMem ((UINT8 *)(token) + (offset), (token), (UINT8 *)(pos) - (UINT8 *)(token)); \ (token) = (UINT8 *)(token) + (offset); \ - (pos) = (UINT8 *)(pos) + (offset); \ } while (0) VOID @@ -434,7 +434,7 @@ OcParseLoadOptions ( return EFI_NOT_FOUND; } - Status = OcParseVars (LoadedImage->LoadOptions, ParsedVars, OcStringFormatUnicode); + Status = OcParseVars (LoadedImage->LoadOptions, ParsedVars, OcStringFormatUnicode, FALSE); if (Status == EFI_INVALID_PARAMETER) { DEBUG ((DEBUG_ERROR, "OCB: Failed to parse LoadOptions (%p:%u)\n", LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize)); @@ -449,16 +449,20 @@ EFI_STATUS OcParseVars ( IN VOID *StrVars, OUT OC_FLEX_ARRAY **ParsedVars, - IN CONST OC_STRING_FORMAT StringFormat + IN CONST OC_STRING_FORMAT StringFormat, + IN CONST BOOLEAN TokensOnly ) { VOID *Pos; + VOID *NewPos; PARSE_VARS_STATE State; PARSE_VARS_STATE PushState; - BOOLEAN Retake; CHAR16 Ch; + CHAR16 NewCh; + CHAR16 QuoteChar; VOID *Name; VOID *Value; + VOID *OriginalValue; OC_PARSED_VAR *Option; if ((StrVars == NULL) || ((StringFormat == OcStringFormatUnicode) ? (((CHAR16 *)StrVars)[0] == CHAR_NULL) : (((CHAR8 *)StrVars)[0] == '\0'))) { @@ -474,7 +478,7 @@ OcParseVars ( Pos = StrVars; State = PARSE_VARS_WHITE_SPACE; PushState = PARSE_VARS_WHITE_SPACE; - Retake = FALSE; + QuoteChar = CHAR_NULL; do { Ch = (StringFormat == OcStringFormatUnicode) ? *((CHAR16 *)Pos) : *((CHAR8 *)Pos); @@ -482,9 +486,23 @@ OcParseVars ( case PARSE_VARS_WHITE_SPACE: if (Ch == '#') { State = PARSE_VARS_COMMENT; - } else if (!(OcIsSpace (Ch) || (Ch == CHAR_NULL))) { - Name = Pos; - State = PARSE_VARS_NAME; + } else if (!OcIsSpaceOrNull (Ch)) { + if (TokensOnly) { + Option = OcFlexArrayAddItem (*ParsedVars); + if (Option == NULL) { + OcFlexArrayFree (ParsedVars); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Value-only token\n")); + + Value = Pos; + OriginalValue = Value; + State = PARSE_VARS_VALUE; + } else { + Name = Pos; + State = PARSE_VARS_NAME; + } } break; @@ -497,7 +515,7 @@ OcParseVars ( break; case PARSE_VARS_NAME: - if ((Ch == L'=') || OcIsSpace (Ch) || (Ch == CHAR_NULL)) { + if ((Ch == L'=') || OcIsSpaceOrNull (Ch)) { if (StringFormat == OcStringFormatUnicode) { *((CHAR16 *)Pos) = CHAR_NULL; } else { @@ -505,7 +523,9 @@ OcParseVars ( } if (Ch == L'=') { - State = PARSE_VARS_VALUE_FIRST; + State = PARSE_VARS_VALUE; + Value = (UINT8 *)Pos + ((StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); + OriginalValue = Value; } else { State = PARSE_VARS_WHITE_SPACE; } @@ -533,18 +553,6 @@ OcParseVars ( break; - case PARSE_VARS_VALUE_FIRST: - if (Ch == L'"') { - State = PARSE_VARS_QUOTED_VALUE; - Value = (UINT8 *)Pos + ((StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); - } else { - State = PARSE_VARS_VALUE; - Value = Pos; - Retake = TRUE; - } - - break; - case PARSE_VARS_SHELL_EXPANSION: if (Ch == '`') { ASSERT (PushState != PARSE_VARS_WHITE_SPACE); @@ -553,23 +561,39 @@ OcParseVars ( break; + // + // In token value (but not name) we handle sh and grub quoting and string concatenation, e.g. 'abc\'"'\""def becomes abc\'"def. + // case PARSE_VARS_VALUE: case PARSE_VARS_QUOTED_VALUE: - if (Ch == L'`') { + if ((State != PARSE_VARS_QUOTED_VALUE) && ((Ch == L'\'') || (Ch == L'"'))) { + QuoteChar = Ch; + SHIFT_TOKEN (Pos, Value, (StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); + State = PARSE_VARS_QUOTED_VALUE; + } else if ((State == PARSE_VARS_QUOTED_VALUE) && (Ch == QuoteChar)) { + SHIFT_TOKEN (Pos, Value, (StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); + QuoteChar = CHAR_NULL; + State = PARSE_VARS_VALUE; + } else if (((State != PARSE_VARS_QUOTED_VALUE) || (QuoteChar == L'"')) && (Ch == L'\\')) { + NewPos = (UINT8 *)Pos + ((StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); + NewCh = (StringFormat == OcStringFormatUnicode) ? *((CHAR16 *)Pos) : *((CHAR8 *)Pos); + // + // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html + // + if ((State != PARSE_VARS_QUOTED_VALUE) || (NewCh == '"') || (NewCh == '\\') || (NewCh == '$') || (NewCh == '`')) { + SHIFT_TOKEN (Pos, Value, (StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); + Pos = NewPos; + Ch = NewCh; + } + } else if (Ch == L'`') { PushState = State; State = PARSE_VARS_SHELL_EXPANSION; - } else if (Ch == L'\\') { - SHIFT_TOKEN (Pos, Value, (StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); - Ch = (StringFormat == OcStringFormatUnicode) ? *((CHAR16 *)Pos) : *((CHAR8 *)Pos); - } else if ( - ((State == PARSE_VARS_VALUE) && (OcIsSpace (Ch) || (Ch == CHAR_NULL))) || - ((State == PARSE_VARS_QUOTED_VALUE) && (Ch == '"'))) - { + } else if ((State == PARSE_VARS_VALUE) && OcIsSpaceOrNull (Ch)) { // - // Explicitly quoted empty string needs to be stored detectably - // differently from missing value. + // Explicitly quoted empty string (e.g. `var=""`) is stored detectably differently from missing value (i.e. `var=`, or just `var`). // - if ((State != PARSE_VARS_QUOTED_VALUE) && (Pos == Value)) { + if (Pos == OriginalValue) { + ASSERT (!TokensOnly); DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No value %u\n", 2)); } else { if (PushState != PARSE_VARS_WHITE_SPACE) { @@ -588,9 +612,10 @@ OcParseVars ( } } - Value = NULL; - Option = NULL; - State = PARSE_VARS_WHITE_SPACE; + Value = NULL; + OriginalValue = NULL; + Option = NULL; + State = PARSE_VARS_WHITE_SPACE; } break; @@ -600,11 +625,7 @@ OcParseVars ( break; } - if (Retake) { - Retake = FALSE; - } else { - Pos = (UINT8 *)Pos + ((StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); - } + Pos = (UINT8 *)Pos + ((StringFormat == OcStringFormatUnicode) ? sizeof (CHAR16) : sizeof (CHAR8)); } while (Ch != CHAR_NULL); if ((State != PARSE_VARS_WHITE_SPACE) || (PushState != PARSE_VARS_WHITE_SPACE)) { diff --git a/Library/OcStringLib/OcUnicodeLib.c b/Library/OcStringLib/OcUnicodeLib.c index f123991c278..c2a6de18754 100644 --- a/Library/OcStringLib/OcUnicodeLib.c +++ b/Library/OcStringLib/OcUnicodeLib.c @@ -530,3 +530,11 @@ OcIsSpace ( { return (Ch == L' ') || (Ch == L'\t') || (Ch == L'\r') || (Ch == L'\n') || (Ch == L'\v') || (Ch == L'\f'); } + +BOOLEAN +OcIsSpaceOrNull ( + CHAR16 Ch + ) +{ + return (Ch == CHAR_NULL) || OcIsSpace (Ch); +} diff --git a/Platform/OpenLinuxBoot/Autodetect.c b/Platform/OpenLinuxBoot/Autodetect.c index 0dd76330c81..b4db7d43d3f 100644 --- a/Platform/OpenLinuxBoot/Autodetect.c +++ b/Platform/OpenLinuxBoot/Autodetect.c @@ -269,7 +269,7 @@ LoadOsRelease ( "LNX: Reading %s\n", OS_RELEASE_FILE )); - Status = OcParseVars (mEtcOsReleaseFileContents, &mEtcOsReleaseOptions, OcStringFormatAscii); + Status = OcParseVars (mEtcOsReleaseFileContents, &mEtcOsReleaseOptions, OcStringFormatAscii, FALSE); if (EFI_ERROR (Status)) { FreePool (mEtcOsReleaseFileContents); mEtcOsReleaseFileContents = NULL; @@ -320,7 +320,7 @@ LoadDefaultGrub ( "LNX: Reading %s\n", GRUB_DEFAULT_FILE )); - Status = OcParseVars (mEtcDefaultGrubFileContents, &mEtcDefaultGrubOptions, OcStringFormatAscii); + Status = OcParseVars (mEtcDefaultGrubFileContents, &mEtcDefaultGrubOptions, OcStringFormatAscii, FALSE); if (EFI_ERROR (Status)) { FreePool (mEtcDefaultGrubFileContents); mEtcDefaultGrubFileContents = NULL; diff --git a/Platform/OpenLinuxBoot/GrubVars.c b/Platform/OpenLinuxBoot/GrubVars.c index 0da488996df..c65fe6ac0c3 100644 --- a/Platform/OpenLinuxBoot/GrubVars.c +++ b/Platform/OpenLinuxBoot/GrubVars.c @@ -82,7 +82,7 @@ InternalSetGrubVar ( Var->Errors |= Errors; DEBUG (( - OC_TRACE_GRUB_VARS, + (gLinuxBootFlags & LINUX_BOOT_LOG_GRUB_VARS) == 0 ? DEBUG_VERBOSE : DEBUG_INFO, "LNX: Repeated %a=%a (0x%x->0x%x)\n", Key, Value, @@ -100,7 +100,7 @@ InternalSetGrubVar ( Var->Errors = Errors; DEBUG (( - OC_TRACE_GRUB_VARS, + (gLinuxBootFlags & LINUX_BOOT_LOG_GRUB_VARS) == 0 ? DEBUG_VERBOSE : DEBUG_INFO, "LNX: Added %a=%a (0x%x)\n", Key, Value, @@ -273,7 +273,13 @@ InternalExpandGrubVars ( } } - DEBUG ((OC_TRACE_GRUB_VARS, "LNX: Expanding '%a' => '%a' - %r\n", Value, *Result, Status)); + DEBUG (( + (gLinuxBootFlags & LINUX_BOOT_LOG_GRUB_VARS) == 0 ? DEBUG_VERBOSE : DEBUG_INFO, + "LNX: Expanding '%a' => '%a' - %r\n", + Value, + *Result, + Status + )); return Status; } diff --git a/Platform/OpenLinuxBoot/LinuxBootInternal.h b/Platform/OpenLinuxBoot/LinuxBootInternal.h index ae52d165db0..8401e73d875 100644 --- a/Platform/OpenLinuxBoot/LinuxBootInternal.h +++ b/Platform/OpenLinuxBoot/LinuxBootInternal.h @@ -6,10 +6,6 @@ #ifndef LINUX_BOOT_INTERNAL_H #define LINUX_BOOT_INTERNAL_H -#if !defined (OC_TRACE_GRUB_VARS) -#define OC_TRACE_GRUB_VARS DEBUG_VERBOSE -#endif - /* Standard attached drives on OVMF appear as MBR, so it can be convenient when debugging to allow entries with incorrect (i.e. specifies no/every drive) @@ -80,7 +76,8 @@ #define LINUX_BOOT_ADD_RW BIT11 /* - TODO: Both blspec-style and autodetect can make use of grub.cfg info if this flag is set. + TODO: (?) Both blspec-style and autodetect can make use of grub.cfg info if this flag is set. + These are currently parsed when needed for GRUB2+blscfg, i.e. if we find /loader/entries and /grub2/grub.cfg. */ // #define LINUX_BOOT_ALLOW_PARSE_GRUB BIT12 @@ -100,6 +97,16 @@ */ #define LINUX_BOOT_ADD_DEBUG_INFO BIT15 +/* + Trace grub var processing. +*/ +#define LINUX_BOOT_LOG_GRUB_VARS BIT16 + +/* + Fix TuneD processing by initialising its grub variables if they are not present. +*/ +#define LINUX_BOOT_FIX_TUNED BIT17 + #define LINUX_BOOT_ALL ( \ LINUX_BOOT_SCAN_ESP | \ LINUX_BOOT_SCAN_XBOOTLDR | \ @@ -112,7 +119,9 @@ LINUX_BOOT_ADD_RW | \ LINUX_BOOT_ALLOW_CONF_AUTO_ROOT | \ LINUX_BOOT_LOG_VERBOSE | \ - LINUX_BOOT_ADD_DEBUG_INFO \ + LINUX_BOOT_ADD_DEBUG_INFO | \ + LINUX_BOOT_LOG_GRUB_VARS | \ + LINUX_BOOT_FIX_TUNED \ ) /* diff --git a/Platform/OpenLinuxBoot/LoaderEntry.c b/Platform/OpenLinuxBoot/LoaderEntry.c index 34930cd4056..9a81b378558 100644 --- a/Platform/OpenLinuxBoot/LoaderEntry.c +++ b/Platform/OpenLinuxBoot/LoaderEntry.c @@ -22,6 +22,15 @@ #include +// +// Vars which require blank values if not present after GRUB2+blscfg var parsing, in order +// to fix up the fact that TuneD does not always initialise them. +// +STATIC CHAR8* mTuneDVars[] = { + "tuned_params", + "tuned_initrd" +}; + // // Root. // @@ -68,6 +77,10 @@ // #define MAX_LOADER_ENTRY_FILE_SIZE SIZE_4KB +// +// grub2/grub.cfg was found, we treat this as enough to show that /loader/entries +// we have found are a GRUB2+blscfg setup. +// STATIC BOOLEAN mIsGrub2; @@ -435,10 +448,6 @@ ExpandReplaceOptions ( GRUB_VAR *DefaultOptionsVar; if (Entry->Options->Count > 0) { - // - // Grub2 blscfg takes the first only. - // - ASSERT (Entry->Options->Count == 1); Status = InternalExpandGrubVarsForArray (Entry->Options); if (EFI_ERROR (Status)) { return Status; @@ -478,6 +487,61 @@ ExpandReplaceOptions ( return EFI_SUCCESS; } +// +// Expand grub vars, and expand multiple initrds per line to multiple initrd lines. +// Do this before checking for files, etc. +// +STATIC +EFI_STATUS +ExpandInitrds ( + IN OUT LOADER_ENTRY *Entry + ) +{ + EFI_STATUS Status; + UINTN OptionsIndex; + CHAR8 **Options; + OC_FLEX_ARRAY *ExpandedInitrds; + OC_FLEX_ARRAY *SplitInitrds; + UINTN SplitInitrdsIndex; + + if (Entry->Initrds->Count == 0) { + return EFI_SUCCESS; + } + + Status = InternalExpandGrubVarsForArray (Entry->Initrds); + if (EFI_ERROR (Status)) { + return Status; + } + + ExpandedInitrds = OcFlexArrayInit (sizeof (CHAR8 *), OcFlexArrayFreePointerItem); + + for (OptionsIndex = 0; OptionsIndex < Entry->Initrds->Count; OptionsIndex++) { + Options = OcFlexArrayItemAt (Entry->Initrds, OptionsIndex); + Status = OcParseVars (*Options, &SplitInitrds, OcStringFormatAscii, TRUE); + if (!EFI_ERROR (Status)) { + for (SplitInitrdsIndex = 0; SplitInitrdsIndex < SplitInitrds->Count; SplitInitrdsIndex++) { + Status = EntryCopyMultipleValue (FALSE, ExpandedInitrds, OcParsedVarsItemAt (SplitInitrds, SplitInitrdsIndex)->Ascii.Value); + if (EFI_ERROR (Status)) { + break; + } + } + OcFlexArrayFree (&SplitInitrds); + } + } + + if (EFI_ERROR (Status)) { + OcFlexArrayFree (&ExpandedInitrds); + return Status; + } + + ASSERT (ExpandedInitrds->Count >= Entry->Initrds->Count); + + OcFlexArrayFree (&Entry->Initrds); + Entry->Initrds = ExpandedInitrds; + + return EFI_SUCCESS; +} + STATIC EFI_STATUS DoFilterLoaderEntry ( @@ -696,7 +760,7 @@ HasRootOption ( return TRUE; } - Status = OcParseVars (OptionCopy, &ParsedVars, OcStringFormatAscii); + Status = OcParseVars (OptionCopy, &ParsedVars, OcStringFormatAscii, FALSE); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "LNX: Error parsing Options[%u]=<%a> - %r\n", Index, *Option, Status)); FreePool (OptionCopy); @@ -804,6 +868,20 @@ DoProcessLoaderEntry ( return EFI_NOT_FOUND; } + if (mIsGrub2) { + Status = ExpandReplaceOptions (Entry); + if (!EFI_ERROR (Status)) { + Status = ExpandInitrds (Entry); + } + if (EFI_ERROR (Status)) { + OcFlexArrayDiscardItem (gLoaderEntries, TRUE); + return Status; + } + } + + // + // Check all files exist, see comment on FindLoaderFile. + // Status = FindLoaderFile (Directory, DirName, &Entry->Linux); if (!EFI_ERROR (Status)) { for (Index = 0; Index < Entry->Initrds->Count; Index++) { @@ -820,14 +898,6 @@ DoProcessLoaderEntry ( return Status; } - if (mIsGrub2) { - Status = ExpandReplaceOptions (Entry); - if (EFI_ERROR (Status)) { - OcFlexArrayDiscardItem (gLoaderEntries, TRUE); - return Status; - } - } - // // Need to understand other reasons to apply this fix (if any), // so not automatically applying unless we recognise the layout. @@ -889,6 +959,30 @@ ProcessLoaderEntry ( return EFI_SUCCESS; } +STATIC +EFI_STATUS +FixTuneDVars ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + + STATIC_ASSERT (ARRAY_SIZE (mTuneDVars) > 0, "No TuneD vars to set"); + + Status = EFI_SUCCESS; + for (Index = 0; Index < ARRAY_SIZE (mTuneDVars); Index++) { + if (InternalGetGrubVar (mTuneDVars[Index]) == NULL) { + Status = InternalSetGrubVar (mTuneDVars[Index], "", VAR_ERR_NONE); + if (EFI_ERROR (Status)) { + break; + } + } + } + + return Status; +} + STATIC EFI_STATUS ScanLoaderEntriesAtDirectory ( @@ -915,7 +1009,7 @@ ScanLoaderEntriesAtDirectory ( gLoaderEntries = NULL; // - // Only treat as GRUB2 if grub2/grub.cfg exists. + // Treat /loader/entries as being GRUB2+blscfg style if /grub2/grub.cfg exists. // GrubCfg = OcReadFileFromDirectory (RootDirectory, GRUB2_GRUB_CFG, NULL, 0); if (GrubCfg == NULL) { @@ -947,15 +1041,23 @@ ScanLoaderEntriesAtDirectory ( )); Status = InternalProcessGrubCfg (GrubCfg); } + + if ( !EFI_ERROR (Status) + && ((gLinuxBootFlags & LINUX_BOOT_FIX_TUNED) != 0)) + { + DEBUG (( + (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO, + "LNX: Fix TuneD vars\n" + )); + Status = FixTuneDVars (); + } } } // // If we are grub2 and $early_initrd exists, then warn and halt (blscfg logic is to use it). - // Would not be hard to implement if required. This is a space separated list of filenames - // to use first as initrds. - // Note: they are filenames only, so (following how blscfg module does it) we need to prepend - // the path of either another (the first) initrd or the (first) vmlinuz. + // Would not be hard to implement in ExpandInitrds if required. This is a space separated list + // of filenames to use first as initrds. // if (!EFI_ERROR (Status)) { if (mIsGrub2) { diff --git a/Platform/OpenLinuxBoot/OpenLinuxBoot.c b/Platform/OpenLinuxBoot/OpenLinuxBoot.c index 23cef3de56d..4dec7143b49 100644 --- a/Platform/OpenLinuxBoot/OpenLinuxBoot.c +++ b/Platform/OpenLinuxBoot/OpenLinuxBoot.c @@ -21,7 +21,7 @@ #include -UINTN gLinuxBootFlags = LINUX_BOOT_ALL & ~(LINUX_BOOT_ADD_DEBUG_INFO | LINUX_BOOT_LOG_VERBOSE | LINUX_BOOT_ADD_RW); +UINTN gLinuxBootFlags = LINUX_BOOT_ALL & ~(LINUX_BOOT_ADD_DEBUG_INFO | LINUX_BOOT_LOG_VERBOSE | LINUX_BOOT_LOG_GRUB_VARS | LINUX_BOOT_ADD_RW); STATIC OC_FLEX_ARRAY *mParsedLoadOptions;