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