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