blob: 828c17cdd5b34cdfe8fc8c1221894e5492eaa004 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#include "config.h"
2
3#include "ipz_parser.hpp"
4
5#include "vpdecc/vpdecc.h"
6
7#include "constants.hpp"
Sunny Srivastava1334be32025-04-07 14:57:08 +05308#include "event_logger.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05009#include "exceptions.hpp"
Souvik Roy612bce82025-04-16 02:22:33 -050010#include "utility/vpd_specific_utility.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050011
12#include <nlohmann/json.hpp>
13
14#include <typeindex>
15
16namespace vpd
17{
18
19// Offset of different entries in VPD data.
20enum 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.
34enum 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 */
55static 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
63bool IpzVpdParser::vhdrEccCheck()
64{
Sunny Srivastava1334be32025-04-07 14:57:08 +053065 // To avoid 1 bit flip correction from corrupting the main buffer.
66 const types::BinaryVector tempVector = m_vpdVector;
67 auto vpdPtr = tempVector.cbegin();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050068
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 {
Sunny Srivastava1334be32025-04-07 14:57:08 +053076 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);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050081 }
82 else if (l_status != VPD_ECC_OK)
83 {
84 return false;
85 }
86
87 return true;
88}
89
90bool 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
Sunny Srivastava1334be32025-04-07 14:57:08 +0530111 // To avoid 1 bit flip correction from corrupting the main buffer.
112 const types::BinaryVector tempVector = m_vpdVector;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500113 // Reset pointer to start of the vpd,
114 // so that Offset will point to correct address
Sunny Srivastava1334be32025-04-07 14:57:08 +0530115 vpdPtr = tempVector.cbegin();
116
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500117 auto l_status = vpdecc_check_data(
Sunny Srivastava1334be32025-04-07 14:57:08 +0530118 const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
119 const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500120 if (l_status == VPD_ECC_CORRECTABLE_DATA)
121 {
Sunny Srivastava1334be32025-04-07 14:57:08 +0530122 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);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500127 }
128 else if (l_status != VPD_ECC_OK)
129 {
130 return false;
131 }
132
133 return true;
134}
135
136bool 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
Sunny Srivastava1334be32025-04-07 14:57:08 +0530159 // To avoid 1 bit flip correction from corrupting the main buffer.
160 const types::BinaryVector tempVector = m_vpdVector;
161 auto vpdPtr = tempVector.cbegin();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500162
Sunny Srivastava1334be32025-04-07 14:57:08 +0530163 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)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500168 {
Sunny Srivastava1334be32025-04-07 14:57:08 +0530169 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;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500178 }
179
Sunny Srivastava1334be32025-04-07 14:57:08 +0530180 return true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500181}
182
183void 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
205auto 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
Souvik Roy612bce82025-04-16 02:22:33 -0500246std::pair<types::RecordOffsetList, types::InvalidRecordList>
247 IpzVpdParser::readPT(types::BinaryVector::const_iterator& itrToPT,
248 auto ptLength)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500249{
250 types::RecordOffsetList recordOffsets;
251
Souvik Roy612bce82025-04-16 02:22:33 -0500252 // List of names of all invalid records found.
253 types::InvalidRecordList l_invalidRecordList;
254
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500255 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 }
Souvik Roy612bce82025-04-16 02:22:33 -0500276 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500277 {
Souvik Roy612bce82025-04-16 02:22:33 -0500278 logging::logMessage(l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500279
Souvik Roy612bce82025-04-16 02:22:33 -0500280 // add the invalid record name and exception object to list
281 l_invalidRecordList.emplace_back(types::InvalidRecordEntry{
282 recordName, EventLogger::getErrorType(l_ex)});
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500283 }
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
Souvik Roy612bce82025-04-16 02:22:33 -0500291 return std::make_pair(recordOffsets, l_invalidRecordList);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500292}
293
Patrick Williams43fedab2025-02-03 14:28:05 -0500294types::IPZVpdMap::mapped_type IpzVpdParser::readKeywords(
295 types::BinaryVector::const_iterator& itrToKwds)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500296{
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
347void 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
375types::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.
Souvik Roy612bce82025-04-16 02:22:33 -0500389 auto l_result = readPT(itrToVPD, ptLen);
390 auto recordOffsets = l_result.first;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500391 for (const auto& offset : recordOffsets)
392 {
393 processRecord(offset);
394 }
395
Souvik Roy612bce82025-04-16 02:22:33 -0500396 if (!processInvalidRecords(l_result.second))
397 {
398 logging::logMessage("Failed to process invalid records for [" +
399 m_vpdFilePath + "]");
400 }
401
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500402 return m_parsedVPDMap;
403 }
404 catch (const std::exception& e)
405 {
406 logging::logMessage(e.what());
407 throw e;
408 }
409}
410
411types::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
483types::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
531types::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
600void 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("ECC update failed with error " + l_eccStatus));
617 }
618
619 auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
620
621 m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
622
623 std::copy(l_recordECCBegin, l_recordECCEnd,
624 std::ostreambuf_iterator<char>(m_vpdFileStream));
625}
626
627int IpzVpdParser::setKeywordValueInRecord(
628 const types::Record& i_recordName, const types::Keyword& i_keywordName,
629 const types::BinaryVector& i_keywordData,
630 const types::RecordOffset& i_recordDataOffset,
631 types::BinaryVector& io_vpdVector)
632{
633 auto l_iterator = io_vpdVector.begin();
634
635 // Go to the record name in the given record's offset
636 std::ranges::advance(l_iterator,
637 i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
638 io_vpdVector.end());
639
640 const std::string l_recordFound(
641 l_iterator,
642 std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
643
644 // Check if the record is present in the given record's offset
645 if (i_recordName != l_recordFound)
646 {
647 throw(DataException("Given record found at the offset " +
648 std::to_string(i_recordDataOffset) + " is : " +
649 l_recordFound + " and not " + i_recordName));
650 }
651
652 std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
653
654 std::string l_kwName = std::string(
655 l_iterator,
656 std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
657
658 // Iterate through the keywords until the last keyword PF is found.
659 while (l_kwName != constants::LAST_KW)
660 {
661 // First character required for #D keyword check
662 char l_kwNameStart = *l_iterator;
663
664 std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
665
666 // Find the keyword's data length
667 size_t l_kwdDataLength = 0;
668
669 if (constants::POUND_KW == l_kwNameStart)
670 {
671 l_kwdDataLength = readUInt16LE(l_iterator);
672 std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
673 io_vpdVector.end());
674 }
675 else
676 {
677 l_kwdDataLength = *l_iterator;
678 std::ranges::advance(l_iterator, sizeof(types::KwSize),
679 io_vpdVector.end());
680 }
681
682 if (l_kwName == i_keywordName)
683 {
684 // Before writing the keyword's value, get the maximum size that can
685 // be updated.
686 const auto l_lengthToUpdate =
687 i_keywordData.size() <= l_kwdDataLength
688 ? i_keywordData.size()
689 : l_kwdDataLength;
690
691 // Set the keyword's value on vector. This is required to update the
692 // record's ECC based on the new value set.
693 const auto i_keywordDataEnd = std::ranges::next(
694 i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
695
696 std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
697
698 // Set the keyword's value on hardware
699 const auto l_kwdDataOffset =
700 std::distance(io_vpdVector.begin(), l_iterator);
701 m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
702 std::ios::beg);
703
704 std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
705 std::ostreambuf_iterator<char>(m_vpdFileStream));
706
707 // return no of bytes set
708 return l_lengthToUpdate;
709 }
710
711 // next keyword search
712 std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
713
714 // next keyword name
715 l_kwName = std::string(
716 l_iterator,
717 std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
718 }
719
720 // Keyword not found
721 throw(DataException(
722 "Keyword " + i_keywordName + " not found in record " + i_recordName));
723}
724
725int IpzVpdParser::writeKeywordOnHardware(
726 const types::WriteVpdParams i_paramsToWriteData)
727{
728 int l_sizeWritten = -1;
729
730 try
731 {
732 types::Record l_recordName;
733 types::Keyword l_keywordName;
734 types::BinaryVector l_keywordData;
735
736 // Extract record, keyword and value from i_paramsToWriteData
737 if (const types::IpzData* l_ipzData =
738 std::get_if<types::IpzData>(&i_paramsToWriteData))
739 {
740 l_recordName = std::get<0>(*l_ipzData);
741 l_keywordName = std::get<1>(*l_ipzData);
742 l_keywordData = std::get<2>(*l_ipzData);
743 }
744 else
745 {
746 logging::logMessage(
747 "Input parameter type provided isn't compatible with the given FRU's VPD type.");
748 throw types::DbusInvalidArgument();
749 }
750
751 if (l_recordName == "VHDR" || l_recordName == "VTOC")
752 {
753 logging::logMessage(
754 "Write operation not allowed on the given record : " +
755 l_recordName);
756 throw types::DbusNotAllowed();
757 }
758
759 if (l_keywordData.size() == 0)
760 {
761 logging::logMessage(
762 "Write operation not allowed as the given keyword's data length is 0.");
763 throw types::DbusInvalidArgument();
764 }
765
766 auto l_vpdBegin = m_vpdVector.begin();
767
768 // Get VTOC offset
769 std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
770 auto l_vtocOffset = readUInt16LE(l_vpdBegin);
771
772 // Get the details of user given record from VTOC
773 const types::RecordData& l_inputRecordDetails =
774 getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
775
776 const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
777
778 if (l_inputRecordOffset == 0)
779 {
780 throw(DataException("Record not found in VTOC PT keyword."));
781 }
782
783 // Create a local copy of m_vpdVector to perform keyword update and ecc
784 // update on filestream.
785 types::BinaryVector l_vpdVector = m_vpdVector;
786
787 // write keyword's value on hardware
788 l_sizeWritten =
789 setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
790 l_inputRecordOffset, l_vpdVector);
791
792 if (l_sizeWritten <= 0)
793 {
794 throw(DataException("Unable to set value on " + l_recordName + ":" +
795 l_keywordName));
796 }
797
798 // Update the record's ECC
799 updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
800 std::get<2>(l_inputRecordDetails),
801 std::get<3>(l_inputRecordDetails), l_vpdVector);
802
803 logging::logMessage(std::to_string(l_sizeWritten) +
804 " bytes updated successfully on hardware for " +
805 l_recordName + ":" + l_keywordName);
806 }
807 catch (const std::exception& l_exception)
808 {
809 throw;
810 }
811
812 return l_sizeWritten;
813}
Souvik Roy612bce82025-04-16 02:22:33 -0500814
815bool IpzVpdParser::processInvalidRecords(
816 const types::InvalidRecordList& i_invalidRecordList) const noexcept
817{
818 bool l_rc{true};
819 if (!i_invalidRecordList.empty())
820 {
821 auto l_invalidRecordToString =
822 [](const types::InvalidRecordEntry& l_record) {
823 return std::string{
824 "{" + l_record.first + "," +
825 EventLogger::getErrorTypeString(l_record.second) + "}"};
826 };
827
828 std::string l_invalidRecordListString{"["};
829 try
830 {
831 for (const auto& l_entry : i_invalidRecordList)
832 {
833 l_invalidRecordListString +=
834 l_invalidRecordToString(l_entry) + ",";
835 }
836 l_invalidRecordListString += "]";
837 }
838 catch (const std::exception& l_ex)
839 {
840 l_invalidRecordListString = "";
841 }
842
843 // Log a Predictive PEL, including names and respective error messages
844 // of all invalid records
845 EventLogger::createSyncPel(
846 types::ErrorType::InvalidVpdMessage, types::SeverityType::Warning,
847 __FILE__, __FUNCTION__, constants::VALUE_0,
848 std::string("Invalid records found while parsing VPD for [" +
849 m_vpdFilePath + "]"),
850 l_invalidRecordListString, std::nullopt, std::nullopt,
851 std::nullopt);
852
853 // Dump Bad VPD to file
854 if (constants::SUCCESS !=
855 vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector))
856 {
857 l_rc = false;
858 }
859 }
860 return l_rc;
861}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500862} // namespace vpd