1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 /// \file fru_utils.cpp 17 18 #include "fru_utils.hpp" 19 20 #include <array> 21 #include <cstdint> 22 #include <filesystem> 23 #include <iostream> 24 #include <numeric> 25 #include <set> 26 #include <string> 27 #include <vector> 28 29 extern "C" 30 { 31 // Include for I2C_SMBUS_BLOCK_MAX 32 #include <linux/i2c.h> 33 } 34 35 static constexpr bool debug = false; 36 constexpr size_t fruVersion = 1; // Current FRU spec version number is 1 37 38 std::tm intelEpoch(void) 39 { 40 std::tm val = {}; 41 val.tm_year = 1996 - 1900; 42 val.tm_mday = 1; 43 return val; 44 } 45 46 char sixBitToChar(uint8_t val) 47 { 48 return static_cast<char>((val & 0x3f) + ' '); 49 } 50 51 char bcdPlusToChar(uint8_t val) 52 { 53 val &= 0xf; 54 return (val < 10) ? static_cast<char>(val + '0') : bcdHighChars[val - 10]; 55 } 56 57 enum FRUDataEncoding 58 { 59 binary = 0x0, 60 bcdPlus = 0x1, 61 sixBitASCII = 0x2, 62 languageDependent = 0x3, 63 }; 64 65 /* Decode FRU data into a std::string, given an input iterator and end. If the 66 * state returned is fruDataOk, then the resulting string is the decoded FRU 67 * data. The input iterator is advanced past the data consumed. 68 * 69 * On fruDataErr, we have lost synchronisation with the length bytes, so the 70 * iterator is no longer usable. 71 */ 72 std::pair<DecodeState, std::string> 73 decodeFRUData(std::vector<uint8_t>::const_iterator& iter, 74 const std::vector<uint8_t>::const_iterator& end, 75 bool isLangEng) 76 { 77 std::string value; 78 unsigned int i; 79 80 /* we need at least one byte to decode the type/len header */ 81 if (iter == end) 82 { 83 std::cerr << "Truncated FRU data\n"; 84 return make_pair(DecodeState::err, value); 85 } 86 87 uint8_t c = *(iter++); 88 89 /* 0xc1 is the end marker */ 90 if (c == 0xc1) 91 { 92 return make_pair(DecodeState::end, value); 93 } 94 95 /* decode type/len byte */ 96 uint8_t type = static_cast<uint8_t>(c >> 6); 97 uint8_t len = static_cast<uint8_t>(c & 0x3f); 98 99 /* we should have at least len bytes of data available overall */ 100 if (iter + len > end) 101 { 102 std::cerr << "FRU data field extends past end of FRU area data\n"; 103 return make_pair(DecodeState::err, value); 104 } 105 106 switch (type) 107 { 108 case FRUDataEncoding::binary: 109 { 110 std::stringstream ss; 111 ss << std::hex << std::setfill('0'); 112 for (i = 0; i < len; i++, iter++) 113 { 114 uint8_t val = static_cast<uint8_t>(*iter); 115 ss << std::setw(2) << static_cast<int>(val); 116 } 117 value = ss.str(); 118 break; 119 } 120 case FRUDataEncoding::languageDependent: 121 /* For language-code dependent encodings, assume 8-bit ASCII */ 122 value = std::string(iter, iter + len); 123 iter += len; 124 125 /* English text is encoded in 8-bit ASCII + Latin 1. All other 126 * languages are required to use 2-byte unicode. FruDevice does not 127 * handle unicode. 128 */ 129 if (!isLangEng) 130 { 131 std::cerr << "Error: Non english string is not supported \n"; 132 return make_pair(DecodeState::err, value); 133 } 134 135 break; 136 137 case FRUDataEncoding::bcdPlus: 138 value = std::string(); 139 for (i = 0; i < len; i++, iter++) 140 { 141 uint8_t val = *iter; 142 value.push_back(bcdPlusToChar(val >> 4)); 143 value.push_back(bcdPlusToChar(val & 0xf)); 144 } 145 break; 146 147 case FRUDataEncoding::sixBitASCII: 148 { 149 unsigned int accum = 0; 150 unsigned int accumBitLen = 0; 151 value = std::string(); 152 for (i = 0; i < len; i++, iter++) 153 { 154 accum |= *iter << accumBitLen; 155 accumBitLen += 8; 156 while (accumBitLen >= 6) 157 { 158 value.push_back(sixBitToChar(accum & 0x3f)); 159 accum >>= 6; 160 accumBitLen -= 6; 161 } 162 } 163 } 164 break; 165 } 166 167 return make_pair(DecodeState::ok, value); 168 } 169 170 bool checkLangEng(uint8_t lang) 171 { 172 // If Lang is not English then the encoding is defined as 2-byte UNICODE, 173 // but we don't support that. 174 if (lang && lang != 25) 175 { 176 std::cerr << "Warning: languages other than English is not " 177 "supported\n"; 178 // Return language flag as non english 179 return false; 180 } 181 return true; 182 } 183 184 /* This function verifies for other offsets to check if they are not 185 * falling under other field area 186 * 187 * fruBytes: Start of Fru data 188 * currentArea: Index of current area offset to be compared against all area 189 * offset and it is a multiple of 8 bytes as per specification 190 * len: Length of current area space and it is a multiple of 8 bytes 191 * as per specification 192 */ 193 bool verifyOffset(const std::vector<uint8_t>& fruBytes, fruAreas currentArea, 194 uint8_t len) 195 { 196 197 unsigned int fruBytesSize = fruBytes.size(); 198 199 // check if Fru data has at least 8 byte header 200 if (fruBytesSize <= fruBlockSize) 201 { 202 std::cerr << "Error: trying to parse empty FRU\n"; 203 return false; 204 } 205 206 // Check range of passed currentArea value 207 if (currentArea > fruAreas::fruAreaMultirecord) 208 { 209 std::cerr << "Error: Fru area is out of range\n"; 210 return false; 211 } 212 213 unsigned int currentAreaIndex = getHeaderAreaFieldOffset(currentArea); 214 if (currentAreaIndex > fruBytesSize) 215 { 216 std::cerr << "Error: Fru area index is out of range\n"; 217 return false; 218 } 219 220 unsigned int start = fruBytes[currentAreaIndex]; 221 unsigned int end = start + len; 222 223 /* Verify each offset within the range of start and end */ 224 for (fruAreas area = fruAreas::fruAreaInternal; 225 area <= fruAreas::fruAreaMultirecord; ++area) 226 { 227 // skip the current offset 228 if (area == currentArea) 229 { 230 continue; 231 } 232 233 unsigned int areaIndex = getHeaderAreaFieldOffset(area); 234 if (areaIndex > fruBytesSize) 235 { 236 std::cerr << "Error: Fru area index is out of range\n"; 237 return false; 238 } 239 240 unsigned int areaOffset = fruBytes[areaIndex]; 241 // if areaOffset is 0 means this area is not available so skip 242 if (areaOffset == 0) 243 { 244 continue; 245 } 246 247 // check for overlapping of current offset with given areaoffset 248 if (areaOffset == start || (areaOffset > start && areaOffset < end)) 249 { 250 std::cerr << getFruAreaName(currentArea) 251 << " offset is overlapping with " << getFruAreaName(area) 252 << " offset\n"; 253 return false; 254 } 255 } 256 return true; 257 } 258 259 resCodes 260 formatIPMIFRU(const std::vector<uint8_t>& fruBytes, 261 boost::container::flat_map<std::string, std::string>& result) 262 { 263 resCodes ret = resCodes::resOK; 264 if (fruBytes.size() <= fruBlockSize) 265 { 266 std::cerr << "Error: trying to parse empty FRU \n"; 267 return resCodes::resErr; 268 } 269 result["Common_Format_Version"] = 270 std::to_string(static_cast<int>(*fruBytes.begin())); 271 272 const std::vector<std::string>* fruAreaFieldNames; 273 274 // Don't parse Internal and Multirecord areas 275 for (fruAreas area = fruAreas::fruAreaChassis; 276 area <= fruAreas::fruAreaProduct; ++area) 277 { 278 279 size_t offset = *(fruBytes.begin() + getHeaderAreaFieldOffset(area)); 280 if (offset == 0) 281 { 282 continue; 283 } 284 offset *= fruBlockSize; 285 std::vector<uint8_t>::const_iterator fruBytesIter = 286 fruBytes.begin() + offset; 287 if (fruBytesIter + fruBlockSize >= fruBytes.end()) 288 { 289 std::cerr << "Not enough data to parse \n"; 290 return resCodes::resErr; 291 } 292 // check for format version 1 293 if (*fruBytesIter != 0x01) 294 { 295 std::cerr << "Unexpected version " << *fruBytesIter << "\n"; 296 return resCodes::resErr; 297 } 298 ++fruBytesIter; 299 300 /* Verify other area offset for overlap with current area by passing 301 * length of current area offset pointed by *fruBytesIter 302 */ 303 if (!verifyOffset(fruBytes, area, *fruBytesIter)) 304 { 305 return resCodes::resErr; 306 } 307 308 size_t fruAreaSize = *fruBytesIter * fruBlockSize; 309 std::vector<uint8_t>::const_iterator fruBytesIterEndArea = 310 fruBytes.begin() + offset + fruAreaSize - 1; 311 ++fruBytesIter; 312 313 uint8_t fruComputedChecksum = 314 calculateChecksum(fruBytes.begin() + offset, fruBytesIterEndArea); 315 if (fruComputedChecksum != *fruBytesIterEndArea) 316 { 317 std::stringstream ss; 318 ss << std::hex << std::setfill('0'); 319 ss << "Checksum error in FRU area " << getFruAreaName(area) << "\n"; 320 ss << "\tComputed checksum: 0x" << std::setw(2) 321 << static_cast<int>(fruComputedChecksum) << "\n"; 322 ss << "\tThe read checksum: 0x" << std::setw(2) 323 << static_cast<int>(*fruBytesIterEndArea) << "\n"; 324 std::cerr << ss.str(); 325 ret = resCodes::resWarn; 326 } 327 328 /* Set default language flag to true as Chassis Fru area are always 329 * encoded in English defined in Section 10 of Fru specification 330 */ 331 332 bool isLangEng = true; 333 switch (area) 334 { 335 case fruAreas::fruAreaChassis: 336 { 337 result["CHASSIS_TYPE"] = 338 std::to_string(static_cast<int>(*fruBytesIter)); 339 fruBytesIter += 1; 340 fruAreaFieldNames = &chassisFruAreas; 341 break; 342 } 343 case fruAreas::fruAreaBoard: 344 { 345 uint8_t lang = *fruBytesIter; 346 result["BOARD_LANGUAGE_CODE"] = 347 std::to_string(static_cast<int>(lang)); 348 isLangEng = checkLangEng(lang); 349 fruBytesIter += 1; 350 351 unsigned int minutes = *fruBytesIter | 352 *(fruBytesIter + 1) << 8 | 353 *(fruBytesIter + 2) << 16; 354 std::tm fruTime = intelEpoch(); 355 std::time_t timeValue = std::mktime(&fruTime); 356 timeValue += minutes * 60; 357 fruTime = *std::gmtime(&timeValue); 358 359 // Tue Nov 20 23:08:00 2018 360 char timeString[32] = {0}; 361 auto bytes = std::strftime(timeString, sizeof(timeString), 362 "%Y-%m-%d - %H:%M:%S", &fruTime); 363 if (bytes == 0) 364 { 365 std::cerr << "invalid time string encountered\n"; 366 return resCodes::resErr; 367 } 368 369 result["BOARD_MANUFACTURE_DATE"] = std::string(timeString); 370 fruBytesIter += 3; 371 fruAreaFieldNames = &boardFruAreas; 372 break; 373 } 374 case fruAreas::fruAreaProduct: 375 { 376 uint8_t lang = *fruBytesIter; 377 result["PRODUCT_LANGUAGE_CODE"] = 378 std::to_string(static_cast<int>(lang)); 379 isLangEng = checkLangEng(lang); 380 fruBytesIter += 1; 381 fruAreaFieldNames = &productFruAreas; 382 break; 383 } 384 default: 385 { 386 std::cerr << "Internal error: unexpected FRU area index: " 387 << static_cast<int>(area) << " \n"; 388 return resCodes::resErr; 389 } 390 } 391 size_t fieldIndex = 0; 392 DecodeState state; 393 do 394 { 395 auto res = 396 decodeFRUData(fruBytesIter, fruBytesIterEndArea, isLangEng); 397 state = res.first; 398 std::string value = res.second; 399 std::string name; 400 if (fieldIndex < fruAreaFieldNames->size()) 401 { 402 name = std::string(getFruAreaName(area)) + "_" + 403 fruAreaFieldNames->at(fieldIndex); 404 } 405 else 406 { 407 name = 408 std::string(getFruAreaName(area)) + "_" + 409 fruCustomFieldName + 410 std::to_string(fieldIndex - fruAreaFieldNames->size() + 1); 411 } 412 413 if (state == DecodeState::ok) 414 { 415 // Strip non null characters from the end 416 value.erase(std::find_if(value.rbegin(), value.rend(), 417 [](char ch) { return ch != 0; }) 418 .base(), 419 value.end()); 420 421 result[name] = std::move(value); 422 ++fieldIndex; 423 } 424 else if (state == DecodeState::err) 425 { 426 std::cerr << "Error while parsing " << name << "\n"; 427 ret = resCodes::resWarn; 428 // Cancel decoding if failed to parse any of mandatory 429 // fields 430 if (fieldIndex < fruAreaFieldNames->size()) 431 { 432 std::cerr << "Failed to parse mandatory field \n"; 433 return resCodes::resErr; 434 } 435 } 436 else 437 { 438 if (fieldIndex < fruAreaFieldNames->size()) 439 { 440 std::cerr << "Mandatory fields absent in FRU area " 441 << getFruAreaName(area) << " after " << name 442 << "\n"; 443 ret = resCodes::resWarn; 444 } 445 } 446 } while (state == DecodeState::ok); 447 for (; fruBytesIter < fruBytesIterEndArea; fruBytesIter++) 448 { 449 uint8_t c = *fruBytesIter; 450 if (c) 451 { 452 std::cerr << "Non-zero byte after EndOfFields in FRU area " 453 << getFruAreaName(area) << "\n"; 454 ret = resCodes::resWarn; 455 break; 456 } 457 } 458 } 459 460 return ret; 461 } 462 463 // Calculate new checksum for fru info area 464 uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter, 465 std::vector<uint8_t>::const_iterator end) 466 { 467 constexpr int checksumMod = 256; 468 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0)); 469 return (checksumMod - sum) % checksumMod; 470 } 471 472 uint8_t calculateChecksum(std::vector<uint8_t>& fruAreaData) 473 { 474 return calculateChecksum(fruAreaData.begin(), fruAreaData.end()); 475 } 476 477 // Update new fru area length & 478 // Update checksum at new checksum location 479 // Return the offset of the area checksum byte 480 unsigned int updateFRUAreaLenAndChecksum(std::vector<uint8_t>& fruData, 481 size_t fruAreaStart, 482 size_t fruAreaEndOfFieldsOffset, 483 size_t fruAreaEndOffset) 484 { 485 size_t traverseFRUAreaIndex = fruAreaEndOfFieldsOffset - fruAreaStart; 486 487 // fill zeros for any remaining unused space 488 std::fill(fruData.begin() + fruAreaEndOfFieldsOffset, 489 fruData.begin() + fruAreaEndOffset, 0); 490 491 size_t mod = traverseFRUAreaIndex % fruBlockSize; 492 size_t checksumLoc; 493 if (!mod) 494 { 495 traverseFRUAreaIndex += (fruBlockSize); 496 checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - 1); 497 } 498 else 499 { 500 traverseFRUAreaIndex += (fruBlockSize - mod); 501 checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - mod - 1); 502 } 503 504 size_t newFRUAreaLen = (traverseFRUAreaIndex / fruBlockSize) + 505 ((traverseFRUAreaIndex % fruBlockSize) != 0); 506 size_t fruAreaLengthLoc = fruAreaStart + 1; 507 fruData[fruAreaLengthLoc] = static_cast<uint8_t>(newFRUAreaLen); 508 509 // Calculate new checksum 510 std::vector<uint8_t> finalFRUData; 511 std::copy_n(fruData.begin() + fruAreaStart, checksumLoc - fruAreaStart, 512 std::back_inserter(finalFRUData)); 513 514 fruData[checksumLoc] = calculateChecksum(finalFRUData); 515 return checksumLoc; 516 } 517 518 ssize_t getFieldLength(uint8_t fruFieldTypeLenValue) 519 { 520 constexpr uint8_t typeLenMask = 0x3F; 521 constexpr uint8_t endOfFields = 0xC1; 522 if (fruFieldTypeLenValue == endOfFields) 523 { 524 return -1; 525 } 526 return fruFieldTypeLenValue & typeLenMask; 527 } 528 529 bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData) 530 { 531 // ipmi spec format version number is currently at 1, verify it 532 if (blockData[0] != fruVersion) 533 { 534 if (debug) 535 { 536 std::cerr << "FRU spec version " << (int)(blockData[0]) 537 << " not supported. Supported version is " 538 << (int)(fruVersion) << "\n"; 539 } 540 return false; 541 } 542 543 // verify pad is set to 0 544 if (blockData[6] != 0x0) 545 { 546 if (debug) 547 { 548 std::cerr << "PAD value in header is non zero, value is " 549 << (int)(blockData[6]) << "\n"; 550 } 551 return false; 552 } 553 554 // verify offsets are 0, or don't point to another offset 555 std::set<uint8_t> foundOffsets; 556 for (int ii = 1; ii < 6; ii++) 557 { 558 if (blockData[ii] == 0) 559 { 560 continue; 561 } 562 auto inserted = foundOffsets.insert(blockData[ii]); 563 if (!inserted.second) 564 { 565 return false; 566 } 567 } 568 569 // validate checksum 570 size_t sum = 0; 571 for (int jj = 0; jj < 7; jj++) 572 { 573 sum += blockData[jj]; 574 } 575 sum = (256 - sum) & 0xFF; 576 577 if (sum != blockData[7]) 578 { 579 if (debug) 580 { 581 std::cerr << "Checksum " << (int)(blockData[7]) 582 << " is invalid. calculated checksum is " << (int)(sum) 583 << "\n"; 584 } 585 return false; 586 } 587 return true; 588 } 589 590 bool findFRUHeader(int flag, int file, uint16_t address, 591 const ReadBlockFunc& readBlock, const std::string& errorHelp, 592 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData, 593 off_t& baseOffset) 594 { 595 if (readBlock(flag, file, address, baseOffset, 0x8, blockData.data()) < 0) 596 { 597 std::cerr << "failed to read " << errorHelp << " base offset " 598 << baseOffset << "\n"; 599 return false; 600 } 601 602 // check the header checksum 603 if (validateHeader(blockData)) 604 { 605 return true; 606 } 607 608 // only continue the search if we just looked at 0x0. 609 if (baseOffset != 0) 610 { 611 return false; 612 } 613 614 // now check for special cases where the IPMI data is at an offset 615 616 // check if blockData starts with tyanHeader 617 const std::vector<uint8_t> tyanHeader = {'$', 'T', 'Y', 'A', 'N', '$'}; 618 if (blockData.size() >= tyanHeader.size() && 619 std::equal(tyanHeader.begin(), tyanHeader.end(), blockData.begin())) 620 { 621 // look for the FRU header at offset 0x6000 622 baseOffset = 0x6000; 623 return findFRUHeader(flag, file, address, readBlock, errorHelp, 624 blockData, baseOffset); 625 } 626 627 if (debug) 628 { 629 std::cerr << "Illegal header " << errorHelp << " base offset " 630 << baseOffset << "\n"; 631 } 632 633 return false; 634 } 635 636 std::vector<uint8_t> readFRUContents(int flag, int file, uint16_t address, 637 const ReadBlockFunc& readBlock, 638 const std::string& errorHelp) 639 { 640 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 641 off_t baseOffset = 0x0; 642 643 if (!findFRUHeader(flag, file, address, readBlock, errorHelp, blockData, 644 baseOffset)) 645 { 646 return {}; 647 } 648 649 std::vector<uint8_t> device; 650 device.insert(device.end(), blockData.begin(), blockData.begin() + 8); 651 652 bool hasMultiRecords = false; 653 size_t fruLength = fruBlockSize; // At least FRU header is present 654 unsigned int prevOffset = 0; 655 for (fruAreas area = fruAreas::fruAreaInternal; 656 area <= fruAreas::fruAreaMultirecord; ++area) 657 { 658 // Offset value can be 255. 659 unsigned int areaOffset = device[getHeaderAreaFieldOffset(area)]; 660 if (areaOffset == 0) 661 { 662 continue; 663 } 664 665 /* Check for offset order, as per Section 17 of FRU specification, FRU 666 * information areas are required to be in order in FRU data layout 667 * which means all offset value should be in increasing order or can be 668 * 0 if that area is not present 669 */ 670 if (areaOffset <= prevOffset) 671 { 672 std::cerr << "Fru area offsets are not in required order as per " 673 "Section 17 of Fru specification\n"; 674 return {}; 675 } 676 prevOffset = areaOffset; 677 678 // MultiRecords are different. area is not tracking section, it's 679 // walking the common header. 680 if (area == fruAreas::fruAreaMultirecord) 681 { 682 hasMultiRecords = true; 683 break; 684 } 685 686 areaOffset *= fruBlockSize; 687 688 if (readBlock(flag, file, address, baseOffset + areaOffset, 0x2, 689 blockData.data()) < 0) 690 { 691 std::cerr << "failed to read " << errorHelp << " base offset " 692 << baseOffset << "\n"; 693 return {}; 694 } 695 696 // Ignore data type (blockData is already unsigned). 697 size_t length = blockData[1] * fruBlockSize; 698 areaOffset += length; 699 fruLength = (areaOffset > fruLength) ? areaOffset : fruLength; 700 } 701 702 if (hasMultiRecords) 703 { 704 // device[area count] is the index to the last area because the 0th 705 // entry is not an offset in the common header. 706 unsigned int areaOffset = 707 device[getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord)]; 708 areaOffset *= fruBlockSize; 709 710 // the multi-area record header is 5 bytes long. 711 constexpr size_t multiRecordHeaderSize = 5; 712 constexpr uint8_t multiRecordEndOfListMask = 0x80; 713 714 // Sanity hard-limit to 64KB. 715 while (areaOffset < std::numeric_limits<uint16_t>::max()) 716 { 717 // In multi-area, the area offset points to the 0th record, each 718 // record has 3 bytes of the header we care about. 719 if (readBlock(flag, file, address, baseOffset + areaOffset, 0x3, 720 blockData.data()) < 0) 721 { 722 std::cerr << "failed to read " << errorHelp << " base offset " 723 << baseOffset << "\n"; 724 return {}; 725 } 726 727 // Ok, let's check the record length, which is in bytes (unsigned, 728 // up to 255, so blockData should hold uint8_t not char) 729 size_t recordLength = blockData[2]; 730 areaOffset += (recordLength + multiRecordHeaderSize); 731 fruLength = (areaOffset > fruLength) ? areaOffset : fruLength; 732 733 // If this is the end of the list bail. 734 if ((blockData[1] & multiRecordEndOfListMask)) 735 { 736 break; 737 } 738 } 739 } 740 741 // You already copied these first 8 bytes (the ipmi fru header size) 742 fruLength -= std::min(fruBlockSize, fruLength); 743 744 int readOffset = fruBlockSize; 745 746 while (fruLength > 0) 747 { 748 size_t requestLength = 749 std::min(static_cast<size_t>(I2C_SMBUS_BLOCK_MAX), fruLength); 750 751 if (readBlock(flag, file, address, baseOffset + readOffset, 752 requestLength, blockData.data()) < 0) 753 { 754 std::cerr << "failed to read " << errorHelp << " base offset " 755 << baseOffset << "\n"; 756 return {}; 757 } 758 759 device.insert(device.end(), blockData.begin(), 760 blockData.begin() + requestLength); 761 762 readOffset += requestLength; 763 fruLength -= std::min(requestLength, fruLength); 764 } 765 766 return device; 767 } 768 769 unsigned int getHeaderAreaFieldOffset(fruAreas area) 770 { 771 return static_cast<unsigned int>(area) + 1; 772 } 773 774 std::vector<uint8_t>& getFRUInfo(const uint8_t& bus, const uint8_t& address) 775 { 776 auto deviceMap = busMap.find(bus); 777 if (deviceMap == busMap.end()) 778 { 779 throw std::invalid_argument("Invalid Bus."); 780 } 781 auto device = deviceMap->second->find(address); 782 if (device == deviceMap->second->end()) 783 { 784 throw std::invalid_argument("Invalid Address."); 785 } 786 std::vector<uint8_t>& ret = device->second; 787 788 return ret; 789 } 790