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