| /** @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 <Library/BaseLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include <Library/QemuFwCfgSimpleParserLib.h> | |
| // | |
| // 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; | |
| } |