1 #include "impl.hpp" 2 3 #include "vpdecc/vpdecc.h" 4 5 #include "const.hpp" 6 #include "defines.hpp" 7 #include "ibm_vpd_utils.hpp" 8 #include "types.hpp" 9 #include "vpd_exceptions.hpp" 10 11 #include <algorithm> 12 #include <exception> 13 #include <iomanip> 14 #include <iostream> 15 #include <iterator> 16 #include <sstream> 17 #include <tuple> 18 #include <unordered_map> 19 20 namespace openpower 21 { 22 namespace vpd 23 { 24 namespace parser 25 { 26 using namespace openpower::vpd::constants; 27 using namespace openpower::vpd::exceptions; 28 29 static const std::unordered_map<std::string, Record> supportedRecords = { 30 {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}}; 31 32 static const std::unordered_map<std::string, internal::KeywordInfo> 33 supportedKeywords = { 34 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)}, 35 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)}, 36 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)}, 37 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)}, 38 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)}, 39 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)}, 40 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)}, 41 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)}, 42 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)}, 43 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)}, 44 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)}, 45 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)}, 46 }; 47 48 namespace 49 { 50 constexpr auto toHex(size_t c) 51 { 52 constexpr auto map = "0123456789abcdef"; 53 return map[c]; 54 } 55 } // namespace 56 57 /*readUInt16LE: Read 2 bytes LE data*/ 58 static LE2ByteData readUInt16LE(Binary::const_iterator iterator) 59 { 60 LE2ByteData lowByte = *iterator; 61 LE2ByteData highByte = *(iterator + 1); 62 lowByte |= (highByte << 8); 63 return lowByte; 64 } 65 66 RecordOffset Impl::getVtocOffset() const 67 { 68 auto vpdPtr = vpd.cbegin(); 69 std::advance(vpdPtr, offsets::VTOC_PTR); 70 // Get VTOC Offset 71 auto vtocOffset = readUInt16LE(vpdPtr); 72 73 return vtocOffset; 74 } 75 76 #ifdef IPZ_PARSER 77 int Impl::vhdrEccCheck() 78 { 79 int rc = eccStatus::SUCCESS; 80 auto vpdPtr = vpd.cbegin(); 81 82 auto l_status = 83 vpdecc_check_data(const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_RECORD]), 84 lengths::VHDR_RECORD_LENGTH, 85 const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_ECC]), 86 lengths::VHDR_ECC_LENGTH); 87 if (l_status == VPD_ECC_CORRECTABLE_DATA) 88 { 89 try 90 { 91 if (vpdFileStream.is_open()) 92 { 93 vpdFileStream.seekp(vpdStartOffset + offsets::VHDR_RECORD, 94 std::ios::beg); 95 vpdFileStream.write( 96 reinterpret_cast<const char*>(&vpd[offsets::VHDR_RECORD]), 97 lengths::VHDR_RECORD_LENGTH); 98 } 99 else 100 { 101 std::cerr << "File not open"; 102 rc = eccStatus::FAILED; 103 } 104 } 105 catch (const std::fstream::failure& e) 106 { 107 std::cout << "Error while operating on file with exception:" 108 << e.what(); 109 rc = eccStatus::FAILED; 110 } 111 } 112 else if (l_status != VPD_ECC_OK) 113 { 114 rc = eccStatus::FAILED; 115 } 116 117 return rc; 118 } 119 120 int Impl::vtocEccCheck() 121 { 122 int rc = eccStatus::SUCCESS; 123 // Use another pointer to get ECC information from VHDR, 124 // actual pointer is pointing to VTOC data 125 126 auto vpdPtr = vpd.cbegin(); 127 128 // Get VTOC Offset 129 auto vtocOffset = getVtocOffset(); 130 131 // Get the VTOC Length 132 std::advance(vpdPtr, offsets::VTOC_PTR + sizeof(RecordOffset)); 133 auto vtocLength = readUInt16LE(vpdPtr); 134 135 // Get the ECC Offset 136 std::advance(vpdPtr, sizeof(RecordLength)); 137 auto vtocECCOffset = readUInt16LE(vpdPtr); 138 139 // Get the ECC length 140 std::advance(vpdPtr, sizeof(ECCOffset)); 141 auto vtocECCLength = readUInt16LE(vpdPtr); 142 143 // Reset pointer to start of the vpd, 144 // so that Offset will point to correct address 145 vpdPtr = vpd.cbegin(); 146 auto l_status = vpdecc_check_data( 147 const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength, 148 const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength); 149 if (l_status == VPD_ECC_CORRECTABLE_DATA) 150 { 151 try 152 { 153 if (vpdFileStream.is_open()) 154 { 155 vpdFileStream.seekp(vpdStartOffset + vtocOffset, std::ios::beg); 156 vpdFileStream.write( 157 reinterpret_cast<const char*>(&vpdPtr[vtocOffset]), 158 vtocLength); 159 } 160 else 161 { 162 std::cerr << "File not open"; 163 rc = eccStatus::FAILED; 164 } 165 } 166 catch (const std::fstream::failure& e) 167 { 168 std::cout << "Error while operating on file with exception " 169 << e.what(); 170 rc = eccStatus::FAILED; 171 } 172 } 173 else if (l_status != VPD_ECC_OK) 174 { 175 rc = eccStatus::FAILED; 176 } 177 178 return rc; 179 } 180 181 int Impl::recordEccCheck(Binary::const_iterator iterator) 182 { 183 int rc = eccStatus::SUCCESS; 184 185 auto recordOffset = readUInt16LE(iterator); 186 187 std::advance(iterator, sizeof(RecordOffset)); 188 auto recordLength = readUInt16LE(iterator); 189 190 std::advance(iterator, sizeof(RecordLength)); 191 auto eccOffset = readUInt16LE(iterator); 192 193 std::advance(iterator, sizeof(ECCOffset)); 194 auto eccLength = readUInt16LE(iterator); 195 196 if (eccLength == 0 || eccOffset == 0) 197 { 198 throw(VpdEccException( 199 "Could not find ECC's offset or Length for Record:")); 200 } 201 202 if (recordOffset == 0 || recordLength == 0) 203 { 204 throw(VpdDataException("Could not find VPD record offset or VPD record " 205 "length for Record:")); 206 } 207 208 auto vpdPtr = vpd.cbegin(); 209 210 auto l_status = vpdecc_check_data( 211 const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength, 212 const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength); 213 if (l_status == VPD_ECC_CORRECTABLE_DATA) 214 { 215 try 216 { 217 if (vpdFileStream.is_open()) 218 { 219 vpdFileStream.seekp(vpdStartOffset + recordOffset, 220 std::ios::beg); 221 vpdFileStream.write( 222 reinterpret_cast<const char*>(&vpdPtr[recordOffset]), 223 recordLength); 224 } 225 else 226 { 227 std::cerr << "File not open"; 228 rc = eccStatus::FAILED; 229 } 230 } 231 catch (const std::fstream::failure& e) 232 { 233 std::cout << "Error while operating on file with exception " 234 << e.what(); 235 rc = eccStatus::FAILED; 236 } 237 } 238 else if (l_status != VPD_ECC_OK) 239 { 240 rc = eccStatus::FAILED; 241 } 242 243 return rc; 244 } 245 #endif 246 247 void Impl::checkHeader() 248 { 249 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size())) 250 { 251 throw(VpdDataException("Malformed VPD")); 252 } 253 else 254 { 255 auto iterator = vpd.cbegin(); 256 std::advance(iterator, offsets::VHDR); 257 auto stop = std::next(iterator, lengths::RECORD_NAME); 258 std::string record(iterator, stop); 259 if ("VHDR" != record) 260 { 261 throw(VpdDataException("VHDR record not found")); 262 } 263 264 #ifdef IPZ_PARSER 265 // Check ECC 266 int rc = eccStatus::FAILED; 267 rc = vhdrEccCheck(); 268 if (rc != eccStatus::SUCCESS) 269 { 270 throw(VpdEccException("ERROR: VHDR ECC check Failed")); 271 } 272 #endif 273 } 274 } 275 276 std::size_t Impl::readTOC(Binary::const_iterator& iterator) 277 { 278 // The offset to VTOC could be 1 or 2 bytes long 279 RecordOffset vtocOffset = getVtocOffset(); 280 281 // Got the offset to VTOC, skip past record header and keyword header 282 // to get to the record name. 283 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) + 284 // Skip past the RT keyword, which contains 285 // the record name. 286 lengths::KW_NAME + sizeof(KwSize)); 287 288 auto stop = std::next(iterator, lengths::RECORD_NAME); 289 std::string record(iterator, stop); 290 if ("VTOC" != record) 291 { 292 throw(VpdDataException("VTOC record not found")); 293 } 294 295 #ifdef IPZ_PARSER 296 // Check ECC 297 int rc = eccStatus::FAILED; 298 rc = vtocEccCheck(); 299 if (rc != eccStatus::SUCCESS) 300 { 301 throw(VpdEccException("ERROR: VTOC ECC check Failed")); 302 } 303 #endif 304 // VTOC record name is good, now read through the TOC, stored in the PT 305 // PT keyword; vpdBuffer is now pointing at the first character of the 306 // name 'VTOC', jump to PT data. 307 // Skip past record name and KW name, 'PT' 308 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME); 309 // Note size of PT 310 std::size_t ptLen = *iterator; 311 // Skip past PT size 312 std::advance(iterator, sizeof(KwSize)); 313 314 // length of PT keyword 315 return ptLen; 316 } 317 318 internal::OffsetList Impl::readPT(Binary::const_iterator iterator, 319 std::size_t ptLength) 320 { 321 internal::OffsetList offsets{}; 322 323 auto end = iterator; 324 std::advance(end, ptLength); 325 326 // Look at each entry in the PT keyword. In the entry, 327 // we care only about the record offset information. 328 while (iterator < end) 329 { 330 #ifdef IPZ_PARSER 331 auto iteratorToRecName = iterator; 332 #endif 333 // Skip record name and record type 334 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType)); 335 336 // Get record offset 337 auto offset = readUInt16LE(iterator); 338 offsets.push_back(offset); 339 340 #ifdef IPZ_PARSER 341 std::string recordName(iteratorToRecName, 342 iteratorToRecName + lengths::RECORD_NAME); 343 344 try 345 { 346 // Verify the ECC for this Record 347 int rc = recordEccCheck(iterator); 348 349 if (rc != eccStatus::SUCCESS) 350 { 351 std::string errorMsg = std::string( 352 "ERROR: ECC check did not pass for the " 353 "Record:"); 354 throw(VpdEccException(errorMsg)); 355 } 356 } 357 catch (const VpdEccException& ex) 358 { 359 inventory::PelAdditionalData additionalData{}; 360 additionalData.emplace("DESCRIPTION", 361 std::string{ex.what()} + recordName); 362 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); 363 createPEL(additionalData, PelSeverity::WARNING, 364 errIntfForEccCheckFail, nullptr); 365 } 366 catch (const VpdDataException& ex) 367 { 368 inventory::PelAdditionalData additionalData{}; 369 additionalData.emplace("DESCRIPTION", 370 std::string{ex.what()} + recordName); 371 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); 372 createPEL(additionalData, PelSeverity::WARNING, 373 errIntfForInvalidVPD, nullptr); 374 } 375 376 #endif 377 378 // Jump record size, record length, ECC offset and ECC length 379 std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) + 380 sizeof(ECCOffset) + sizeof(ECCLength)); 381 } 382 383 return offsets; 384 } 385 386 void Impl::processRecord(std::size_t recordOffset) 387 { 388 // Jump to record name 389 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) + 390 // Skip past the RT keyword, which contains 391 // the record name. 392 lengths::KW_NAME + sizeof(KwSize); 393 // Get record name 394 auto iterator = vpd.cbegin(); 395 std::advance(iterator, nameOffset); 396 397 std::string name(iterator, iterator + lengths::RECORD_NAME); 398 399 #ifndef IPZ_PARSER 400 if (supportedRecords.end() != supportedRecords.find(name)) 401 { 402 #endif 403 // If it's a record we're interested in, proceed to find 404 // contained keywords and their values. 405 std::advance(iterator, lengths::RECORD_NAME); 406 407 #ifdef IPZ_PARSER 408 409 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value 410 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) + 411 lengths::RECORD_NAME)); 412 #endif 413 auto kwMap = readKeywords(iterator); 414 // Add entry for this record (and contained keyword:value pairs) 415 // to the parsed vpd output. 416 out.emplace(std::move(name), std::move(kwMap)); 417 418 #ifndef IPZ_PARSER 419 } 420 #endif 421 } 422 423 std::string Impl::readKwData(const internal::KeywordInfo& keyword, 424 std::size_t dataLength, 425 Binary::const_iterator iterator) 426 { 427 using namespace openpower::vpd; 428 switch (std::get<keyword::Encoding>(keyword)) 429 { 430 case keyword::Encoding::ASCII: 431 { 432 auto stop = std::next(iterator, dataLength); 433 return std::string(iterator, stop); 434 } 435 436 case keyword::Encoding::RAW: 437 { 438 auto stop = std::next(iterator, dataLength); 439 std::string data(iterator, stop); 440 std::string result{}; 441 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) { 442 result += toHex(c >> 4); 443 result += toHex(c & 0x0F); 444 }); 445 return result; 446 } 447 448 case keyword::Encoding::MB: 449 { 450 // MB is BuildDate, represent as 451 // 1997-01-01-08:30:00 452 // <year>-<month>-<day>-<hour>:<min>:<sec> 453 auto stop = std::next(iterator, MB_LEN_BYTES); 454 std::string data(iterator, stop); 455 std::string result; 456 result.reserve(MB_LEN_BYTES); 457 auto strItr = data.cbegin(); 458 std::advance(strItr, 1); 459 std::for_each(strItr, data.cend(), [&result](size_t c) { 460 result += toHex(c >> 4); 461 result += toHex(c & 0x0F); 462 }); 463 464 result.insert(MB_YEAR_END, 1, '-'); 465 result.insert(MB_MONTH_END, 1, '-'); 466 result.insert(MB_DAY_END, 1, '-'); 467 result.insert(MB_HOUR_END, 1, ':'); 468 result.insert(MB_MIN_END, 1, ':'); 469 470 return result; 471 } 472 473 case keyword::Encoding::B1: 474 { 475 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF 476 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES); 477 std::string data(iterator, stop); 478 std::string result{}; 479 auto strItr = data.cbegin(); 480 size_t firstDigit = *strItr; 481 result += toHex(firstDigit >> 4); 482 result += toHex(firstDigit & 0x0F); 483 std::advance(strItr, 1); 484 std::for_each(strItr, data.cend(), [&result](size_t c) { 485 result += ":"; 486 result += toHex(c >> 4); 487 result += toHex(c & 0x0F); 488 }); 489 return result; 490 } 491 492 case keyword::Encoding::UD: 493 { 494 // UD, the UUID info, represented as 495 // 123e4567-e89b-12d3-a456-426655440000 496 //<time_low>-<time_mid>-<time hi and version> 497 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id> 498 auto stop = std::next(iterator, UUID_LEN_BYTES); 499 std::string data(iterator, stop); 500 std::string result{}; 501 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) { 502 result += toHex(c >> 4); 503 result += toHex(c & 0x0F); 504 }); 505 result.insert(UUID_TIME_LOW_END, 1, '-'); 506 result.insert(UUID_TIME_MID_END, 1, '-'); 507 result.insert(UUID_TIME_HIGH_END, 1, '-'); 508 result.insert(UUID_CLK_SEQ_END, 1, '-'); 509 510 return result; 511 } 512 default: 513 break; 514 } 515 516 return {}; 517 } 518 519 internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator) 520 { 521 internal::KeywordMap map{}; 522 while (true) 523 { 524 // Note keyword name 525 std::string kw(iterator, iterator + lengths::KW_NAME); 526 if (LAST_KW == kw) 527 { 528 // We're done 529 break; 530 } 531 // Check if the Keyword is '#kw' 532 char kwNameStart = *iterator; 533 534 // Jump past keyword name 535 std::advance(iterator, lengths::KW_NAME); 536 537 std::size_t length; 538 std::size_t lengthHighByte; 539 if (POUND_KW == kwNameStart) 540 { 541 // Note keyword data length 542 length = *iterator; 543 lengthHighByte = *(iterator + 1); 544 length |= (lengthHighByte << 8); 545 546 // Jump past 2Byte keyword length 547 std::advance(iterator, sizeof(PoundKwSize)); 548 } 549 else 550 { 551 // Note keyword data length 552 length = *iterator; 553 554 // Jump past keyword length 555 std::advance(iterator, sizeof(KwSize)); 556 } 557 558 // Pointing to keyword data now 559 #ifndef IPZ_PARSER 560 if (supportedKeywords.end() != supportedKeywords.find(kw)) 561 { 562 // Keyword is of interest to us 563 std::string data = readKwData((supportedKeywords.find(kw))->second, 564 length, iterator); 565 map.emplace(std::move(kw), std::move(data)); 566 } 567 568 #else 569 // support all the Keywords 570 auto stop = std::next(iterator, length); 571 std::string kwdata(iterator, stop); 572 map.emplace(std::move(kw), std::move(kwdata)); 573 574 #endif 575 // Jump past keyword data length 576 std::advance(iterator, length); 577 } 578 579 return map; 580 } 581 582 Store Impl::run() 583 { 584 // Check if the VHDR record is present 585 checkHeader(); 586 587 auto iterator = vpd.cbegin(); 588 589 // Read the table of contents record 590 std::size_t ptLen = readTOC(iterator); 591 592 // Read the table of contents record, to get offsets 593 // to other records. 594 auto offsets = readPT(iterator, ptLen); 595 for (const auto& offset : offsets) 596 { 597 processRecord(offset); 598 } 599 // Return a Store object, which has interfaces to 600 // access parsed VPD by record:keyword 601 return Store(std::move(out)); 602 } 603 604 void Impl::checkVPDHeader() 605 { 606 // Check if the VHDR record is present and is valid 607 checkHeader(); 608 } 609 610 std::string Impl::readKwFromHw(const std::string& record, 611 const std::string& keyword) 612 { 613 // Check if the VHDR record is present 614 checkHeader(); 615 616 auto iterator = vpd.cbegin(); 617 618 // Read the table of contents record 619 std::size_t ptLen = readTOC(iterator); 620 621 // Read the table of contents record, to get offsets 622 // to other records. 623 auto offsets = readPT(iterator, ptLen); 624 for (const auto& offset : offsets) 625 { 626 // Jump to record name 627 auto nameOffset = offset + sizeof(RecordId) + sizeof(RecordSize) + 628 // Skip past the RT keyword, which contains 629 // the record name. 630 lengths::KW_NAME + sizeof(KwSize); 631 // Get record name 632 auto iterator = vpd.cbegin(); 633 std::advance(iterator, nameOffset); 634 635 std::string name(iterator, iterator + lengths::RECORD_NAME); 636 if (name != record) 637 { 638 continue; 639 } 640 else 641 { 642 processRecord(offset); 643 const auto& itr = out.find(record); 644 if (itr != out.end()) 645 { 646 const auto& kwValItr = (itr->second).find(keyword); 647 if (kwValItr != (itr->second).end()) 648 { 649 return kwValItr->second; 650 } 651 else 652 { 653 return ""; 654 } 655 } 656 } 657 } 658 return ""; 659 } 660 661 } // namespace parser 662 } // namespace vpd 663 } // namespace openpower 664