1 /** 2 * Copyright © 2019 IBM 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 #include "config.h" 17 18 #include "pel.hpp" 19 20 #include "bcd_time.hpp" 21 #include "extended_user_data.hpp" 22 #include "extended_user_header.hpp" 23 #include "failing_mtms.hpp" 24 #include "fru_identity.hpp" 25 #include "json_utils.hpp" 26 #include "log_id.hpp" 27 #include "pel_rules.hpp" 28 #include "pel_values.hpp" 29 #include "section_factory.hpp" 30 #include "src.hpp" 31 #include "stream.hpp" 32 #include "user_data_formats.hpp" 33 34 #ifdef PEL_ENABLE_PHAL 35 #include "phal_service_actions.hpp" 36 #include "sbe_ffdc_handler.hpp" 37 #endif 38 39 #include <fmt/format.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 43 #include <phosphor-logging/log.hpp> 44 45 #include <iostream> 46 47 namespace openpower 48 { 49 namespace pels 50 { 51 namespace pv = openpower::pels::pel_values; 52 using namespace phosphor::logging; 53 54 constexpr auto unknownValue = "Unknown"; 55 56 PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp, 57 phosphor::logging::Entry::Level severity, 58 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn, 59 const DataInterfaceBase& dataIface) 60 { 61 // No changes in input, for non SBE error related requests 62 PelFFDC ffdcFiles = ffdcFilesIn; 63 64 #ifdef PEL_ENABLE_PHAL 65 // Add sbe ffdc processed data into ffdcfiles. 66 namespace sbe = openpower::pels::sbe; 67 auto processReq = 68 std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) { 69 return file.format == UserDataFormat::custom && 70 file.subType == sbe::sbeFFDCSubType; 71 }); 72 // sbeFFDC can't be destroyed until the end of the PEL constructor 73 // because it needs to keep around the FFDC Files to be used below. 74 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr; 75 if (processReq) 76 { 77 sbeFFDCPtr = std::make_unique<sbe::SbeFFDC>(additionalData, 78 ffdcFilesIn); 79 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC(); 80 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(), 81 sbeFFDCFiles.end()); 82 83 // update pel priority for spare clock failures 84 if (auto customSeverity = sbeFFDCPtr->getSeverity()) 85 { 86 severity = customSeverity.value(); 87 } 88 } 89 #endif 90 91 std::map<std::string, std::vector<std::string>> debugData; 92 nlohmann::json callouts; 93 94 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID, 95 timestamp); 96 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData, 97 dataIface); 98 99 // Extract any callouts embedded in an FFDC file. 100 if (!ffdcFiles.empty()) 101 { 102 try 103 { 104 callouts = getCalloutJSON(ffdcFiles); 105 } 106 catch (const std::exception& e) 107 { 108 debugData.emplace("FFDC file JSON callouts error", 109 std::vector<std::string>{e.what()}); 110 } 111 } 112 113 auto src = std::make_unique<SRC>(regEntry, additionalData, callouts, 114 dataIface); 115 116 if (!src->getDebugData().empty()) 117 { 118 // Something didn't go as planned 119 debugData.emplace("SRC", src->getDebugData()); 120 } 121 122 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src); 123 124 _optionalSections.push_back(std::move(src)); 125 _optionalSections.push_back(std::move(euh)); 126 127 auto mtms = std::make_unique<FailingMTMS>(dataIface); 128 _optionalSections.push_back(std::move(mtms)); 129 130 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); 131 addUserDataSection(std::move(ud)); 132 133 // Check for pel severity of type - 0x51 = critical error, system 134 // termination and update terminate bit in SRC for pels 135 updateTerminateBitInSRCSection(); 136 137 // Create a UserData section from AdditionalData. 138 if (!additionalData.empty()) 139 { 140 ud = util::makeADUserDataSection(additionalData); 141 addUserDataSection(std::move(ud)); 142 } 143 144 // Add any FFDC files into UserData sections 145 for (const auto& file : ffdcFiles) 146 { 147 ud = util::makeFFDCuserDataSection(regEntry.componentID, file); 148 if (!ud) 149 { 150 // Add this error into the debug data UserData section 151 std::ostringstream msg; 152 msg << "Could not make PEL FFDC UserData section from file" 153 << std::hex << regEntry.componentID << " " << file.subType 154 << " " << file.version; 155 if (debugData.count("FFDC File")) 156 { 157 debugData.at("FFDC File").push_back(msg.str()); 158 } 159 else 160 { 161 debugData.emplace("FFDC File", 162 std::vector<std::string>{msg.str()}); 163 } 164 165 continue; 166 } 167 168 addUserDataSection(std::move(ud)); 169 } 170 171 #ifdef PEL_ENABLE_PHAL 172 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 173 openpower::pels::phal::createServiceActions(callouts, path, dataIface, 174 plid()); 175 #endif 176 177 // Store in the PEL any important debug data created while 178 // building the PEL sections. 179 if (!debugData.empty()) 180 { 181 nlohmann::json data; 182 data["PEL Internal Debug Data"] = debugData; 183 ud = util::makeJSONUserDataSection(data); 184 185 addUserDataSection(std::move(ud)); 186 187 // Also put in the journal for debug 188 for (const auto& [name, msgs] : debugData) 189 { 190 for (const auto& message : msgs) 191 { 192 std::string entry = name + ": " + message; 193 log<level::INFO>(entry.c_str()); 194 } 195 } 196 } 197 198 _ph->setSectionCount(2 + _optionalSections.size()); 199 200 checkRulesAndFix(); 201 } 202 203 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {} 204 205 PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID) 206 { 207 populateFromRawData(data, obmcLogID); 208 } 209 210 void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID) 211 { 212 Stream pelData{data}; 213 _ph = std::make_unique<PrivateHeader>(pelData); 214 if (obmcLogID != 0) 215 { 216 _ph->setOBMCLogID(obmcLogID); 217 } 218 219 _uh = std::make_unique<UserHeader>(pelData); 220 221 // Use the section factory to create the rest of the objects 222 for (size_t i = 2; i < _ph->sectionCount(); i++) 223 { 224 auto section = section_factory::create(pelData); 225 _optionalSections.push_back(std::move(section)); 226 } 227 } 228 229 bool PEL::valid() const 230 { 231 bool valid = _ph->valid(); 232 233 if (valid) 234 { 235 valid = _uh->valid(); 236 } 237 238 if (valid) 239 { 240 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(), 241 [](const auto& section) { return section->valid(); })) 242 { 243 valid = false; 244 } 245 } 246 247 return valid; 248 } 249 250 void PEL::setCommitTime() 251 { 252 auto now = std::chrono::system_clock::now(); 253 _ph->setCommitTimestamp(getBCDTime(now)); 254 } 255 256 void PEL::assignID() 257 { 258 _ph->setID(generatePELID()); 259 } 260 261 void PEL::flatten(std::vector<uint8_t>& pelBuffer) const 262 { 263 Stream pelData{pelBuffer}; 264 265 if (!valid()) 266 { 267 log<level::WARNING>("Unflattening an invalid PEL"); 268 } 269 270 _ph->flatten(pelData); 271 _uh->flatten(pelData); 272 273 for (auto& section : _optionalSections) 274 { 275 section->flatten(pelData); 276 } 277 } 278 279 std::vector<uint8_t> PEL::data() const 280 { 281 std::vector<uint8_t> pelData; 282 flatten(pelData); 283 return pelData; 284 } 285 286 size_t PEL::size() const 287 { 288 size_t size = 0; 289 290 if (_ph) 291 { 292 size += _ph->header().size; 293 } 294 295 if (_uh) 296 { 297 size += _uh->header().size; 298 } 299 300 for (const auto& section : _optionalSections) 301 { 302 size += section->header().size; 303 } 304 305 return size; 306 } 307 308 std::optional<SRC*> PEL::primarySRC() const 309 { 310 auto src = std::find_if( 311 _optionalSections.begin(), _optionalSections.end(), [](auto& section) { 312 return section->header().id == 313 static_cast<uint16_t>(SectionID::primarySRC); 314 }); 315 if (src != _optionalSections.end()) 316 { 317 return static_cast<SRC*>(src->get()); 318 } 319 320 return std::nullopt; 321 } 322 323 void PEL::checkRulesAndFix() 324 { 325 // Only fix if the action flags are at their default value which 326 // means they weren't specified in the registry. Otherwise 327 // assume the user knows what they are doing. 328 if (_uh->actionFlags() == actionFlagsDefault) 329 { 330 auto [actionFlags, eventType] = pel_rules::check(0, _uh->eventType(), 331 _uh->severity()); 332 333 _uh->setActionFlags(actionFlags); 334 _uh->setEventType(eventType); 335 } 336 } 337 338 void PEL::printSectionInJSON(const Section& section, std::string& buf, 339 std::map<uint16_t, size_t>& pluralSections, 340 message::Registry& registry, 341 const std::vector<std::string>& plugins, 342 uint8_t creatorID) const 343 { 344 char tmpB[5]; 345 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8), 346 static_cast<uint8_t>(section.header().id)}; 347 sprintf(tmpB, "%c%c", id[0], id[1]); 348 std::string sectionID(tmpB); 349 std::string sectionName = pv::sectionTitles.count(sectionID) 350 ? pv::sectionTitles.at(sectionID) 351 : "Unknown Section"; 352 353 // Add a count if there are multiple of this type of section 354 auto count = pluralSections.find(section.header().id); 355 if (count != pluralSections.end()) 356 { 357 sectionName += " " + std::to_string(count->second); 358 count->second++; 359 } 360 361 if (section.valid()) 362 { 363 std::optional<std::string> json; 364 if (sectionID == "PS" || sectionID == "SS") 365 { 366 json = section.getJSON(registry, plugins, creatorID); 367 } 368 else if ((sectionID == "UD") || (sectionID == "ED")) 369 { 370 json = section.getJSON(creatorID, plugins); 371 } 372 else 373 { 374 json = section.getJSON(); 375 } 376 377 buf += "\"" + sectionName + "\": {\n"; 378 379 if (json) 380 { 381 buf += *json + "\n},\n"; 382 } 383 else 384 { 385 jsonInsert(buf, pv::sectionVer, 386 getNumberString("%d", section.header().version), 1); 387 jsonInsert(buf, pv::subSection, 388 getNumberString("%d", section.header().subType), 1); 389 jsonInsert(buf, pv::createdBy, 390 getNumberString("0x%X", section.header().componentID), 391 1); 392 393 std::vector<uint8_t> data; 394 Stream s{data}; 395 section.flatten(s); 396 std::string dstr = 397 dumpHex(std::data(data) + SectionHeader::flattenedSize(), 398 data.size() - SectionHeader::flattenedSize(), 2); 399 std::string jsonIndent(indentLevel, 0x20); 400 buf += jsonIndent + "\"Data\": [\n"; 401 buf += dstr; 402 buf += jsonIndent + "]\n"; 403 buf += "},\n"; 404 } 405 } 406 else 407 { 408 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n"; 409 } 410 } 411 412 std::map<uint16_t, size_t> PEL::getPluralSections() const 413 { 414 std::map<uint16_t, size_t> sectionCounts; 415 416 for (const auto& section : optionalSections()) 417 { 418 if (sectionCounts.find(section->header().id) == sectionCounts.end()) 419 { 420 sectionCounts[section->header().id] = 1; 421 } 422 else 423 { 424 sectionCounts[section->header().id]++; 425 } 426 } 427 428 std::map<uint16_t, size_t> sections; 429 for (const auto& [id, count] : sectionCounts) 430 { 431 if (count > 1) 432 { 433 // Start with 0 here and printSectionInJSON() 434 // will increment it as it goes. 435 sections.emplace(id, 0); 436 } 437 } 438 439 return sections; 440 } 441 442 void PEL::toJSON(message::Registry& registry, 443 const std::vector<std::string>& plugins) const 444 { 445 auto sections = getPluralSections(); 446 447 std::string buf = "{\n"; 448 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins); 449 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins); 450 for (auto& section : this->optionalSections()) 451 { 452 printSectionInJSON(*(section.get()), buf, sections, registry, plugins, 453 _ph->creatorID()); 454 } 455 buf += "}"; 456 std::size_t found = buf.rfind(","); 457 if (found != std::string::npos) 458 buf.replace(found, 1, ""); 459 std::cout << buf << std::endl; 460 } 461 462 bool PEL::addUserDataSection(std::unique_ptr<UserData> userData) 463 { 464 if (size() + userData->header().size > _maxPELSize) 465 { 466 if (userData->shrink(_maxPELSize - size())) 467 { 468 _optionalSections.push_back(std::move(userData)); 469 } 470 else 471 { 472 log<level::WARNING>( 473 "Could not shrink UserData section. Dropping", 474 entry("SECTION_SIZE=%d\n", userData->header().size), 475 entry("COMPONENT_ID=0x%02X", userData->header().componentID), 476 entry("SUBTYPE=0x%X", userData->header().subType), 477 entry("VERSION=0x%X", userData->header().version)); 478 return false; 479 } 480 } 481 else 482 { 483 _optionalSections.push_back(std::move(userData)); 484 } 485 return true; 486 } 487 488 nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles) 489 { 490 nlohmann::json callouts; 491 492 for (const auto& file : ffdcFiles) 493 { 494 if ((file.format == UserDataFormat::json) && 495 (file.subType == jsonCalloutSubtype)) 496 { 497 auto data = util::readFD(file.fd); 498 if (data.empty()) 499 { 500 throw std::runtime_error{ 501 "Could not get data from JSON callout file descriptor"}; 502 } 503 504 std::string jsonString{data.begin(), data.begin() + data.size()}; 505 506 callouts = nlohmann::json::parse(jsonString); 507 break; 508 } 509 } 510 511 return callouts; 512 } 513 514 bool PEL::isHwCalloutPresent() const 515 { 516 auto pSRC = primarySRC(); 517 if (!pSRC) 518 { 519 return false; 520 } 521 522 bool calloutPresent = false; 523 if ((*pSRC)->callouts()) 524 { 525 for (auto& i : (*pSRC)->callouts()->callouts()) 526 { 527 if (((*i).fruIdentity())) 528 { 529 auto& fruId = (*i).fruIdentity(); 530 if ((*fruId).failingComponentType() == 531 src::FRUIdentity::hardwareFRU) 532 { 533 calloutPresent = true; 534 break; 535 } 536 } 537 } 538 } 539 540 return calloutPresent; 541 } 542 543 void PEL::updateSysInfoInExtendedUserDataSection( 544 const DataInterfaceBase& dataIface) 545 { 546 const AdditionalData additionalData; 547 548 // Check for PEL from Hostboot 549 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot)) 550 { 551 // Get the ED section from PEL 552 auto op = std::find_if(_optionalSections.begin(), 553 _optionalSections.end(), [](auto& section) { 554 return section->header().id == 555 static_cast<uint16_t>( 556 SectionID::extUserData); 557 }); 558 559 // Check for ED section found and its not the last section of PEL 560 if (op != _optionalSections.end()) 561 { 562 // Get the extended user data class mapped to found section 563 auto extUserData = static_cast<ExtendedUserData*>(op->get()); 564 565 // Check for the creator ID is for OpenBMC 566 if (extUserData->creatorID() == 567 static_cast<uint8_t>(CreatorID::openBMC)) 568 { 569 // Update subtype and component id 570 auto subType = static_cast<uint8_t>(UserDataFormat::json); 571 auto componentId = 572 static_cast<uint16_t>(ComponentID::phosphorLogging); 573 574 // Update system data to ED section 575 auto ud = util::makeSysInfoUserDataSection(additionalData, 576 dataIface, false); 577 extUserData->updateDataSection(subType, componentId, 578 ud->data()); 579 } 580 } 581 } 582 } 583 584 void PEL::updateTerminateBitInSRCSection() 585 { 586 // Check for pel severity of type - 0x51 = critical error, system 587 // termination 588 if (_uh->severity() == 0x51) 589 { 590 // Get the primary SRC section 591 auto pSRC = primarySRC(); 592 if (pSRC) 593 { 594 (*pSRC)->setTerminateBit(); 595 } 596 } 597 } 598 599 namespace util 600 { 601 602 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 603 { 604 auto jsonString = json.dump(); 605 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 606 607 // Pad to a 4 byte boundary 608 while ((jsonData.size() % 4) != 0) 609 { 610 jsonData.push_back(0); 611 } 612 613 return std::make_unique<UserData>( 614 static_cast<uint16_t>(ComponentID::phosphorLogging), 615 static_cast<uint8_t>(UserDataFormat::json), 616 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 617 } 618 619 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 620 { 621 assert(!ad.empty()); 622 nlohmann::json json; 623 624 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 625 if (ad.getValue("ESEL")) 626 { 627 auto newAD = ad; 628 newAD.remove("ESEL"); 629 json = newAD.toJSON(); 630 } 631 else 632 { 633 json = ad.toJSON(); 634 } 635 636 return makeJSONUserDataSection(json); 637 } 638 639 void addProcessNameToJSON(nlohmann::json& json, 640 const std::optional<std::string>& pid, 641 const DataInterfaceBase& dataIface) 642 { 643 std::string name{unknownValue}; 644 645 try 646 { 647 if (pid) 648 { 649 auto n = dataIface.getProcessName(*pid); 650 if (n) 651 { 652 name = *n; 653 } 654 } 655 } 656 catch (const std::exception& e) 657 {} 658 659 if (pid) 660 { 661 json["Process Name"] = std::move(name); 662 } 663 } 664 665 void addBMCFWVersionIDToJSON(nlohmann::json& json, 666 const DataInterfaceBase& dataIface) 667 { 668 auto id = dataIface.getBMCFWVersionID(); 669 if (id.empty()) 670 { 671 id = unknownValue; 672 } 673 674 json["FW Version ID"] = std::move(id); 675 } 676 677 std::string lastSegment(char separator, std::string data) 678 { 679 auto pos = data.find_last_of(separator); 680 if (pos != std::string::npos) 681 { 682 data = data.substr(pos + 1); 683 } 684 685 return data; 686 } 687 688 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface) 689 { 690 auto keyword = dataIface.getSystemIMKeyword(); 691 692 std::string value{}; 693 694 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) { 695 value += fmt::format("{:02X}", byte); 696 }); 697 698 json["System IM"] = value; 699 } 700 701 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 702 { 703 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 704 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 705 json["HostState"] = lastSegment('.', dataIface.getHostState()); 706 json["BootState"] = lastSegment('.', dataIface.getBootState()); 707 } 708 709 void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface) 710 { 711 auto seconds = dataIface.getUptimeInSeconds(); 712 if (seconds) 713 { 714 json["BMCUptime"] = dataIface.getBMCUptime(*seconds); 715 } 716 else 717 { 718 json["BMCUptime"] = ""; 719 } 720 json["BMCLoad"] = dataIface.getBMCLoadAvg(); 721 } 722 723 std::unique_ptr<UserData> 724 makeSysInfoUserDataSection(const AdditionalData& ad, 725 const DataInterfaceBase& dataIface, 726 bool addUptime) 727 { 728 nlohmann::json json; 729 730 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 731 addBMCFWVersionIDToJSON(json, dataIface); 732 addIMKeyword(json, dataIface); 733 addStatesToJSON(json, dataIface); 734 735 if (addUptime) 736 { 737 addBMCUptime(json, dataIface); 738 } 739 740 return makeJSONUserDataSection(json); 741 } 742 743 std::vector<uint8_t> readFD(int fd) 744 { 745 std::vector<uint8_t> data; 746 747 // Get the size 748 struct stat s; 749 int r = fstat(fd, &s); 750 if (r != 0) 751 { 752 auto e = errno; 753 log<level::ERR>("Could not get FFDC file size from FD", 754 entry("ERRNO=%d", e)); 755 return data; 756 } 757 758 if (0 == s.st_size) 759 { 760 log<level::ERR>("FFDC file is empty"); 761 return data; 762 } 763 764 data.resize(s.st_size); 765 766 // Make sure its at the beginning, as maybe another 767 // extension already used it. 768 r = lseek(fd, 0, SEEK_SET); 769 if (r == -1) 770 { 771 auto e = errno; 772 log<level::ERR>("Could not seek to beginning of FFDC file", 773 entry("ERRNO=%d", e)); 774 return data; 775 } 776 777 r = read(fd, data.data(), s.st_size); 778 if (r == -1) 779 { 780 auto e = errno; 781 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e)); 782 } 783 else if (r != s.st_size) 784 { 785 log<level::WARNING>("Could not read full FFDC file", 786 entry("FILE_SIZE=%d", s.st_size), 787 entry("SIZE_READ=%d", r)); 788 } 789 790 return data; 791 } 792 793 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID, 794 const PelFFDCfile& file) 795 { 796 auto data = readFD(file.fd); 797 798 if (data.empty()) 799 { 800 return std::unique_ptr<UserData>(); 801 } 802 803 // The data needs 4 Byte alignment, and save amount padded for the 804 // CBOR case. 805 uint32_t pad = 0; 806 while (data.size() % 4) 807 { 808 data.push_back(0); 809 pad++; 810 } 811 812 // For JSON, CBOR, and Text use our component ID, subType, and version, 813 // otherwise use the supplied ones. 814 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging); 815 uint8_t subType{}; 816 uint8_t version{}; 817 818 switch (file.format) 819 { 820 case UserDataFormat::json: 821 subType = static_cast<uint8_t>(UserDataFormat::json); 822 version = static_cast<uint8_t>(UserDataFormatVersion::json); 823 break; 824 case UserDataFormat::cbor: 825 subType = static_cast<uint8_t>(UserDataFormat::cbor); 826 version = static_cast<uint8_t>(UserDataFormatVersion::cbor); 827 828 // The CBOR parser will fail on the extra pad bytes since they 829 // aren't CBOR. Add the amount we padded to the end and other 830 // code will remove it all before parsing. 831 { 832 data.resize(data.size() + 4); 833 Stream stream{data}; 834 stream.offset(data.size() - 4); 835 stream << pad; 836 } 837 838 break; 839 case UserDataFormat::text: 840 subType = static_cast<uint8_t>(UserDataFormat::text); 841 version = static_cast<uint8_t>(UserDataFormatVersion::text); 842 break; 843 case UserDataFormat::custom: 844 default: 845 // Use the passed in values 846 compID = componentID; 847 subType = file.subType; 848 version = file.version; 849 break; 850 } 851 852 return std::make_unique<UserData>(compID, subType, version, data); 853 } 854 855 } // namespace util 856 857 } // namespace pels 858 } // namespace openpower 859