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