From 4b744f20629f5d829079e5a0a104811071199fb4 Mon Sep 17 00:00:00 2001 From: Priyanga Ramasamy Date: Mon, 4 Nov 2024 07:25:22 -0600 Subject: [PATCH] Keyword VPD: Update keyword on Dbus Keyword's value gets updated on D-bus irrespective of verifying the keyword size. For example, For a 4 byte keyword, if user gives more than 4 bytes of data to update, hardware update will accept only 4 bytes but D-bus update will accept every extra byte given by the user. This will create a mismatch between hardware and D-bus. So to avoid such mismatches we came up with the design of updating the keyword's value in hardware first ( which takes care of size checks) and copy the same in other sources. With this approach we maintain the reliability of keyword updates across all other sources. This commit implements the portion where we read the keyword's value from hardware (assuming the hardware write is already done from Manager class) and write the same on D-bus. Test: busctl call com.ibm.VPD.Manager /com/ibm/VPD/Manager com.ibm.VPD.Manager ReadKeyword sv "/tmp/keyword.dat" s "SN" v ay 12 89 72 51 48 66 71 55 56 66 48 49 52 Change-Id: I60d7a2b418ad5e451e46f288bde3477794145ec0 Signed-off-by: Priyanga Ramasamy --- include/keyword_vpd_parser.hpp | 26 ++++++++++ src/keyword_vpd_parser.cpp | 93 ++++++++++++++++++++++++++++++++++ src/parser.cpp | 28 ++++++++++ 3 files changed, 147 insertions(+) diff --git a/include/keyword_vpd_parser.hpp b/include/keyword_vpd_parser.hpp index ae30a34c8..038ba05fa 100644 --- a/include/keyword_vpd_parser.hpp +++ b/include/keyword_vpd_parser.hpp @@ -41,6 +41,19 @@ class KeywordVpdParser : public ParserInterface */ types::VPDMapVariant parse(); + /** + * @brief API to read keyword's value from hardware + * + * @param[in] i_paramsToReadData - Data required to perform read + * + * @throw + * sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument + * + * @return On success return the value read. On failure throw exception. + */ + types::DbusVariantType + readKeywordFromHardware(const types::ReadVpdParams i_paramsToReadData); + private: /** * @brief Parse the VPD data and emplace them as pair into the Map. @@ -88,6 +101,19 @@ class KeywordVpdParser : public ParserInterface */ void checkNextBytesValidity(uint8_t numberOfBytes); + /** + * @brief API to iterate through the VPD vector to find the given keyword. + * + * This API iterates through VPD vector using m_vpdIterator and finds the + * given keyword. m_vpdIterator points to the keyword name if find is + * successful. + * + * @param[in] i_keyword - Keyword name. + * + * @return 0 On successful find, -1 on failure. + */ + int findKeyword(const std::string& i_keyword); + /*Vector of keyword VPD data*/ const types::BinaryVector& m_keywordVpdVector; diff --git a/src/keyword_vpd_parser.cpp b/src/keyword_vpd_parser.cpp index 0e0181f54..f826d4ec6 100644 --- a/src/keyword_vpd_parser.cpp +++ b/src/keyword_vpd_parser.cpp @@ -133,4 +133,97 @@ void KeywordVpdParser::checkNextBytesValidity(uint8_t i_numberOfBytes) } } +types::DbusVariantType KeywordVpdParser::readKeywordFromHardware( + const types::ReadVpdParams i_paramsToReadData) +{ + types::Keyword l_keyword; + + if (const types::Keyword* l_kwData = + std::get_if(&i_paramsToReadData)) + { + l_keyword = *l_kwData; + } + else + { + logging::logMessage("Given VPD type is not supported."); + throw types::DbusInvalidArgument(); + } + + if (l_keyword.empty()) + { + logging::logMessage("Given an empty keyword name."); + throw types::DbusInvalidArgument(); + } + + // Iterate through VPD vector to find the keyword + if (findKeyword(l_keyword) != 0) + { + logging::logMessage("Keyword " + l_keyword + " not found."); + throw types::DbusInvalidArgument(); + } + + // Skip bytes representing the keyword name + std::advance(m_vpdIterator, constants::TWO_BYTES); + + // Get size of the keyword + const auto l_keywordSize = *m_vpdIterator; + + // Skip bytes representing the size of the keyword + std::advance(m_vpdIterator, constants::ONE_BYTE); + + // Read the keyword's value and return + return types::DbusVariantType{types::BinaryVector( + m_vpdIterator, std::ranges::next(m_vpdIterator, l_keywordSize, + m_keywordVpdVector.cend()))}; +} + +int KeywordVpdParser::findKeyword(const std::string& i_keyword) +{ + m_vpdIterator = m_keywordVpdVector.begin(); + + // Skip Keyword VPD's start tag + std::advance(m_vpdIterator, sizeof(constants::KW_VPD_START_TAG)); + + // Get size of the header + uint16_t l_dataSize = getKwDataSize(); + + // Skip bytes which represents the size of header + Skip header data + std::advance(m_vpdIterator, constants::TWO_BYTES + l_dataSize); + + // Skip Keyword VPD pair's start tag + std::advance(m_vpdIterator, constants::ONE_BYTE); + + // Get total size of keyword value pairs + auto l_totalSize = getKwDataSize(); + + if (l_totalSize <= 0) + { + // Keyword not found + return -1; + } + + // Skip bytes which represents the total size of kw-value pairs + std::advance(m_vpdIterator, constants::TWO_BYTES); + + while (l_totalSize > 0) + { + // Get keyword name + std::string l_keywordName(m_vpdIterator, + m_vpdIterator + constants::TWO_BYTES); + + if (l_keywordName == i_keyword) + { + // Keyword successfully found + return 0; + } + std::advance(m_vpdIterator, constants::TWO_BYTES); + size_t l_kwSize = *m_vpdIterator; + + std::advance(m_vpdIterator, constants::ONE_BYTE + l_kwSize); + l_totalSize -= constants::TWO_BYTES + constants::ONE_BYTE + l_kwSize; + } + + // Keyword not found + return -1; +} } // namespace vpd diff --git a/src/parser.cpp b/src/parser.cpp index ce45ef205..b146acde9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -116,6 +116,34 @@ int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData) return -1; } } + else if (const types::KwData* l_kwData = + std::get_if(&i_paramsToWriteData)) + { + l_interfaceName = constants::kwdVpdInf; + l_propertyName = std::get<0>(*l_kwData); + + try + { + // Read keyword's value from hardware to write the same on + // D-bus. + std::shared_ptr l_vpdParserInstance = + getVpdParserInstance(); + logging::logMessage("Performing VPD read on " + m_vpdFilePath); + l_keywordValue = l_vpdParserInstance->readKeywordFromHardware( + types::ReadVpdParams(l_propertyName)); + } + catch (const std::exception& l_exception) + { + // Unable to read keyword's value from hardware. + logging::logMessage( + "Error while reading keyword's value from hadware path " + + m_vpdFilePath + + ", error: " + std::string(l_exception.what())); + + // TODO: Log PEL + return -1; + } + } else { // Input parameter type provided isn't compatible to perform update.