| #include "config.h" |
| |
| #include "ipz_parser.hpp" |
| |
| #include "vpdecc/vpdecc.h" |
| |
| #include "constants.hpp" |
| #include "event_logger.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() |
| { |
| // To avoid 1 bit flip correction from corrupting the main buffer. |
| const types::BinaryVector tempVector = m_vpdVector; |
| auto vpdPtr = tempVector.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) |
| { |
| EventLogger::createSyncPel( |
| types::ErrorType::EccCheckFailed, |
| types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, |
| "One bit correction for VHDR performed", std::nullopt, std::nullopt, |
| std::nullopt, std::nullopt); |
| } |
| 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); |
| |
| // To avoid 1 bit flip correction from corrupting the main buffer. |
| const types::BinaryVector tempVector = m_vpdVector; |
| // Reset pointer to start of the vpd, |
| // so that Offset will point to correct address |
| vpdPtr = tempVector.cbegin(); |
| |
| auto l_status = vpdecc_check_data( |
| const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength, |
| const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength); |
| if (l_status == VPD_ECC_CORRECTABLE_DATA) |
| { |
| EventLogger::createSyncPel( |
| types::ErrorType::EccCheckFailed, |
| types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, |
| "One bit correction for VTOC performed", std::nullopt, std::nullopt, |
| std::nullopt, std::nullopt); |
| } |
| 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.")); |
| } |
| |
| // To avoid 1 bit flip correction from corrupting the main buffer. |
| const types::BinaryVector tempVector = m_vpdVector; |
| auto vpdPtr = tempVector.cbegin(); |
| |
| auto l_status = vpdecc_check_data( |
| const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength, |
| const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength); |
| |
| if (l_status == VPD_ECC_CORRECTABLE_DATA) |
| { |
| EventLogger::createSyncPel( |
| types::ErrorType::EccCheckFailed, |
| types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, |
| "One bit correction for record performed", std::nullopt, |
| std::nullopt, std::nullopt, std::nullopt); |
| } |
| else if (l_status != VPD_ECC_OK) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| 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 |