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( 617 "ECC update failed with error " + std::to_string(l_eccStatus))); 618 } 619 620 auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength); 621 622 m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg); 623 624 std::copy(l_recordECCBegin, l_recordECCEnd, 625 std::ostreambuf_iterator<char>(m_vpdFileStream)); 626 } 627 628 int IpzVpdParser::setKeywordValueInRecord( 629 const types::Record& i_recordName, const types::Keyword& i_keywordName, 630 const types::BinaryVector& i_keywordData, 631 const types::RecordOffset& i_recordDataOffset, 632 types::BinaryVector& io_vpdVector) 633 { 634 auto l_iterator = io_vpdVector.begin(); 635 636 // Go to the record name in the given record's offset 637 std::ranges::advance(l_iterator, 638 i_recordDataOffset + Length::JUMP_TO_RECORD_NAME, 639 io_vpdVector.end()); 640 641 const std::string l_recordFound( 642 l_iterator, 643 std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end())); 644 645 // Check if the record is present in the given record's offset 646 if (i_recordName != l_recordFound) 647 { 648 throw(DataException("Given record found at the offset " + 649 std::to_string(i_recordDataOffset) + " is : " + 650 l_recordFound + " and not " + i_recordName)); 651 } 652 653 std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end()); 654 655 std::string l_kwName = std::string( 656 l_iterator, 657 std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end())); 658 659 // Iterate through the keywords until the last keyword PF is found. 660 while (l_kwName != constants::LAST_KW) 661 { 662 // First character required for #D keyword check 663 char l_kwNameStart = *l_iterator; 664 665 std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end()); 666 667 // Find the keyword's data length 668 size_t l_kwdDataLength = 0; 669 670 if (constants::POUND_KW == l_kwNameStart) 671 { 672 l_kwdDataLength = readUInt16LE(l_iterator); 673 std::ranges::advance(l_iterator, sizeof(types::PoundKwSize), 674 io_vpdVector.end()); 675 } 676 else 677 { 678 l_kwdDataLength = *l_iterator; 679 std::ranges::advance(l_iterator, sizeof(types::KwSize), 680 io_vpdVector.end()); 681 } 682 683 if (l_kwName == i_keywordName) 684 { 685 // Before writing the keyword's value, get the maximum size that can 686 // be updated. 687 const auto l_lengthToUpdate = 688 i_keywordData.size() <= l_kwdDataLength 689 ? i_keywordData.size() 690 : l_kwdDataLength; 691 692 // Set the keyword's value on vector. This is required to update the 693 // record's ECC based on the new value set. 694 const auto i_keywordDataEnd = std::ranges::next( 695 i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend()); 696 697 std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator); 698 699 // Set the keyword's value on hardware 700 const auto l_kwdDataOffset = 701 std::distance(io_vpdVector.begin(), l_iterator); 702 m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset, 703 std::ios::beg); 704 705 std::copy(i_keywordData.cbegin(), i_keywordDataEnd, 706 std::ostreambuf_iterator<char>(m_vpdFileStream)); 707 708 // return no of bytes set 709 return l_lengthToUpdate; 710 } 711 712 // next keyword search 713 std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end()); 714 715 // next keyword name 716 l_kwName = std::string( 717 l_iterator, 718 std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end())); 719 } 720 721 // Keyword not found 722 throw(DataException( 723 "Keyword " + i_keywordName + " not found in record " + i_recordName)); 724 } 725 726 int IpzVpdParser::writeKeywordOnHardware( 727 const types::WriteVpdParams i_paramsToWriteData) 728 { 729 int l_sizeWritten = -1; 730 731 try 732 { 733 types::Record l_recordName; 734 types::Keyword l_keywordName; 735 types::BinaryVector l_keywordData; 736 737 // Extract record, keyword and value from i_paramsToWriteData 738 if (const types::IpzData* l_ipzData = 739 std::get_if<types::IpzData>(&i_paramsToWriteData)) 740 { 741 l_recordName = std::get<0>(*l_ipzData); 742 l_keywordName = std::get<1>(*l_ipzData); 743 l_keywordData = std::get<2>(*l_ipzData); 744 } 745 else 746 { 747 logging::logMessage( 748 "Input parameter type provided isn't compatible with the given FRU's VPD type."); 749 throw types::DbusInvalidArgument(); 750 } 751 752 if (l_recordName == "VHDR" || l_recordName == "VTOC") 753 { 754 logging::logMessage( 755 "Write operation not allowed on the given record : " + 756 l_recordName); 757 throw types::DbusNotAllowed(); 758 } 759 760 if (l_keywordData.size() == 0) 761 { 762 logging::logMessage( 763 "Write operation not allowed as the given keyword's data length is 0."); 764 throw types::DbusInvalidArgument(); 765 } 766 767 auto l_vpdBegin = m_vpdVector.begin(); 768 769 // Get VTOC offset 770 std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end()); 771 auto l_vtocOffset = readUInt16LE(l_vpdBegin); 772 773 // Get the details of user given record from VTOC 774 const types::RecordData& l_inputRecordDetails = 775 getRecordDetailsFromVTOC(l_recordName, l_vtocOffset); 776 777 const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails); 778 779 if (l_inputRecordOffset == 0) 780 { 781 throw(DataException("Record not found in VTOC PT keyword.")); 782 } 783 784 // Create a local copy of m_vpdVector to perform keyword update and ecc 785 // update on filestream. 786 types::BinaryVector l_vpdVector = m_vpdVector; 787 788 // write keyword's value on hardware 789 l_sizeWritten = 790 setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData, 791 l_inputRecordOffset, l_vpdVector); 792 793 if (l_sizeWritten <= 0) 794 { 795 throw(DataException("Unable to set value on " + l_recordName + ":" + 796 l_keywordName)); 797 } 798 799 // Update the record's ECC 800 updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails), 801 std::get<2>(l_inputRecordDetails), 802 std::get<3>(l_inputRecordDetails), l_vpdVector); 803 804 logging::logMessage(std::to_string(l_sizeWritten) + 805 " bytes updated successfully on hardware for " + 806 l_recordName + ":" + l_keywordName); 807 } 808 catch (const std::exception& l_exception) 809 { 810 throw; 811 } 812 813 return l_sizeWritten; 814 } 815 816 bool IpzVpdParser::processInvalidRecords( 817 const types::InvalidRecordList& i_invalidRecordList) const noexcept 818 { 819 bool l_rc{true}; 820 if (!i_invalidRecordList.empty()) 821 { 822 auto l_invalidRecordToString = 823 [](const types::InvalidRecordEntry& l_record) { 824 return std::string{ 825 "{" + l_record.first + "," + 826 EventLogger::getErrorTypeString(l_record.second) + "}"}; 827 }; 828 829 std::string l_invalidRecordListString{"["}; 830 try 831 { 832 for (const auto& l_entry : i_invalidRecordList) 833 { 834 l_invalidRecordListString += 835 l_invalidRecordToString(l_entry) + ","; 836 } 837 l_invalidRecordListString += "]"; 838 } 839 catch (const std::exception& l_ex) 840 { 841 l_invalidRecordListString = ""; 842 } 843 844 // Log a Predictive PEL, including names and respective error messages 845 // of all invalid records 846 EventLogger::createSyncPelWithInvCallOut( 847 types::ErrorType::VpdParseError, types::SeverityType::Warning, 848 __FILE__, __FUNCTION__, constants::VALUE_0, 849 std::string( 850 "Check failed for record(s) while parsing VPD. Check user data for reason and list of failed record(s). Re-program VPD."), 851 std::vector{ 852 std::make_tuple(m_vpdFilePath, types::CalloutPriority::High)}, 853 l_invalidRecordListString, std::nullopt, std::nullopt, 854 std::nullopt); 855 856 // Dump Bad VPD to file 857 if (constants::SUCCESS != 858 vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector)) 859 { 860 l_rc = false; 861 } 862 } 863 return l_rc; 864 } 865 } // namespace vpd 866