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