Revamped code for VPD parser
The commit removes all the pre-existing code from the branch
and pushes the revamped code.
Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.
Note: This code supports all the existing functionalities as it is.
Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/src/ipz_parser.cpp b/vpd-manager/src/ipz_parser.cpp
new file mode 100644
index 0000000..4300e12
--- /dev/null
+++ b/vpd-manager/src/ipz_parser.cpp
@@ -0,0 +1,842 @@
+#include "config.h"
+
+#include "ipz_parser.hpp"
+
+#include "vpdecc/vpdecc.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <typeindex>
+
+namespace vpd
+{
+
+// Offset of different entries in VPD data.
+enum Offset
+{
+ VHDR = 17,
+ VHDR_TOC_ENTRY = 29,
+ VTOC_PTR = 35,
+ VTOC_REC_LEN = 37,
+ VTOC_ECC_OFF = 39,
+ VTOC_ECC_LEN = 41,
+ VTOC_DATA = 13,
+ VHDR_ECC = 0,
+ VHDR_RECORD = 11
+};
+
+// Length of some specific entries w.r.t VPD data.
+enum Length
+{
+ RECORD_NAME = 4,
+ KW_NAME = 2,
+ RECORD_OFFSET = 2,
+ RECORD_MIN = 44,
+ RECORD_LENGTH = 2,
+ RECORD_ECC_OFFSET = 2,
+ VHDR_ECC_LENGTH = 11,
+ VHDR_RECORD_LENGTH = 44,
+ RECORD_TYPE = 2,
+ SKIP_A_RECORD_IN_PT = 14,
+ JUMP_TO_RECORD_NAME = 6
+}; // enum Length
+
+/**
+ * @brief API to read 2 bytes LE data.
+ *
+ * @param[in] iterator - iterator to VPD vector.
+ * @return read bytes.
+ */
+static uint16_t readUInt16LE(types::BinaryVector::const_iterator iterator)
+{
+ uint16_t lowByte = *iterator;
+ uint16_t highByte = *(iterator + 1);
+ lowByte |= (highByte << 8);
+ return lowByte;
+}
+
+bool IpzVpdParser::vhdrEccCheck()
+{
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ auto l_status = vpdecc_check_data(
+ const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_RECORD]),
+ Length::VHDR_RECORD_LENGTH,
+ const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_ECC]),
+ Length::VHDR_ECC_LENGTH);
+ if (l_status == VPD_ECC_CORRECTABLE_DATA)
+ {
+ try
+ {
+ if (m_vpdFileStream.is_open())
+ {
+ m_vpdFileStream.seekp(m_vpdStartOffset + Offset::VHDR_RECORD,
+ std::ios::beg);
+ m_vpdFileStream.write(reinterpret_cast<const char*>(
+ &m_vpdVector[Offset::VHDR_RECORD]),
+ Length::VHDR_RECORD_LENGTH);
+ }
+ else
+ {
+ logging::logMessage("File not open");
+ return false;
+ }
+ }
+ catch (const std::fstream::failure& e)
+ {
+ logging::logMessage(
+ "Error while operating on file with exception: " +
+ std::string(e.what()));
+ return false;
+ }
+ }
+ else if (l_status != VPD_ECC_OK)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IpzVpdParser::vtocEccCheck()
+{
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ std::advance(vpdPtr, Offset::VTOC_PTR);
+
+ // The offset to VTOC could be 1 or 2 bytes long
+ auto vtocOffset = readUInt16LE(vpdPtr);
+
+ // Get the VTOC Length
+ std::advance(vpdPtr, sizeof(types::RecordOffset));
+ auto vtocLength = readUInt16LE(vpdPtr);
+
+ // Get the ECC Offset
+ std::advance(vpdPtr, sizeof(types::RecordLength));
+ auto vtocECCOffset = readUInt16LE(vpdPtr);
+
+ // Get the ECC length
+ std::advance(vpdPtr, sizeof(types::ECCOffset));
+ auto vtocECCLength = readUInt16LE(vpdPtr);
+
+ // Reset pointer to start of the vpd,
+ // so that Offset will point to correct address
+ vpdPtr = m_vpdVector.cbegin();
+ auto l_status = vpdecc_check_data(
+ const_cast<uint8_t*>(&m_vpdVector[vtocOffset]), vtocLength,
+ const_cast<uint8_t*>(&m_vpdVector[vtocECCOffset]), vtocECCLength);
+ if (l_status == VPD_ECC_CORRECTABLE_DATA)
+ {
+ try
+ {
+ if (m_vpdFileStream.is_open())
+ {
+ m_vpdFileStream.seekp(m_vpdStartOffset + vtocOffset,
+ std::ios::beg);
+ m_vpdFileStream.write(
+ reinterpret_cast<const char*>(&m_vpdVector[vtocOffset]),
+ vtocLength);
+ }
+ else
+ {
+ logging::logMessage("File not open");
+ return false;
+ }
+ }
+ catch (const std::fstream::failure& e)
+ {
+ logging::logMessage(
+ "Error while operating on file with exception " +
+ std::string(e.what()));
+ return false;
+ }
+ }
+ else if (l_status != VPD_ECC_OK)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IpzVpdParser::recordEccCheck(types::BinaryVector::const_iterator iterator)
+{
+ auto recordOffset = readUInt16LE(iterator);
+
+ std::advance(iterator, sizeof(types::RecordOffset));
+ auto recordLength = readUInt16LE(iterator);
+
+ if (recordOffset == 0 || recordLength == 0)
+ {
+ throw(DataException("Invalid record offset or length"));
+ }
+
+ std::advance(iterator, sizeof(types::RecordLength));
+ auto eccOffset = readUInt16LE(iterator);
+
+ std::advance(iterator, sizeof(types::ECCOffset));
+ auto eccLength = readUInt16LE(iterator);
+
+ if (eccLength == 0 || eccOffset == 0)
+ {
+ throw(EccException("Invalid ECC length or offset."));
+ }
+
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ if (vpdecc_check_data(
+ const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
+ const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength) == VPD_ECC_OK)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void IpzVpdParser::checkHeader(types::BinaryVector::const_iterator itrToVPD)
+{
+ if (m_vpdVector.empty() || (Length::RECORD_MIN > m_vpdVector.size()))
+ {
+ throw(DataException("Malformed VPD"));
+ }
+
+ std::advance(itrToVPD, Offset::VHDR);
+ auto stop = std::next(itrToVPD, Length::RECORD_NAME);
+
+ std::string record(itrToVPD, stop);
+ if ("VHDR" != record)
+ {
+ throw(DataException("VHDR record not found"));
+ }
+
+ if (!vhdrEccCheck())
+ {
+ throw(EccException("ERROR: VHDR ECC check Failed"));
+ }
+}
+
+auto IpzVpdParser::readTOC(types::BinaryVector::const_iterator& itrToVPD)
+{
+ // The offset to VTOC could be 1 or 2 bytes long
+ uint16_t vtocOffset =
+ readUInt16LE((itrToVPD + Offset::VTOC_PTR)); // itrToVPD);
+
+ // Got the offset to VTOC, skip past record header and keyword header
+ // to get to the record name.
+ std::advance(itrToVPD, vtocOffset + sizeof(types::RecordId) +
+ sizeof(types::RecordSize) +
+ // Skip past the RT keyword, which contains
+ // the record name.
+ Length::KW_NAME + sizeof(types::KwSize));
+
+ std::string record(itrToVPD, std::next(itrToVPD, Length::RECORD_NAME));
+ if ("VTOC" != record)
+ {
+ throw(DataException("VTOC record not found"));
+ }
+
+ if (!vtocEccCheck())
+ {
+ throw(EccException("ERROR: VTOC ECC check Failed"));
+ }
+
+ // VTOC record name is good, now read through the TOC, stored in the PT
+ // PT keyword; vpdBuffer is now pointing at the first character of the
+ // name 'VTOC', jump to PT data.
+ // Skip past record name and KW name, 'PT'
+ std::advance(itrToVPD, Length::RECORD_NAME + Length::KW_NAME);
+
+ // Note size of PT
+ auto ptLen = *itrToVPD;
+
+ // Skip past PT size
+ std::advance(itrToVPD, sizeof(types::KwSize));
+
+ // length of PT keyword
+ return ptLen;
+}
+
+types::RecordOffsetList IpzVpdParser::readPT(
+ types::BinaryVector::const_iterator& itrToPT, auto ptLength)
+{
+ types::RecordOffsetList recordOffsets;
+
+ auto end = itrToPT;
+ std::advance(end, ptLength);
+
+ // Look at each entry in the PT keyword. In the entry,
+ // we care only about the record offset information.
+ while (itrToPT < end)
+ {
+ std::string recordName(itrToPT, itrToPT + Length::RECORD_NAME);
+ // Skip record name and record type
+ std::advance(itrToPT, Length::RECORD_NAME + sizeof(types::RecordType));
+
+ // Get record offset
+ recordOffsets.push_back(readUInt16LE(itrToPT));
+ try
+ {
+ // Verify the ECC for this Record
+ if (!recordEccCheck(itrToPT))
+ {
+ throw(EccException("ERROR: ECC check failed"));
+ }
+ }
+ catch (const EccException& ex)
+ {
+ logging::logMessage(ex.what());
+
+ /*TODO: uncomment when PEL code goes in */
+
+ /*std::string errMsg =
+ std::string{ex.what()} + " Record: " + recordName;
+
+ inventory::PelAdditionalData additionalData{};
+ additionalData.emplace("DESCRIPTION", errMsg);
+ additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+ createPEL(additionalData, PelSeverity::WARNING,
+ errIntfForEccCheckFail, nullptr);*/
+ }
+ catch (const DataException& ex)
+ {
+ logging::logMessage(ex.what());
+
+ /*TODO: uncomment when PEL code goes in */
+
+ /*std::string errMsg =
+ std::string{ex.what()} + " Record: " + recordName;
+
+ inventory::PelAdditionalData additionalData{};
+ additionalData.emplace("DESCRIPTION", errMsg);
+ additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+ createPEL(additionalData, PelSeverity::WARNING,
+ errIntfForInvalidVPD, nullptr);*/
+ }
+
+ // Jump record size, record length, ECC offset and ECC length
+ std::advance(itrToPT,
+ sizeof(types::RecordOffset) + sizeof(types::RecordLength) +
+ sizeof(types::ECCOffset) + sizeof(types::ECCLength));
+ }
+
+ return recordOffsets;
+}
+
+types::IPZVpdMap::mapped_type
+ IpzVpdParser::readKeywords(types::BinaryVector::const_iterator& itrToKwds)
+{
+ types::IPZVpdMap::mapped_type kwdValueMap{};
+ while (true)
+ {
+ // Note keyword name
+ std::string kwdName(itrToKwds, itrToKwds + Length::KW_NAME);
+ if (constants::LAST_KW == kwdName)
+ {
+ // We're done
+ break;
+ }
+ // Check if the Keyword is '#kw'
+ char kwNameStart = *itrToKwds;
+
+ // Jump past keyword name
+ std::advance(itrToKwds, Length::KW_NAME);
+
+ std::size_t kwdDataLength;
+ std::size_t lengthHighByte;
+
+ if (constants::POUND_KW == kwNameStart)
+ {
+ // Note keyword data length
+ kwdDataLength = *itrToKwds;
+ lengthHighByte = *(itrToKwds + 1);
+ kwdDataLength |= (lengthHighByte << 8);
+
+ // Jump past 2Byte keyword length
+ std::advance(itrToKwds, sizeof(types::PoundKwSize));
+ }
+ else
+ {
+ // Note keyword data length
+ kwdDataLength = *itrToKwds;
+
+ // Jump past keyword length
+ std::advance(itrToKwds, sizeof(types::KwSize));
+ }
+
+ // support all the Keywords
+ auto stop = std::next(itrToKwds, kwdDataLength);
+ std::string kwdata(itrToKwds, stop);
+ kwdValueMap.emplace(std::move(kwdName), std::move(kwdata));
+
+ // Jump past keyword data length
+ std::advance(itrToKwds, kwdDataLength);
+ }
+
+ return kwdValueMap;
+}
+
+void IpzVpdParser::processRecord(auto recordOffset)
+{
+ // Jump to record name
+ auto recordNameOffset =
+ recordOffset + sizeof(types::RecordId) + sizeof(types::RecordSize) +
+ // Skip past the RT keyword, which contains
+ // the record name.
+ Length::KW_NAME + sizeof(types::KwSize);
+
+ // Get record name
+ auto itrToVPDStart = m_vpdVector.cbegin();
+ std::advance(itrToVPDStart, recordNameOffset);
+
+ std::string recordName(itrToVPDStart, itrToVPDStart + Length::RECORD_NAME);
+
+ // proceed to find contained keywords and their values.
+ std::advance(itrToVPDStart, Length::RECORD_NAME);
+
+ // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
+ std::advance(itrToVPDStart, -(Length::KW_NAME + sizeof(types::KwSize) +
+ Length::RECORD_NAME));
+
+ // Add entry for this record (and contained keyword:value pairs)
+ // to the parsed vpd output.
+ m_parsedVPDMap.emplace(std::move(recordName),
+ std::move(readKeywords(itrToVPDStart)));
+}
+
+types::VPDMapVariant IpzVpdParser::parse()
+{
+ try
+ {
+ auto itrToVPD = m_vpdVector.cbegin();
+
+ // Check vaidity of VHDR record
+ checkHeader(itrToVPD);
+
+ // Read the table of contents
+ auto ptLen = readTOC(itrToVPD);
+
+ // Read the table of contents record, to get offsets
+ // to other records.
+ auto recordOffsets = readPT(itrToVPD, ptLen);
+ for (const auto& offset : recordOffsets)
+ {
+ processRecord(offset);
+ }
+
+ return m_parsedVPDMap;
+ }
+ catch (const std::exception& e)
+ {
+ logging::logMessage(e.what());
+ throw e;
+ }
+}
+
+types::BinaryVector IpzVpdParser::getKeywordValueFromRecord(
+ const types::Record& i_recordName, const types::Keyword& i_keywordName,
+ const types::RecordOffset& i_recordDataOffset)
+{
+ auto l_iterator = m_vpdVector.cbegin();
+
+ // Go to the record name in the given record's offset
+ std::ranges::advance(l_iterator,
+ i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+ m_vpdVector.cend());
+
+ // Check if the record is present in the given record's offset
+ if (i_recordName !=
+ std::string(l_iterator,
+ std::ranges::next(l_iterator, Length::RECORD_NAME,
+ m_vpdVector.cend())))
+ {
+ throw std::runtime_error(
+ "Given record is not present in the offset provided");
+ }
+
+ std::ranges::advance(l_iterator, Length::RECORD_NAME, m_vpdVector.cend());
+
+ std::string l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+
+ // Iterate through the keywords until the last keyword PF is found.
+ while (l_kwName != constants::LAST_KW)
+ {
+ // First character required for #D keyword check
+ char l_kwNameStart = *l_iterator;
+
+ std::ranges::advance(l_iterator, Length::KW_NAME, m_vpdVector.cend());
+
+ // Get the keyword's data length
+ auto l_kwdDataLength = 0;
+
+ if (constants::POUND_KW == l_kwNameStart)
+ {
+ l_kwdDataLength = readUInt16LE(l_iterator);
+ std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+ m_vpdVector.cend());
+ }
+ else
+ {
+ l_kwdDataLength = *l_iterator;
+ std::ranges::advance(l_iterator, sizeof(types::KwSize),
+ m_vpdVector.cend());
+ }
+
+ if (l_kwName == i_keywordName)
+ {
+ // Return keyword's value to the caller
+ return types::BinaryVector(
+ l_iterator, std::ranges::next(l_iterator, l_kwdDataLength,
+ m_vpdVector.cend()));
+ }
+
+ // next keyword search
+ std::ranges::advance(l_iterator, l_kwdDataLength, m_vpdVector.cend());
+
+ // next keyword name
+ l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+ }
+
+ // Keyword not found
+ throw std::runtime_error("Given keyword not found.");
+}
+
+types::RecordData IpzVpdParser::getRecordDetailsFromVTOC(
+ const types::Record& i_recordName, const types::RecordOffset& i_vtocOffset)
+{
+ // Get VTOC's PT keyword value.
+ const auto l_vtocPTKwValue =
+ getKeywordValueFromRecord("VTOC", "PT", i_vtocOffset);
+
+ // Parse through VTOC PT keyword value to find the record which we are
+ // interested in.
+ auto l_vtocPTItr = l_vtocPTKwValue.cbegin();
+
+ types::RecordData l_recordData;
+
+ while (l_vtocPTItr < l_vtocPTKwValue.cend())
+ {
+ if (i_recordName ==
+ std::string(l_vtocPTItr, l_vtocPTItr + Length::RECORD_NAME))
+ {
+ // Record found in VTOC PT keyword. Get offset
+ std::ranges::advance(l_vtocPTItr,
+ Length::RECORD_NAME + Length::RECORD_TYPE,
+ l_vtocPTKwValue.cend());
+ const auto l_recordOffset = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_OFFSET,
+ l_vtocPTKwValue.cend());
+ const auto l_recordLength = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_LENGTH,
+ l_vtocPTKwValue.cend());
+ const auto l_eccOffset = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_ECC_OFFSET,
+ l_vtocPTKwValue.cend());
+ const auto l_eccLength = readUInt16LE(l_vtocPTItr);
+
+ l_recordData = std::make_tuple(l_recordOffset, l_recordLength,
+ l_eccOffset, l_eccLength);
+ break;
+ }
+
+ std::ranges::advance(l_vtocPTItr, Length::SKIP_A_RECORD_IN_PT,
+ l_vtocPTKwValue.cend());
+ }
+
+ return l_recordData;
+}
+
+types::DbusVariantType IpzVpdParser::readKeywordFromHardware(
+ const types::ReadVpdParams i_paramsToReadData)
+{
+ // Extract record and keyword from i_paramsToReadData
+ types::Record l_record;
+ types::Keyword l_keyword;
+
+ if (const types::IpzType* l_ipzData =
+ std::get_if<types::IpzType>(&i_paramsToReadData))
+ {
+ l_record = std::get<0>(*l_ipzData);
+ l_keyword = std::get<1>(*l_ipzData);
+ }
+ else
+ {
+ logging::logMessage(
+ "Input parameter type provided isn't compatible with the given VPD type.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Read keyword's value from vector
+ auto l_itrToVPD = m_vpdVector.cbegin();
+
+ if (l_record == "VHDR")
+ {
+// Disable providing a way to read keywords from VHDR for the time being.
+#if 0
+ std::ranges::advance(l_itrToVPD, Offset::VHDR_RECORD,
+ m_vpdVector.cend());
+
+ return types::DbusVariantType{getKeywordValueFromRecord(
+ l_record, l_keyword, Offset::VHDR_RECORD)};
+#endif
+
+ logging::logMessage("Read cannot be performed on VHDR record.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Get VTOC offset
+ std::ranges::advance(l_itrToVPD, Offset::VTOC_PTR, m_vpdVector.cend());
+ auto l_vtocOffset = readUInt16LE(l_itrToVPD);
+
+ if (l_record == "VTOC")
+ {
+ // Disable providing a way to read keywords from VTOC for the time
+ // being.
+#if 0
+ return types::DbusVariantType{
+ getKeywordValueFromRecord(l_record, l_keyword, l_vtocOffset)};
+#endif
+
+ logging::logMessage("Read cannot be performed on VTOC record.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Get record offset from VTOC's PT keyword value.
+ auto l_recordData = getRecordDetailsFromVTOC(l_record, l_vtocOffset);
+ const auto l_recordOffset = std::get<0>(l_recordData);
+
+ if (l_recordOffset == 0)
+ {
+ throw std::runtime_error("Record not found in VTOC PT keyword.");
+ }
+
+ // Get the given keyword's value
+ return types::DbusVariantType{
+ getKeywordValueFromRecord(l_record, l_keyword, l_recordOffset)};
+}
+
+void IpzVpdParser::updateRecordECC(
+ const auto& i_recordDataOffset, const auto& i_recordDataLength,
+ const auto& i_recordECCOffset, size_t i_recordECCLength,
+ types::BinaryVector& io_vpdVector)
+{
+ auto l_recordDataBegin =
+ std::next(io_vpdVector.begin(), i_recordDataOffset);
+
+ auto l_recordECCBegin = std::next(io_vpdVector.begin(), i_recordECCOffset);
+
+ auto l_eccStatus = vpdecc_create_ecc(
+ const_cast<uint8_t*>(&l_recordDataBegin[0]), i_recordDataLength,
+ const_cast<uint8_t*>(&l_recordECCBegin[0]), &i_recordECCLength);
+
+ if (l_eccStatus != VPD_ECC_OK)
+ {
+ throw(EccException("ECC update failed with error " + l_eccStatus));
+ }
+
+ auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
+
+ m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
+
+ std::copy(l_recordECCBegin, l_recordECCEnd,
+ std::ostreambuf_iterator<char>(m_vpdFileStream));
+}
+
+int IpzVpdParser::setKeywordValueInRecord(
+ const types::Record& i_recordName, const types::Keyword& i_keywordName,
+ const types::BinaryVector& i_keywordData,
+ const types::RecordOffset& i_recordDataOffset,
+ types::BinaryVector& io_vpdVector)
+{
+ auto l_iterator = io_vpdVector.begin();
+
+ // Go to the record name in the given record's offset
+ std::ranges::advance(l_iterator,
+ i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+ io_vpdVector.end());
+
+ const std::string l_recordFound(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
+
+ // Check if the record is present in the given record's offset
+ if (i_recordName != l_recordFound)
+ {
+ throw(DataException("Given record found at the offset " +
+ std::to_string(i_recordDataOffset) + " is : " +
+ l_recordFound + " and not " + i_recordName));
+ }
+
+ std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
+
+ std::string l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+
+ // Iterate through the keywords until the last keyword PF is found.
+ while (l_kwName != constants::LAST_KW)
+ {
+ // First character required for #D keyword check
+ char l_kwNameStart = *l_iterator;
+
+ std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
+
+ // Find the keyword's data length
+ size_t l_kwdDataLength = 0;
+
+ if (constants::POUND_KW == l_kwNameStart)
+ {
+ l_kwdDataLength = readUInt16LE(l_iterator);
+ std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+ io_vpdVector.end());
+ }
+ else
+ {
+ l_kwdDataLength = *l_iterator;
+ std::ranges::advance(l_iterator, sizeof(types::KwSize),
+ io_vpdVector.end());
+ }
+
+ if (l_kwName == i_keywordName)
+ {
+ // Before writing the keyword's value, get the maximum size that can
+ // be updated.
+ const auto l_lengthToUpdate =
+ i_keywordData.size() <= l_kwdDataLength
+ ? i_keywordData.size()
+ : l_kwdDataLength;
+
+ // Set the keyword's value on vector. This is required to update the
+ // record's ECC based on the new value set.
+ const auto i_keywordDataEnd = std::ranges::next(
+ i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
+
+ std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
+
+ // Set the keyword's value on hardware
+ const auto l_kwdDataOffset =
+ std::distance(io_vpdVector.begin(), l_iterator);
+ m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
+ std::ios::beg);
+
+ std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
+ std::ostreambuf_iterator<char>(m_vpdFileStream));
+
+ // return no of bytes set
+ return l_lengthToUpdate;
+ }
+
+ // next keyword search
+ std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
+
+ // next keyword name
+ l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+ }
+
+ // Keyword not found
+ throw(DataException(
+ "Keyword " + i_keywordName + " not found in record " + i_recordName));
+}
+
+int IpzVpdParser::writeKeywordOnHardware(
+ const types::WriteVpdParams i_paramsToWriteData)
+{
+ int l_sizeWritten = -1;
+
+ try
+ {
+ types::Record l_recordName;
+ types::Keyword l_keywordName;
+ types::BinaryVector l_keywordData;
+
+ // Extract record, keyword and value from i_paramsToWriteData
+ if (const types::IpzData* l_ipzData =
+ std::get_if<types::IpzData>(&i_paramsToWriteData))
+ {
+ l_recordName = std::get<0>(*l_ipzData);
+ l_keywordName = std::get<1>(*l_ipzData);
+ l_keywordData = std::get<2>(*l_ipzData);
+ }
+ else
+ {
+ logging::logMessage(
+ "Input parameter type provided isn't compatible with the given FRU's VPD type.");
+ throw types::DbusInvalidArgument();
+ }
+
+ if (l_recordName == "VHDR" || l_recordName == "VTOC")
+ {
+ logging::logMessage(
+ "Write operation not allowed on the given record : " +
+ l_recordName);
+ throw types::DbusNotAllowed();
+ }
+
+ if (l_keywordData.size() == 0)
+ {
+ logging::logMessage(
+ "Write operation not allowed as the given keyword's data length is 0.");
+ throw types::DbusInvalidArgument();
+ }
+
+ auto l_vpdBegin = m_vpdVector.begin();
+
+ // Get VTOC offset
+ std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
+ auto l_vtocOffset = readUInt16LE(l_vpdBegin);
+
+ // Get the details of user given record from VTOC
+ const types::RecordData& l_inputRecordDetails =
+ getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
+
+ const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
+
+ if (l_inputRecordOffset == 0)
+ {
+ throw(DataException("Record not found in VTOC PT keyword."));
+ }
+
+ // Create a local copy of m_vpdVector to perform keyword update and ecc
+ // update on filestream.
+ types::BinaryVector l_vpdVector = m_vpdVector;
+
+ // write keyword's value on hardware
+ l_sizeWritten =
+ setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
+ l_inputRecordOffset, l_vpdVector);
+
+ if (l_sizeWritten <= 0)
+ {
+ throw(DataException("Unable to set value on " + l_recordName + ":" +
+ l_keywordName));
+ }
+
+ // Update the record's ECC
+ updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
+ std::get<2>(l_inputRecordDetails),
+ std::get<3>(l_inputRecordDetails), l_vpdVector);
+
+ logging::logMessage(std::to_string(l_sizeWritten) +
+ " bytes updated successfully on hardware for " +
+ l_recordName + ":" + l_keywordName);
+ }
+ catch (const std::exception& l_exception)
+ {
+ throw;
+ }
+
+ return l_sizeWritten;
+}
+} // namespace vpd