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