1 #include "config.h" 2 3 #include "ipz_parser.hpp" 4 5 #include "vpdecc/vpdecc.h" 6 7 #include "constants.hpp" 8 #include "event_logger.hpp" 9 #include "exceptions.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 */ 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 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 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); 81 } 82 else if (l_status != VPD_ECC_OK) 83 { 84 return false; 85 } 86 87 return true; 88 } 89 90 bool 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 111 // To avoid 1 bit flip correction from corrupting the main buffer. 112 const types::BinaryVector tempVector = m_vpdVector; 113 // Reset pointer to start of the vpd, 114 // so that Offset will point to correct address 115 vpdPtr = tempVector.cbegin(); 116 117 auto l_status = vpdecc_check_data( 118 const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength, 119 const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength); 120 if (l_status == VPD_ECC_CORRECTABLE_DATA) 121 { 122 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); 127 } 128 else if (l_status != VPD_ECC_OK) 129 { 130 return false; 131 } 132 133 return true; 134 } 135 136 bool 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 159 // To avoid 1 bit flip correction from corrupting the main buffer. 160 const types::BinaryVector tempVector = m_vpdVector; 161 auto vpdPtr = tempVector.cbegin(); 162 163 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) 168 { 169 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; 178 } 179 180 return true; 181 } 182 183 void 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 205 auto 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 246 std::pair<types::RecordOffsetList, types::InvalidRecordList> 247 IpzVpdParser::readPT(types::BinaryVector::const_iterator& itrToPT, 248 auto ptLength) 249 { 250 types::RecordOffsetList recordOffsets; 251 252 // List of names of all invalid records found. 253 types::InvalidRecordList l_invalidRecordList; 254 255 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 } 276 catch (const std::exception& l_ex) 277 { 278 logging::logMessage(l_ex.what()); 279 280 // add the invalid record name and exception object to list 281 l_invalidRecordList.emplace_back(types::InvalidRecordEntry{ 282 recordName, EventLogger::getErrorType(l_ex)}); 283 } 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 291 return std::make_pair(recordOffsets, l_invalidRecordList); 292 } 293 294 types::IPZVpdMap::mapped_type IpzVpdParser::readKeywords( 295 types::BinaryVector::const_iterator& itrToKwds) 296 { 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 347 void 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 375 types::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. 389 auto l_result = readPT(itrToVPD, ptLen); 390 auto recordOffsets = l_result.first; 391 for (const auto& offset : recordOffsets) 392 { 393 processRecord(offset); 394 } 395 396 if (!processInvalidRecords(l_result.second)) 397 { 398 logging::logMessage("Failed to process invalid records for [" + 399 m_vpdFilePath + "]"); 400 } 401 402 return m_parsedVPDMap; 403 } 404 catch (const std::exception& e) 405 { 406 logging::logMessage(e.what()); 407 throw e; 408 } 409 } 410 411 types::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 483 types::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 531 types::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 600 void 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 627 int 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 725 int 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 } 814 815 bool 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::createSyncPelWithInvCallOut( 846 types::ErrorType::VpdParseError, types::SeverityType::Warning, 847 __FILE__, __FUNCTION__, constants::VALUE_0, 848 std::string( 849 "Check failed for record(s) while parsing VPD. Check user data for reason and list of failed record(s). Re-program VPD."), 850 std::vector{ 851 std::make_tuple(m_vpdFilePath, types::CalloutPriority::High)}, 852 l_invalidRecordListString, std::nullopt, std::nullopt, 853 std::nullopt); 854 855 // Dump Bad VPD to file 856 if (constants::SUCCESS != 857 vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector)) 858 { 859 l_rc = false; 860 } 861 } 862 return l_rc; 863 } 864 } // namespace vpd 865