From: Laszlo Ersek Date: Fri, 24 Apr 2020 07:53:47 +0000 (+0200) Subject: OvmfPkg: introduce QemuFwCfgSimpleParserLib X-Git-Tag: edk2-stable202005~132 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;ds=sidebyside;h=611c7f1101267d53b039a04136e55f313c756c5c;p=mirror_edk2.git OvmfPkg: introduce QemuFwCfgSimpleParserLib We already parse some boolean and integer values from named fw_cfg files (usually into PCDs), and we're going to cover more. Add a dedicated library for centralizing the parsing logic. Cc: Ard Biesheuvel Cc: Jordan Justen Cc: Per Sundstrom Cc: Philippe Mathieu-Daudé Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2681 Signed-off-by: Laszlo Ersek Message-Id: <20200424075353.8489-2-lersek@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Ard Biesheuvel --- diff --git a/OvmfPkg/Include/Library/QemuFwCfgSimpleParserLib.h b/OvmfPkg/Include/Library/QemuFwCfgSimpleParserLib.h new file mode 100644 index 0000000000..c6062bae87 --- /dev/null +++ b/OvmfPkg/Include/Library/QemuFwCfgSimpleParserLib.h @@ -0,0 +1,128 @@ +/** @file + Parse the contents of named fw_cfg files as simple (scalar) data types. + + Copyright (C) 2020, Red Hat, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef QEMU_FW_CFG_SIMPLE_PARSER_LIB_H_ +#define QEMU_FW_CFG_SIMPLE_PARSER_LIB_H_ + +#include + +/** + Look up FileName with QemuFwCfgFindFile() from QemuFwCfgLib. Read the fw_cfg + file into a small array with automatic storage duration. Parse the array as + the textual representation of a BOOLEAN. + + @param[in] FileName The name of the fw_cfg file to look up and parse. + + @param[out] Value On success, Value is TRUE if the contents of the fw_cfg + file case-insensitively match "true", "yes", "y", + "enable", "enabled", "1". + + On success, Value is FALSE if the contents of the fw_cfg + file case-insensitively match "false", "no", "n", + "disable", "disabled", "0". + + On failure, Value is not changed. + + @retval RETURN_SUCCESS Parsing successful. Value has been set. + + @retval RETURN_UNSUPPORTED Firmware configuration is unavailable. + + @retval RETURN_PROTOCOL_ERROR Parsing failed. Value has not been changed. + + @return Error codes propagated from + QemuFwCfgFindFile(). Value has not been + changed. +**/ +RETURN_STATUS +EFIAPI +QemuFwCfgParseBool ( + IN CONST CHAR8 *FileName, + OUT BOOLEAN *Value + ); + +/** + Look up FileName with QemuFwCfgFindFile() from QemuFwCfgLib. Read the fw_cfg + file into a small array with automatic storage duration. Parse the array as + the textual representation of a UINT8. + + @param[in] FileName The name of the fw_cfg file to look up and parse. + + @param[in] ParseAsHex If TRUE, call BaseLib's AsciiStrHexToUint64S() for + parsing the fw_cfg file. + + If FALSE, call BaseLib's AsciiStrDecimalToUint64S() + for parsing the fw_cfg file. + + @param[out] Value On success, Value has been parsed with the BaseLib + function determined by ParseAsHex, and also + range-checked for [0, MAX_UINT8]. + + On failure, Value is not changed. + + @retval RETURN_SUCCESS Parsing successful. Value has been set. + + @retval RETURN_UNSUPPORTED Firmware configuration is unavailable. + + @retval RETURN_PROTOCOL_ERROR Parsing failed. Value has not been changed. + + @retval RETURN_PROTOCOL_ERROR Parsing succeeded, but the result does not fit + in the [0, MAX_UINT8] range. Value has not + been changed. + + @return Error codes propagated from + QemuFwCfgFindFile() and from the BaseLib + function selected by ParseAsHex. Value has not + been changed. +**/ +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint8 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT8 *Value + ); + +// +// The following functions behave identically to QemuFwCfgParseUint8(), +// only their range checks use MAX_UINT16, MAX_UINT32, MAX_UINT64, MAX_UINTN, +// respectively. +// + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint16 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT16 *Value + ); + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint32 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT32 *Value + ); + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint64 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT64 *Value + ); + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUintn ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINTN *Value + ); + +#endif // QEMU_FW_CFG_SIMPLE_PARSER_LIB_H_ diff --git a/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c b/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c new file mode 100644 index 0000000000..5ca22fcf0d --- /dev/null +++ b/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c @@ -0,0 +1,398 @@ +/** @file + Parse the contents of named fw_cfg files as simple (scalar) data types. + + Copyright (C) 2020, Red Hat, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include + +// +// Size of the longest valid UINT64 string, including the terminating NUL. +// +#define UINT64_STRING_MAX_SIZE \ + MAX (sizeof "18446744073709551615", sizeof "0xFFFFFFFFFFFFFFFF") + +// +// Size of the longest valid BOOL string (see the "mTrueString" and +// "mFalseString" arrays below), including the terminating NUL. +// +#define BOOL_STRING_MAX_SIZE (sizeof "disabled") + +// +// Length of "\r\n", not including the terminating NUL. +// +#define CRLF_LENGTH (sizeof "\r\n" - 1) + +// +// Words recognized as representing TRUE or FALSE. +// +STATIC CONST CHAR8 * CONST mTrueString[] = { + "true", "yes", "y", "enable", "enabled", "1" +}; +STATIC CONST CHAR8 * CONST mFalseString[] = { + "false", "no", "n", "disable", "disabled", "0" +}; + +// +// Helper functions. +// + +/** + Look up FileName with QemuFwCfgFindFile() from QemuFwCfgLib. Read the fw_cfg + file into the caller-provided CHAR8 array. NUL-terminate the array. + + @param[in] FileName The name of the fw_cfg file to look up and read. + + @param[in,out] BufferSize On input, number of bytes available in Buffer. + + On output, the number of bytes that have been + stored to Buffer. + + On error, BufferSize is indeterminate. + + @param[out] Buffer The buffer to read the fw_cfg file into. If the + fw_cfg file contents are not NUL-terminated, then + a NUL character is placed into Buffer after the + fw_cfg file contents. + + On error, Buffer is indeterminate. + + @retval RETURN_SUCCESS Buffer has been populated with the fw_cfg file + contents. Buffer is NUL-terminated regardless + of whether the fw_cfg file itself was + NUL-terminated. + + @retval RETURN_UNSUPPORTED Firmware configuration is unavailable. + + @retval RETURN_PROTOCOL_ERROR The fw_cfg file does not fit into Buffer. + (This is considered a QEMU configuration + error; BufferSize is considered authoritative + for the contents of the fw_cfg file identified + by FileName.) + + @retval RETURN_PROTOCOL_ERROR The fw_cfg file contents are not themselves + NUL-terminated, and an extra NUL byte does not + fit into Buffer. (Again a QEMU configuration + error.) + + @return Error codes propagated from + QemuFwCfgFindFile(). +**/ +STATIC +RETURN_STATUS +QemuFwCfgGetAsString ( + IN CONST CHAR8 *FileName, + IN OUT UINTN *BufferSize, + OUT CHAR8 *Buffer + ) +{ + RETURN_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + + if (!QemuFwCfgIsAvailable ()) { + return RETURN_UNSUPPORTED; + } + + Status = QemuFwCfgFindFile (FileName, &FwCfgItem, &FwCfgSize); + if (RETURN_ERROR (Status)) { + return Status; + } + if (FwCfgSize > *BufferSize) { + return RETURN_PROTOCOL_ERROR; + } + + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, Buffer); + + // + // If Buffer is already NUL-terminated due to fw_cfg contents, we're done. + // + if (FwCfgSize > 0 && Buffer[FwCfgSize - 1] == '\0') { + *BufferSize = FwCfgSize; + return RETURN_SUCCESS; + } + // + // Otherwise, append a NUL byte to Buffer (if we have room for it). + // + if (FwCfgSize == *BufferSize) { + return RETURN_PROTOCOL_ERROR; + } + Buffer[FwCfgSize] = '\0'; + *BufferSize = FwCfgSize + 1; + return RETURN_SUCCESS; +} + +/** + Remove a trailing \r\n or \n sequence from a string. + + @param[in,out] BufferSize On input, the number of bytes in Buffer, including + the terminating NUL. + + On output, the adjusted string size (including the + terminating NUL), after stripping the \r\n or \n + suffix. + + @param[in,out] Buffer The NUL-terminated string to trim. +**/ +STATIC +VOID +StripNewline ( + IN OUT UINTN *BufferSize, + IN OUT CHAR8 *Buffer + ) +{ + UINTN InSize, OutSize; + + InSize = *BufferSize; + OutSize = InSize; + + if (InSize >= 3 && + Buffer[InSize - 3] == '\r' && Buffer[InSize - 2] == '\n') { + OutSize = InSize - 2; + } else if (InSize >= 2 && Buffer[InSize - 2] == '\n') { + OutSize = InSize - 1; + } + + if (OutSize < InSize) { + Buffer[OutSize - 1] = '\0'; + *BufferSize = OutSize; + } +} + +/** + Read the fw_cfg file identified by FileName as a string into a small array + with automatic storage duration, using QemuFwCfgGetAsString(). Parse the + string as a UINT64. Perform a range-check on the parsed value. + + @param[in] FileName The name of the fw_cfg file to look up and parse. + + @param[in] ParseAsHex If TRUE, call BaseLib's AsciiStrHexToUint64S() for + parsing the fw_cfg file. + + If FALSE, call BaseLib's AsciiStrDecimalToUint64S() + for parsing the fw_cfg file. + + @param[in] Limit The inclusive upper bound on the parsed UINT64 value. + + @param[out] Value On success, Value has been parsed with the BaseLib + function determined by ParseAsHex, and has been + range-checked against [0, Limit]. + + On failure, Value is not changed. + + @retval RETURN_SUCCESS Parsing successful. Value has been set. + + @retval RETURN_UNSUPPORTED Firmware configuration is unavailable. + + @retval RETURN_PROTOCOL_ERROR Parsing failed. Value has not been changed. + + @retval RETURN_PROTOCOL_ERROR Parsing succeeded, but the result does not fit + in the [0, Limit] range. Value has not been + changed. + + @return Error codes propagated from + QemuFwCfgFindFile() and from the BaseLib + function selected by ParseAsHex. Value has not + been changed. +**/ +STATIC +RETURN_STATUS +QemuFwCfgParseUint64WithLimit ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + IN UINT64 Limit, + OUT UINT64 *Value + ) +{ + UINTN Uint64StringSize; + CHAR8 Uint64String[UINT64_STRING_MAX_SIZE + CRLF_LENGTH]; + RETURN_STATUS Status; + CHAR8 *EndPointer; + UINT64 Uint64; + + Uint64StringSize = sizeof Uint64String; + Status = QemuFwCfgGetAsString (FileName, &Uint64StringSize, Uint64String); + if (RETURN_ERROR (Status)) { + return Status; + } + + StripNewline (&Uint64StringSize, Uint64String); + + if (ParseAsHex) { + Status = AsciiStrHexToUint64S (Uint64String, &EndPointer, &Uint64); + } else { + Status = AsciiStrDecimalToUint64S (Uint64String, &EndPointer, &Uint64); + } + if (RETURN_ERROR (Status)) { + return Status; + } + + // + // Report a wire protocol error if the subject sequence is empty, or trailing + // garbage is present, or Limit is not honored. + // + if (EndPointer == Uint64String || *EndPointer != '\0' || Uint64 > Limit) { + return RETURN_PROTOCOL_ERROR; + } + + *Value = Uint64; + return RETURN_SUCCESS; +} + +// +// Public functions. +// + +RETURN_STATUS +EFIAPI +QemuFwCfgSimpleParserInit ( + VOID + ) +{ + // + // Do nothing, just participate in constructor dependency ordering. + // + return RETURN_SUCCESS; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseBool ( + IN CONST CHAR8 *FileName, + OUT BOOLEAN *Value + ) +{ + UINTN BoolStringSize; + CHAR8 BoolString[BOOL_STRING_MAX_SIZE + CRLF_LENGTH]; + RETURN_STATUS Status; + UINTN Idx; + + BoolStringSize = sizeof BoolString; + Status = QemuFwCfgGetAsString (FileName, &BoolStringSize, BoolString); + if (RETURN_ERROR (Status)) { + return Status; + } + + StripNewline (&BoolStringSize, BoolString); + + for (Idx = 0; Idx < ARRAY_SIZE (mTrueString); ++Idx) { + if (AsciiStriCmp (BoolString, mTrueString[Idx]) == 0) { + *Value = TRUE; + return RETURN_SUCCESS; + } + } + + for (Idx = 0; Idx < ARRAY_SIZE (mFalseString); ++Idx) { + if (AsciiStriCmp (BoolString, mFalseString[Idx]) == 0) { + *Value = FALSE; + return RETURN_SUCCESS; + } + } + + return RETURN_PROTOCOL_ERROR; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint8 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT8 *Value + ) +{ + RETURN_STATUS Status; + UINT64 Uint64; + + Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT8, + &Uint64); + if (RETURN_ERROR (Status)) { + return Status; + } + *Value = (UINT8)Uint64; + return RETURN_SUCCESS; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint16 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT16 *Value + ) +{ + RETURN_STATUS Status; + UINT64 Uint64; + + Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT16, + &Uint64); + if (RETURN_ERROR (Status)) { + return Status; + } + *Value = (UINT16)Uint64; + return RETURN_SUCCESS; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint32 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT32 *Value + ) +{ + RETURN_STATUS Status; + UINT64 Uint64; + + Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT32, + &Uint64); + if (RETURN_ERROR (Status)) { + return Status; + } + *Value = (UINT32)Uint64; + return RETURN_SUCCESS; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUint64 ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINT64 *Value + ) +{ + RETURN_STATUS Status; + UINT64 Uint64; + + Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT64, + &Uint64); + if (RETURN_ERROR (Status)) { + return Status; + } + *Value = Uint64; + return RETURN_SUCCESS; +} + +RETURN_STATUS +EFIAPI +QemuFwCfgParseUintn ( + IN CONST CHAR8 *FileName, + IN BOOLEAN ParseAsHex, + OUT UINTN *Value + ) +{ + RETURN_STATUS Status; + UINT64 Uint64; + + Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINTN, + &Uint64); + if (RETURN_ERROR (Status)) { + return Status; + } + *Value = (UINTN)Uint64; + return RETURN_SUCCESS; +} diff --git a/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf b/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf new file mode 100644 index 0000000000..c0e6ab3a13 --- /dev/null +++ b/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf @@ -0,0 +1,27 @@ +## @file +# Parse the contents of named fw_cfg files as simple (scalar) data types. +# +# Copyright (C) 2020, Red Hat, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = QemuFwCfgSimpleParserLib + FILE_GUID = a9a1211d-061e-4b64-af30-5dd0cac9dc99 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = QemuFwCfgSimpleParserLib + CONSTRUCTOR = QemuFwCfgSimpleParserInit + +[Sources] + QemuFwCfgSimpleParser.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + QemuFwCfgLib diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 28030391cf..8a46fe7334 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -60,6 +60,10 @@ # QemuFwCfgS3Lib|Include/Library/QemuFwCfgS3Lib.h + ## @libraryclass Parse the contents of named fw_cfg files as simple + # (scalar) data types. + QemuFwCfgSimpleParserLib|Include/Library/QemuFwCfgSimpleParserLib.h + ## @libraryclass Rewrite the BootOrder NvVar based on QEMU's "bootorder" # fw_cfg file. # diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index d5e90c0013..5e29720631 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -160,6 +160,7 @@ UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf SerializeVariablesLib|OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf + QemuFwCfgSimpleParserLib|OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 066f49aeae..18e6909a33 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -164,6 +164,7 @@ UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf SerializeVariablesLib|OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf + QemuFwCfgSimpleParserLib|OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index ac510522a9..3d24cc4c1c 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -164,6 +164,7 @@ UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf SerializeVariablesLib|OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf + QemuFwCfgSimpleParserLib|OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf