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