xref: /openbmc/openpower-vpd-parser/vpd-manager/src/ipz_parser.cpp (revision 10a6ac958a6b947e7b10621959119058ce209e2e)
1 #include "config.h"
2 
3 #include "ipz_parser.hpp"
4 
5 #include "vpdecc/vpdecc.h"
6 
7 #include "constants.hpp"
8 #include "event_logger.hpp"
9 #include "exceptions.hpp"
10 #include "utility/vpd_specific_utility.hpp"
11 
12 #include <nlohmann/json.hpp>
13 
14 #include <typeindex>
15 
16 namespace vpd
17 {
18 
19 // Offset of different entries in VPD data.
20 enum Offset
21 {
22     VHDR = 17,
23     VHDR_TOC_ENTRY = 29,
24     VTOC_PTR = 35,
25     VTOC_REC_LEN = 37,
26     VTOC_ECC_OFF = 39,
27     VTOC_ECC_LEN = 41,
28     VTOC_DATA = 13,
29     VHDR_ECC = 0,
30     VHDR_RECORD = 11
31 };
32 
33 // Length of some specific entries w.r.t VPD data.
34 enum Length
35 {
36     RECORD_NAME = 4,
37     KW_NAME = 2,
38     RECORD_OFFSET = 2,
39     RECORD_MIN = 44,
40     RECORD_LENGTH = 2,
41     RECORD_ECC_OFFSET = 2,
42     VHDR_ECC_LENGTH = 11,
43     VHDR_RECORD_LENGTH = 44,
44     RECORD_TYPE = 2,
45     SKIP_A_RECORD_IN_PT = 14,
46     JUMP_TO_RECORD_NAME = 6
47 }; // enum Length
48 
49 /**
50  * @brief API to read 2 bytes LE data.
51  *
52  * @param[in] iterator - iterator to VPD vector.
53  * @return read bytes.
54  */
readUInt16LE(types::BinaryVector::const_iterator iterator)55 static uint16_t readUInt16LE(types::BinaryVector::const_iterator iterator)
56 {
57     uint16_t lowByte = *iterator;
58     uint16_t highByte = *(iterator + 1);
59     lowByte |= (highByte << 8);
60     return lowByte;
61 }
62 
vhdrEccCheck()63 bool IpzVpdParser::vhdrEccCheck()
64 {
65     // To avoid 1 bit flip correction from corrupting the main buffer.
66     const types::BinaryVector tempVector = m_vpdVector;
67     auto vpdPtr = tempVector.cbegin();
68 
69     auto l_status = vpdecc_check_data(
70         const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_RECORD]),
71         Length::VHDR_RECORD_LENGTH,
72         const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_ECC]),
73         Length::VHDR_ECC_LENGTH);
74     if (l_status == VPD_ECC_CORRECTABLE_DATA)
75     {
76         EventLogger::createSyncPel(
77             types::ErrorType::EccCheckFailed,
78             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
79             "One bit correction for VHDR performed", std::nullopt, std::nullopt,
80             std::nullopt, std::nullopt);
81     }
82     else if (l_status != VPD_ECC_OK)
83     {
84         return false;
85     }
86 
87     return true;
88 }
89 
vtocEccCheck()90 bool IpzVpdParser::vtocEccCheck()
91 {
92     auto vpdPtr = m_vpdVector.cbegin();
93 
94     std::advance(vpdPtr, Offset::VTOC_PTR);
95 
96     // The offset to VTOC could be 1 or 2 bytes long
97     auto vtocOffset = readUInt16LE(vpdPtr);
98 
99     // Get the VTOC Length
100     std::advance(vpdPtr, sizeof(types::RecordOffset));
101     auto vtocLength = readUInt16LE(vpdPtr);
102 
103     // Get the ECC Offset
104     std::advance(vpdPtr, sizeof(types::RecordLength));
105     auto vtocECCOffset = readUInt16LE(vpdPtr);
106 
107     // Get the ECC length
108     std::advance(vpdPtr, sizeof(types::ECCOffset));
109     auto vtocECCLength = readUInt16LE(vpdPtr);
110 
111     // To avoid 1 bit flip correction from corrupting the main buffer.
112     const types::BinaryVector tempVector = m_vpdVector;
113     // Reset pointer to start of the vpd,
114     // so that Offset will point to correct address
115     vpdPtr = tempVector.cbegin();
116 
117     auto l_status = vpdecc_check_data(
118         const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
119         const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
120     if (l_status == VPD_ECC_CORRECTABLE_DATA)
121     {
122         EventLogger::createSyncPel(
123             types::ErrorType::EccCheckFailed,
124             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
125             "One bit correction for VTOC performed", std::nullopt, std::nullopt,
126             std::nullopt, std::nullopt);
127     }
128     else if (l_status != VPD_ECC_OK)
129     {
130         return false;
131     }
132 
133     return true;
134 }
135 
recordEccCheck(types::BinaryVector::const_iterator iterator)136 bool IpzVpdParser::recordEccCheck(types::BinaryVector::const_iterator iterator)
137 {
138     auto recordOffset = readUInt16LE(iterator);
139 
140     std::advance(iterator, sizeof(types::RecordOffset));
141     auto recordLength = readUInt16LE(iterator);
142 
143     if (recordOffset == 0 || recordLength == 0)
144     {
145         throw(DataException("Invalid record offset or length"));
146     }
147 
148     std::advance(iterator, sizeof(types::RecordLength));
149     auto eccOffset = readUInt16LE(iterator);
150 
151     std::advance(iterator, sizeof(types::ECCOffset));
152     auto eccLength = readUInt16LE(iterator);
153 
154     if (eccLength == 0 || eccOffset == 0)
155     {
156         throw(EccException("Invalid ECC length or offset."));
157     }
158 
159     // To avoid 1 bit flip correction from corrupting the main buffer.
160     const types::BinaryVector tempVector = m_vpdVector;
161     auto vpdPtr = tempVector.cbegin();
162 
163     auto l_status = vpdecc_check_data(
164         const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
165         const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength);
166 
167     if (l_status == VPD_ECC_CORRECTABLE_DATA)
168     {
169         EventLogger::createSyncPel(
170             types::ErrorType::EccCheckFailed,
171             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
172             "One bit correction for record performed", std::nullopt,
173             std::nullopt, std::nullopt, std::nullopt);
174     }
175     else if (l_status != VPD_ECC_OK)
176     {
177         return false;
178     }
179 
180     return true;
181 }
182 
checkHeader(types::BinaryVector::const_iterator itrToVPD)183 void IpzVpdParser::checkHeader(types::BinaryVector::const_iterator itrToVPD)
184 {
185     if (m_vpdVector.empty() || (Length::RECORD_MIN > m_vpdVector.size()))
186     {
187         throw(DataException("Malformed VPD"));
188     }
189 
190     std::advance(itrToVPD, Offset::VHDR);
191     auto stop = std::next(itrToVPD, Length::RECORD_NAME);
192 
193     std::string record(itrToVPD, stop);
194     if ("VHDR" != record)
195     {
196         throw(DataException("VHDR record not found"));
197     }
198 
199     if (!vhdrEccCheck())
200     {
201         throw(EccException("ERROR: VHDR ECC check Failed"));
202     }
203 }
204 
readTOC(types::BinaryVector::const_iterator & itrToVPD)205 auto IpzVpdParser::readTOC(types::BinaryVector::const_iterator& itrToVPD)
206 {
207     // The offset to VTOC could be 1 or 2 bytes long
208     uint16_t vtocOffset =
209         readUInt16LE((itrToVPD + Offset::VTOC_PTR)); // itrToVPD);
210 
211     // Got the offset to VTOC, skip past record header and keyword header
212     // to get to the record name.
213     std::advance(itrToVPD, vtocOffset + sizeof(types::RecordId) +
214                                sizeof(types::RecordSize) +
215                                // Skip past the RT keyword, which contains
216                                // the record name.
217                                Length::KW_NAME + sizeof(types::KwSize));
218 
219     std::string record(itrToVPD, std::next(itrToVPD, Length::RECORD_NAME));
220     if ("VTOC" != record)
221     {
222         throw(DataException("VTOC record not found"));
223     }
224 
225     if (!vtocEccCheck())
226     {
227         throw(EccException("ERROR: VTOC ECC check Failed"));
228     }
229 
230     // VTOC record name is good, now read through the TOC, stored in the PT
231     // PT keyword; vpdBuffer is now pointing at the first character of the
232     // name 'VTOC', jump to PT data.
233     // Skip past record name and KW name, 'PT'
234     std::advance(itrToVPD, Length::RECORD_NAME + Length::KW_NAME);
235 
236     // Note size of PT
237     auto ptLen = *itrToVPD;
238 
239     // Skip past PT size
240     std::advance(itrToVPD, sizeof(types::KwSize));
241 
242     // length of PT keyword
243     return ptLen;
244 }
245 
246 std::pair<types::RecordOffsetList, types::InvalidRecordList>
readPT(types::BinaryVector::const_iterator & itrToPT,auto ptLength)247     IpzVpdParser::readPT(types::BinaryVector::const_iterator& itrToPT,
248                          auto ptLength)
249 {
250     types::RecordOffsetList recordOffsets;
251 
252     // List of names of all invalid records found.
253     types::InvalidRecordList l_invalidRecordList;
254 
255     auto end = itrToPT;
256     std::advance(end, ptLength);
257 
258     // Look at each entry in the PT keyword. In the entry,
259     // we care only about the record offset information.
260     while (itrToPT < end)
261     {
262         std::string recordName(itrToPT, itrToPT + Length::RECORD_NAME);
263         // Skip record name and record type
264         std::advance(itrToPT, Length::RECORD_NAME + sizeof(types::RecordType));
265 
266         // Get record offset
267         recordOffsets.push_back(readUInt16LE(itrToPT));
268         try
269         {
270             // Verify the ECC for this Record
271             if (!recordEccCheck(itrToPT))
272             {
273                 throw(EccException("ERROR: ECC check failed"));
274             }
275         }
276         catch (const std::exception& l_ex)
277         {
278             logging::logMessage(l_ex.what());
279 
280             // add the invalid record name and exception object to list
281             l_invalidRecordList.emplace_back(types::InvalidRecordEntry{
282                 recordName, EventLogger::getErrorType(l_ex)});
283         }
284 
285         // Jump record size, record length, ECC offset and ECC length
286         std::advance(itrToPT,
287                      sizeof(types::RecordOffset) + sizeof(types::RecordLength) +
288                          sizeof(types::ECCOffset) + sizeof(types::ECCLength));
289     }
290 
291     return std::make_pair(recordOffsets, l_invalidRecordList);
292 }
293 
readKeywords(types::BinaryVector::const_iterator & itrToKwds)294 types::IPZVpdMap::mapped_type IpzVpdParser::readKeywords(
295     types::BinaryVector::const_iterator& itrToKwds)
296 {
297     types::IPZVpdMap::mapped_type kwdValueMap{};
298     while (true)
299     {
300         // Note keyword name
301         std::string kwdName(itrToKwds, itrToKwds + Length::KW_NAME);
302         if (constants::LAST_KW == kwdName)
303         {
304             // We're done
305             break;
306         }
307         // Check if the Keyword is '#kw'
308         char kwNameStart = *itrToKwds;
309 
310         // Jump past keyword name
311         std::advance(itrToKwds, Length::KW_NAME);
312 
313         std::size_t kwdDataLength;
314         std::size_t lengthHighByte;
315 
316         if (constants::POUND_KW == kwNameStart)
317         {
318             // Note keyword data length
319             kwdDataLength = *itrToKwds;
320             lengthHighByte = *(itrToKwds + 1);
321             kwdDataLength |= (lengthHighByte << 8);
322 
323             // Jump past 2Byte keyword length
324             std::advance(itrToKwds, sizeof(types::PoundKwSize));
325         }
326         else
327         {
328             // Note keyword data length
329             kwdDataLength = *itrToKwds;
330 
331             // Jump past keyword length
332             std::advance(itrToKwds, sizeof(types::KwSize));
333         }
334 
335         // support all the Keywords
336         auto stop = std::next(itrToKwds, kwdDataLength);
337         std::string kwdata(itrToKwds, stop);
338         kwdValueMap.emplace(std::move(kwdName), std::move(kwdata));
339 
340         // Jump past keyword data length
341         std::advance(itrToKwds, kwdDataLength);
342     }
343 
344     return kwdValueMap;
345 }
346 
processRecord(auto recordOffset)347 void IpzVpdParser::processRecord(auto recordOffset)
348 {
349     // Jump to record name
350     auto recordNameOffset =
351         recordOffset + sizeof(types::RecordId) + sizeof(types::RecordSize) +
352         // Skip past the RT keyword, which contains
353         // the record name.
354         Length::KW_NAME + sizeof(types::KwSize);
355 
356     // Get record name
357     auto itrToVPDStart = m_vpdVector.cbegin();
358     std::advance(itrToVPDStart, recordNameOffset);
359 
360     std::string recordName(itrToVPDStart, itrToVPDStart + Length::RECORD_NAME);
361 
362     // proceed to find contained keywords and their values.
363     std::advance(itrToVPDStart, Length::RECORD_NAME);
364 
365     // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
366     std::advance(itrToVPDStart, -(Length::KW_NAME + sizeof(types::KwSize) +
367                                   Length::RECORD_NAME));
368 
369     // Add entry for this record (and contained keyword:value pairs)
370     // to the parsed vpd output.
371     m_parsedVPDMap.emplace(std::move(recordName),
372                            std::move(readKeywords(itrToVPDStart)));
373 }
374 
parse()375 types::VPDMapVariant IpzVpdParser::parse()
376 {
377     try
378     {
379         auto itrToVPD = m_vpdVector.cbegin();
380 
381         // Check vaidity of VHDR record
382         checkHeader(itrToVPD);
383 
384         // Read the table of contents
385         auto ptLen = readTOC(itrToVPD);
386 
387         // Read the table of contents record, to get offsets
388         // to other records.
389         auto l_result = readPT(itrToVPD, ptLen);
390         auto recordOffsets = l_result.first;
391         for (const auto& offset : recordOffsets)
392         {
393             processRecord(offset);
394         }
395 
396         if (!processInvalidRecords(l_result.second))
397         {
398             logging::logMessage("Failed to process invalid records for [" +
399                                 m_vpdFilePath + "]");
400         }
401 
402         return m_parsedVPDMap;
403     }
404     catch (const std::exception& e)
405     {
406         logging::logMessage(e.what());
407         throw e;
408     }
409 }
410 
getKeywordValueFromRecord(const types::Record & i_recordName,const types::Keyword & i_keywordName,const types::RecordOffset & i_recordDataOffset)411 types::BinaryVector IpzVpdParser::getKeywordValueFromRecord(
412     const types::Record& i_recordName, const types::Keyword& i_keywordName,
413     const types::RecordOffset& i_recordDataOffset)
414 {
415     auto l_iterator = m_vpdVector.cbegin();
416 
417     // Go to the record name in the given record's offset
418     std::ranges::advance(l_iterator,
419                          i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
420                          m_vpdVector.cend());
421 
422     // Check if the record is present in the given record's offset
423     if (i_recordName !=
424         std::string(l_iterator,
425                     std::ranges::next(l_iterator, Length::RECORD_NAME,
426                                       m_vpdVector.cend())))
427     {
428         throw std::runtime_error(
429             "Given record is not present in the offset provided");
430     }
431 
432     std::ranges::advance(l_iterator, Length::RECORD_NAME, m_vpdVector.cend());
433 
434     std::string l_kwName = std::string(
435         l_iterator,
436         std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
437 
438     // Iterate through the keywords until the last keyword PF is found.
439     while (l_kwName != constants::LAST_KW)
440     {
441         // First character required for #D keyword check
442         char l_kwNameStart = *l_iterator;
443 
444         std::ranges::advance(l_iterator, Length::KW_NAME, m_vpdVector.cend());
445 
446         // Get the keyword's data length
447         auto l_kwdDataLength = 0;
448 
449         if (constants::POUND_KW == l_kwNameStart)
450         {
451             l_kwdDataLength = readUInt16LE(l_iterator);
452             std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
453                                  m_vpdVector.cend());
454         }
455         else
456         {
457             l_kwdDataLength = *l_iterator;
458             std::ranges::advance(l_iterator, sizeof(types::KwSize),
459                                  m_vpdVector.cend());
460         }
461 
462         if (l_kwName == i_keywordName)
463         {
464             // Return keyword's value to the caller
465             return types::BinaryVector(
466                 l_iterator, std::ranges::next(l_iterator, l_kwdDataLength,
467                                               m_vpdVector.cend()));
468         }
469 
470         // next keyword search
471         std::ranges::advance(l_iterator, l_kwdDataLength, m_vpdVector.cend());
472 
473         // next keyword name
474         l_kwName = std::string(
475             l_iterator,
476             std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
477     }
478 
479     // Keyword not found
480     throw std::runtime_error("Given keyword not found.");
481 }
482 
getRecordDetailsFromVTOC(const types::Record & i_recordName,const types::RecordOffset & i_vtocOffset)483 types::RecordData IpzVpdParser::getRecordDetailsFromVTOC(
484     const types::Record& i_recordName, const types::RecordOffset& i_vtocOffset)
485 {
486     // Get VTOC's PT keyword value.
487     const auto l_vtocPTKwValue =
488         getKeywordValueFromRecord("VTOC", "PT", i_vtocOffset);
489 
490     // Parse through VTOC PT keyword value to find the record which we are
491     // interested in.
492     auto l_vtocPTItr = l_vtocPTKwValue.cbegin();
493 
494     types::RecordData l_recordData;
495 
496     while (l_vtocPTItr < l_vtocPTKwValue.cend())
497     {
498         if (i_recordName ==
499             std::string(l_vtocPTItr, l_vtocPTItr + Length::RECORD_NAME))
500         {
501             // Record found in VTOC PT keyword. Get offset
502             std::ranges::advance(l_vtocPTItr,
503                                  Length::RECORD_NAME + Length::RECORD_TYPE,
504                                  l_vtocPTKwValue.cend());
505             const auto l_recordOffset = readUInt16LE(l_vtocPTItr);
506 
507             std::ranges::advance(l_vtocPTItr, Length::RECORD_OFFSET,
508                                  l_vtocPTKwValue.cend());
509             const auto l_recordLength = readUInt16LE(l_vtocPTItr);
510 
511             std::ranges::advance(l_vtocPTItr, Length::RECORD_LENGTH,
512                                  l_vtocPTKwValue.cend());
513             const auto l_eccOffset = readUInt16LE(l_vtocPTItr);
514 
515             std::ranges::advance(l_vtocPTItr, Length::RECORD_ECC_OFFSET,
516                                  l_vtocPTKwValue.cend());
517             const auto l_eccLength = readUInt16LE(l_vtocPTItr);
518 
519             l_recordData = std::make_tuple(l_recordOffset, l_recordLength,
520                                            l_eccOffset, l_eccLength);
521             break;
522         }
523 
524         std::ranges::advance(l_vtocPTItr, Length::SKIP_A_RECORD_IN_PT,
525                              l_vtocPTKwValue.cend());
526     }
527 
528     return l_recordData;
529 }
530 
readKeywordFromHardware(const types::ReadVpdParams i_paramsToReadData)531 types::DbusVariantType IpzVpdParser::readKeywordFromHardware(
532     const types::ReadVpdParams i_paramsToReadData)
533 {
534     // Extract record and keyword from i_paramsToReadData
535     types::Record l_record;
536     types::Keyword l_keyword;
537 
538     if (const types::IpzType* l_ipzData =
539             std::get_if<types::IpzType>(&i_paramsToReadData))
540     {
541         l_record = std::get<0>(*l_ipzData);
542         l_keyword = std::get<1>(*l_ipzData);
543     }
544     else
545     {
546         logging::logMessage(
547             "Input parameter type provided isn't compatible with the given VPD type.");
548         throw types::DbusInvalidArgument();
549     }
550 
551     // Read keyword's value from vector
552     auto l_itrToVPD = m_vpdVector.cbegin();
553 
554     if (l_record == "VHDR")
555     {
556 // Disable providing a way to read keywords from VHDR for the time being.
557 #if 0
558         std::ranges::advance(l_itrToVPD, Offset::VHDR_RECORD,
559                              m_vpdVector.cend());
560 
561         return types::DbusVariantType{getKeywordValueFromRecord(
562             l_record, l_keyword, Offset::VHDR_RECORD)};
563 #endif
564 
565         logging::logMessage("Read cannot be performed on VHDR record.");
566         throw types::DbusInvalidArgument();
567     }
568 
569     // Get VTOC offset
570     std::ranges::advance(l_itrToVPD, Offset::VTOC_PTR, m_vpdVector.cend());
571     auto l_vtocOffset = readUInt16LE(l_itrToVPD);
572 
573     if (l_record == "VTOC")
574     {
575         // Disable providing a way to read keywords from VTOC for the time
576         // being.
577 #if 0
578         return types::DbusVariantType{
579             getKeywordValueFromRecord(l_record, l_keyword, l_vtocOffset)};
580 #endif
581 
582         logging::logMessage("Read cannot be performed on VTOC record.");
583         throw types::DbusInvalidArgument();
584     }
585 
586     // Get record offset from VTOC's PT keyword value.
587     auto l_recordData = getRecordDetailsFromVTOC(l_record, l_vtocOffset);
588     const auto l_recordOffset = std::get<0>(l_recordData);
589 
590     if (l_recordOffset == 0)
591     {
592         throw std::runtime_error("Record not found in VTOC PT keyword.");
593     }
594 
595     // Get the given keyword's value
596     return types::DbusVariantType{
597         getKeywordValueFromRecord(l_record, l_keyword, l_recordOffset)};
598 }
599 
updateRecordECC(const auto & i_recordDataOffset,const auto & i_recordDataLength,const auto & i_recordECCOffset,size_t i_recordECCLength,types::BinaryVector & io_vpdVector)600 void IpzVpdParser::updateRecordECC(
601     const auto& i_recordDataOffset, const auto& i_recordDataLength,
602     const auto& i_recordECCOffset, size_t i_recordECCLength,
603     types::BinaryVector& io_vpdVector)
604 {
605     auto l_recordDataBegin =
606         std::next(io_vpdVector.begin(), i_recordDataOffset);
607 
608     auto l_recordECCBegin = std::next(io_vpdVector.begin(), i_recordECCOffset);
609 
610     auto l_eccStatus = vpdecc_create_ecc(
611         const_cast<uint8_t*>(&l_recordDataBegin[0]), i_recordDataLength,
612         const_cast<uint8_t*>(&l_recordECCBegin[0]), &i_recordECCLength);
613 
614     if (l_eccStatus != VPD_ECC_OK)
615     {
616         throw(EccException("ECC update failed with error " + l_eccStatus));
617     }
618 
619     auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
620 
621     m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
622 
623     std::copy(l_recordECCBegin, l_recordECCEnd,
624               std::ostreambuf_iterator<char>(m_vpdFileStream));
625 }
626 
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)627 int IpzVpdParser::setKeywordValueInRecord(
628     const types::Record& i_recordName, const types::Keyword& i_keywordName,
629     const types::BinaryVector& i_keywordData,
630     const types::RecordOffset& i_recordDataOffset,
631     types::BinaryVector& io_vpdVector)
632 {
633     auto l_iterator = io_vpdVector.begin();
634 
635     // Go to the record name in the given record's offset
636     std::ranges::advance(l_iterator,
637                          i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
638                          io_vpdVector.end());
639 
640     const std::string l_recordFound(
641         l_iterator,
642         std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
643 
644     // Check if the record is present in the given record's offset
645     if (i_recordName != l_recordFound)
646     {
647         throw(DataException("Given record found at the offset " +
648                             std::to_string(i_recordDataOffset) + " is : " +
649                             l_recordFound + " and not " + i_recordName));
650     }
651 
652     std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
653 
654     std::string l_kwName = std::string(
655         l_iterator,
656         std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
657 
658     // Iterate through the keywords until the last keyword PF is found.
659     while (l_kwName != constants::LAST_KW)
660     {
661         // First character required for #D keyword check
662         char l_kwNameStart = *l_iterator;
663 
664         std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
665 
666         // Find the keyword's data length
667         size_t l_kwdDataLength = 0;
668 
669         if (constants::POUND_KW == l_kwNameStart)
670         {
671             l_kwdDataLength = readUInt16LE(l_iterator);
672             std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
673                                  io_vpdVector.end());
674         }
675         else
676         {
677             l_kwdDataLength = *l_iterator;
678             std::ranges::advance(l_iterator, sizeof(types::KwSize),
679                                  io_vpdVector.end());
680         }
681 
682         if (l_kwName == i_keywordName)
683         {
684             // Before writing the keyword's value, get the maximum size that can
685             // be updated.
686             const auto l_lengthToUpdate =
687                 i_keywordData.size() <= l_kwdDataLength
688                     ? i_keywordData.size()
689                     : l_kwdDataLength;
690 
691             // Set the keyword's value on vector. This is required to update the
692             // record's ECC based on the new value set.
693             const auto i_keywordDataEnd = std::ranges::next(
694                 i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
695 
696             std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
697 
698             // Set the keyword's value on hardware
699             const auto l_kwdDataOffset =
700                 std::distance(io_vpdVector.begin(), l_iterator);
701             m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
702                                   std::ios::beg);
703 
704             std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
705                       std::ostreambuf_iterator<char>(m_vpdFileStream));
706 
707             // return no of bytes set
708             return l_lengthToUpdate;
709         }
710 
711         // next keyword search
712         std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
713 
714         // next keyword name
715         l_kwName = std::string(
716             l_iterator,
717             std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
718     }
719 
720     // Keyword not found
721     throw(DataException(
722         "Keyword " + i_keywordName + " not found in record " + i_recordName));
723 }
724 
writeKeywordOnHardware(const types::WriteVpdParams i_paramsToWriteData)725 int IpzVpdParser::writeKeywordOnHardware(
726     const types::WriteVpdParams i_paramsToWriteData)
727 {
728     int l_sizeWritten = -1;
729 
730     try
731     {
732         types::Record l_recordName;
733         types::Keyword l_keywordName;
734         types::BinaryVector l_keywordData;
735 
736         // Extract record, keyword and value from i_paramsToWriteData
737         if (const types::IpzData* l_ipzData =
738                 std::get_if<types::IpzData>(&i_paramsToWriteData))
739         {
740             l_recordName = std::get<0>(*l_ipzData);
741             l_keywordName = std::get<1>(*l_ipzData);
742             l_keywordData = std::get<2>(*l_ipzData);
743         }
744         else
745         {
746             logging::logMessage(
747                 "Input parameter type provided isn't compatible with the given FRU's VPD type.");
748             throw types::DbusInvalidArgument();
749         }
750 
751         if (l_recordName == "VHDR" || l_recordName == "VTOC")
752         {
753             logging::logMessage(
754                 "Write operation not allowed on the given record : " +
755                 l_recordName);
756             throw types::DbusNotAllowed();
757         }
758 
759         if (l_keywordData.size() == 0)
760         {
761             logging::logMessage(
762                 "Write operation not allowed as the given keyword's data length is 0.");
763             throw types::DbusInvalidArgument();
764         }
765 
766         auto l_vpdBegin = m_vpdVector.begin();
767 
768         // Get VTOC offset
769         std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
770         auto l_vtocOffset = readUInt16LE(l_vpdBegin);
771 
772         // Get the details of user given record from VTOC
773         const types::RecordData& l_inputRecordDetails =
774             getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
775 
776         const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
777 
778         if (l_inputRecordOffset == 0)
779         {
780             throw(DataException("Record not found in VTOC PT keyword."));
781         }
782 
783         // Create a local copy of m_vpdVector to perform keyword update and ecc
784         // update on filestream.
785         types::BinaryVector l_vpdVector = m_vpdVector;
786 
787         // write keyword's value on hardware
788         l_sizeWritten =
789             setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
790                                     l_inputRecordOffset, l_vpdVector);
791 
792         if (l_sizeWritten <= 0)
793         {
794             throw(DataException("Unable to set value on " + l_recordName + ":" +
795                                 l_keywordName));
796         }
797 
798         // Update the record's ECC
799         updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
800                         std::get<2>(l_inputRecordDetails),
801                         std::get<3>(l_inputRecordDetails), l_vpdVector);
802 
803         logging::logMessage(std::to_string(l_sizeWritten) +
804                             " bytes updated successfully on hardware for " +
805                             l_recordName + ":" + l_keywordName);
806     }
807     catch (const std::exception& l_exception)
808     {
809         throw;
810     }
811 
812     return l_sizeWritten;
813 }
814 
processInvalidRecords(const types::InvalidRecordList & i_invalidRecordList) const815 bool IpzVpdParser::processInvalidRecords(
816     const types::InvalidRecordList& i_invalidRecordList) const noexcept
817 {
818     bool l_rc{true};
819     if (!i_invalidRecordList.empty())
820     {
821         auto l_invalidRecordToString =
822             [](const types::InvalidRecordEntry& l_record) {
823                 return std::string{
824                     "{" + l_record.first + "," +
825                     EventLogger::getErrorTypeString(l_record.second) + "}"};
826             };
827 
828         std::string l_invalidRecordListString{"["};
829         try
830         {
831             for (const auto& l_entry : i_invalidRecordList)
832             {
833                 l_invalidRecordListString +=
834                     l_invalidRecordToString(l_entry) + ",";
835             }
836             l_invalidRecordListString += "]";
837         }
838         catch (const std::exception& l_ex)
839         {
840             l_invalidRecordListString = "";
841         }
842 
843         // Log a Predictive PEL, including names and respective error messages
844         // of all invalid records
845         EventLogger::createSyncPelWithInvCallOut(
846             types::ErrorType::VpdParseError, types::SeverityType::Warning,
847             __FILE__, __FUNCTION__, constants::VALUE_0,
848             std::string(
849                 "Check failed for record(s) while parsing VPD. Check user data for reason and list of failed record(s). Re-program VPD."),
850             std::vector{
851                 std::make_tuple(m_vpdFilePath, types::CalloutPriority::High)},
852             l_invalidRecordListString, std::nullopt, std::nullopt,
853             std::nullopt);
854 
855         // Dump Bad VPD to file
856         if (constants::SUCCESS !=
857             vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector))
858         {
859             l_rc = false;
860         }
861     }
862     return l_rc;
863 }
864 } // namespace vpd
865