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