xref: /openbmc/openpower-vpd-parser/vpd-manager/src/ipz_parser.cpp (revision b00b92affa78574fc608e21a1612db19592b2d6b)
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(
617             "ECC update failed with error " + std::to_string(l_eccStatus)));
618     }
619 
620     auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
621 
622     m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
623 
624     std::copy(l_recordECCBegin, l_recordECCEnd,
625               std::ostreambuf_iterator<char>(m_vpdFileStream));
626 }
627 
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)628 int IpzVpdParser::setKeywordValueInRecord(
629     const types::Record& i_recordName, const types::Keyword& i_keywordName,
630     const types::BinaryVector& i_keywordData,
631     const types::RecordOffset& i_recordDataOffset,
632     types::BinaryVector& io_vpdVector)
633 {
634     auto l_iterator = io_vpdVector.begin();
635 
636     // Go to the record name in the given record's offset
637     std::ranges::advance(l_iterator,
638                          i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
639                          io_vpdVector.end());
640 
641     const std::string l_recordFound(
642         l_iterator,
643         std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
644 
645     // Check if the record is present in the given record's offset
646     if (i_recordName != l_recordFound)
647     {
648         throw(DataException("Given record found at the offset " +
649                             std::to_string(i_recordDataOffset) + " is : " +
650                             l_recordFound + " and not " + i_recordName));
651     }
652 
653     std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
654 
655     std::string l_kwName = std::string(
656         l_iterator,
657         std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
658 
659     // Iterate through the keywords until the last keyword PF is found.
660     while (l_kwName != constants::LAST_KW)
661     {
662         // First character required for #D keyword check
663         char l_kwNameStart = *l_iterator;
664 
665         std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
666 
667         // Find the keyword's data length
668         size_t l_kwdDataLength = 0;
669 
670         if (constants::POUND_KW == l_kwNameStart)
671         {
672             l_kwdDataLength = readUInt16LE(l_iterator);
673             std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
674                                  io_vpdVector.end());
675         }
676         else
677         {
678             l_kwdDataLength = *l_iterator;
679             std::ranges::advance(l_iterator, sizeof(types::KwSize),
680                                  io_vpdVector.end());
681         }
682 
683         if (l_kwName == i_keywordName)
684         {
685             // Before writing the keyword's value, get the maximum size that can
686             // be updated.
687             const auto l_lengthToUpdate =
688                 i_keywordData.size() <= l_kwdDataLength
689                     ? i_keywordData.size()
690                     : l_kwdDataLength;
691 
692             // Set the keyword's value on vector. This is required to update the
693             // record's ECC based on the new value set.
694             const auto i_keywordDataEnd = std::ranges::next(
695                 i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
696 
697             std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
698 
699             // Set the keyword's value on hardware
700             const auto l_kwdDataOffset =
701                 std::distance(io_vpdVector.begin(), l_iterator);
702             m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
703                                   std::ios::beg);
704 
705             std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
706                       std::ostreambuf_iterator<char>(m_vpdFileStream));
707 
708             // return no of bytes set
709             return l_lengthToUpdate;
710         }
711 
712         // next keyword search
713         std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
714 
715         // next keyword name
716         l_kwName = std::string(
717             l_iterator,
718             std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
719     }
720 
721     // Keyword not found
722     throw(DataException(
723         "Keyword " + i_keywordName + " not found in record " + i_recordName));
724 }
725 
writeKeywordOnHardware(const types::WriteVpdParams i_paramsToWriteData)726 int IpzVpdParser::writeKeywordOnHardware(
727     const types::WriteVpdParams i_paramsToWriteData)
728 {
729     int l_sizeWritten = -1;
730 
731     try
732     {
733         types::Record l_recordName;
734         types::Keyword l_keywordName;
735         types::BinaryVector l_keywordData;
736 
737         // Extract record, keyword and value from i_paramsToWriteData
738         if (const types::IpzData* l_ipzData =
739                 std::get_if<types::IpzData>(&i_paramsToWriteData))
740         {
741             l_recordName = std::get<0>(*l_ipzData);
742             l_keywordName = std::get<1>(*l_ipzData);
743             l_keywordData = std::get<2>(*l_ipzData);
744         }
745         else
746         {
747             logging::logMessage(
748                 "Input parameter type provided isn't compatible with the given FRU's VPD type.");
749             throw types::DbusInvalidArgument();
750         }
751 
752         if (l_recordName == "VHDR" || l_recordName == "VTOC")
753         {
754             logging::logMessage(
755                 "Write operation not allowed on the given record : " +
756                 l_recordName);
757             throw types::DbusNotAllowed();
758         }
759 
760         if (l_keywordData.size() == 0)
761         {
762             logging::logMessage(
763                 "Write operation not allowed as the given keyword's data length is 0.");
764             throw types::DbusInvalidArgument();
765         }
766 
767         auto l_vpdBegin = m_vpdVector.begin();
768 
769         // Get VTOC offset
770         std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
771         auto l_vtocOffset = readUInt16LE(l_vpdBegin);
772 
773         // Get the details of user given record from VTOC
774         const types::RecordData& l_inputRecordDetails =
775             getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
776 
777         const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
778 
779         if (l_inputRecordOffset == 0)
780         {
781             throw(DataException("Record not found in VTOC PT keyword."));
782         }
783 
784         // Create a local copy of m_vpdVector to perform keyword update and ecc
785         // update on filestream.
786         types::BinaryVector l_vpdVector = m_vpdVector;
787 
788         // write keyword's value on hardware
789         l_sizeWritten =
790             setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
791                                     l_inputRecordOffset, l_vpdVector);
792 
793         if (l_sizeWritten <= 0)
794         {
795             throw(DataException("Unable to set value on " + l_recordName + ":" +
796                                 l_keywordName));
797         }
798 
799         // Update the record's ECC
800         updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
801                         std::get<2>(l_inputRecordDetails),
802                         std::get<3>(l_inputRecordDetails), l_vpdVector);
803 
804         logging::logMessage(std::to_string(l_sizeWritten) +
805                             " bytes updated successfully on hardware for " +
806                             l_recordName + ":" + l_keywordName);
807     }
808     catch (const std::exception& l_exception)
809     {
810         throw;
811     }
812 
813     return l_sizeWritten;
814 }
815 
processInvalidRecords(const types::InvalidRecordList & i_invalidRecordList) const816 bool IpzVpdParser::processInvalidRecords(
817     const types::InvalidRecordList& i_invalidRecordList) const noexcept
818 {
819     bool l_rc{true};
820     if (!i_invalidRecordList.empty())
821     {
822         auto l_invalidRecordToString =
823             [](const types::InvalidRecordEntry& l_record) {
824                 return std::string{
825                     "{" + l_record.first + "," +
826                     EventLogger::getErrorTypeString(l_record.second) + "}"};
827             };
828 
829         std::string l_invalidRecordListString{"["};
830         try
831         {
832             for (const auto& l_entry : i_invalidRecordList)
833             {
834                 l_invalidRecordListString +=
835                     l_invalidRecordToString(l_entry) + ",";
836             }
837             l_invalidRecordListString += "]";
838         }
839         catch (const std::exception& l_ex)
840         {
841             l_invalidRecordListString = "";
842         }
843 
844         // Log a Predictive PEL, including names and respective error messages
845         // of all invalid records
846         EventLogger::createSyncPelWithInvCallOut(
847             types::ErrorType::VpdParseError, types::SeverityType::Warning,
848             __FILE__, __FUNCTION__, constants::VALUE_0,
849             std::string(
850                 "Check failed for record(s) while parsing VPD. Check user data for reason and list of failed record(s). Re-program VPD."),
851             std::vector{
852                 std::make_tuple(m_vpdFilePath, types::CalloutPriority::High)},
853             l_invalidRecordListString, std::nullopt, std::nullopt,
854             std::nullopt);
855 
856         // Dump Bad VPD to file
857         if (constants::SUCCESS !=
858             vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector))
859         {
860             l_rc = false;
861         }
862     }
863     return l_rc;
864 }
865 } // namespace vpd
866