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