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, const JournalBase& journal) 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 addJournalSections(regEntry, journal); 199 200 _ph->setSectionCount(2 + _optionalSections.size()); 201 202 checkRulesAndFix(); 203 } 204 205 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {} 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] = pel_rules::check(0, _uh->eventType(), 333 _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(creatorID); 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 _ph->creatorID()); 452 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins, 453 _ph->creatorID()); 454 for (auto& section : this->optionalSections()) 455 { 456 printSectionInJSON(*(section.get()), buf, sections, registry, plugins, 457 _ph->creatorID()); 458 } 459 buf += "}"; 460 std::size_t found = buf.rfind(","); 461 if (found != std::string::npos) 462 buf.replace(found, 1, ""); 463 std::cout << buf << std::endl; 464 } 465 466 bool PEL::addUserDataSection(std::unique_ptr<UserData> userData) 467 { 468 if (size() + userData->header().size > _maxPELSize) 469 { 470 if (userData->shrink(_maxPELSize - size())) 471 { 472 _optionalSections.push_back(std::move(userData)); 473 } 474 else 475 { 476 log<level::WARNING>( 477 "Could not shrink UserData section. Dropping", 478 entry("SECTION_SIZE=%d\n", userData->header().size), 479 entry("COMPONENT_ID=0x%02X", userData->header().componentID), 480 entry("SUBTYPE=0x%X", userData->header().subType), 481 entry("VERSION=0x%X", userData->header().version)); 482 return false; 483 } 484 } 485 else 486 { 487 _optionalSections.push_back(std::move(userData)); 488 } 489 return true; 490 } 491 492 nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles) 493 { 494 nlohmann::json callouts; 495 496 for (const auto& file : ffdcFiles) 497 { 498 if ((file.format == UserDataFormat::json) && 499 (file.subType == jsonCalloutSubtype)) 500 { 501 auto data = util::readFD(file.fd); 502 if (data.empty()) 503 { 504 throw std::runtime_error{ 505 "Could not get data from JSON callout file descriptor"}; 506 } 507 508 std::string jsonString{data.begin(), data.begin() + data.size()}; 509 510 callouts = nlohmann::json::parse(jsonString); 511 break; 512 } 513 } 514 515 return callouts; 516 } 517 518 bool PEL::isHwCalloutPresent() const 519 { 520 auto pSRC = primarySRC(); 521 if (!pSRC) 522 { 523 return false; 524 } 525 526 bool calloutPresent = false; 527 if ((*pSRC)->callouts()) 528 { 529 for (auto& i : (*pSRC)->callouts()->callouts()) 530 { 531 if (((*i).fruIdentity())) 532 { 533 auto& fruId = (*i).fruIdentity(); 534 if ((*fruId).failingComponentType() == 535 src::FRUIdentity::hardwareFRU) 536 { 537 calloutPresent = true; 538 break; 539 } 540 } 541 } 542 } 543 544 return calloutPresent; 545 } 546 547 void PEL::updateSysInfoInExtendedUserDataSection( 548 const DataInterfaceBase& dataIface) 549 { 550 const AdditionalData additionalData; 551 552 // Check for PEL from Hostboot 553 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot)) 554 { 555 // Get the ED section from PEL 556 auto op = std::find_if(_optionalSections.begin(), 557 _optionalSections.end(), [](auto& section) { 558 return section->header().id == 559 static_cast<uint16_t>( 560 SectionID::extUserData); 561 }); 562 563 // Check for ED section found and its not the last section of PEL 564 if (op != _optionalSections.end()) 565 { 566 // Get the extended user data class mapped to found section 567 auto extUserData = static_cast<ExtendedUserData*>(op->get()); 568 569 // Check for the creator ID is for OpenBMC 570 if (extUserData->creatorID() == 571 static_cast<uint8_t>(CreatorID::openBMC)) 572 { 573 // Update subtype and component id 574 auto subType = static_cast<uint8_t>(UserDataFormat::json); 575 auto componentId = 576 static_cast<uint16_t>(ComponentID::phosphorLogging); 577 578 // Update system data to ED section 579 auto ud = util::makeSysInfoUserDataSection(additionalData, 580 dataIface, false); 581 extUserData->updateDataSection(subType, componentId, 582 ud->data()); 583 } 584 } 585 } 586 } 587 588 void PEL::updateTerminateBitInSRCSection() 589 { 590 // Check for pel severity of type - 0x51 = critical error, system 591 // termination 592 if (_uh->severity() == 0x51) 593 { 594 // Get the primary SRC section 595 auto pSRC = primarySRC(); 596 if (pSRC) 597 { 598 (*pSRC)->setTerminateBit(); 599 } 600 } 601 } 602 603 void PEL::addJournalSections(const message::Entry& regEntry, 604 const JournalBase& journal) 605 { 606 if (!regEntry.journalCapture) 607 { 608 return; 609 } 610 611 // Write all unwritten journal data to disk. 612 journal.sync(); 613 614 const auto& jc = regEntry.journalCapture.value(); 615 std::vector<std::vector<std::string>> allMessages; 616 617 if (std::holds_alternative<size_t>(jc)) 618 { 619 // Get the previous numLines journal entries 620 const auto& numLines = std::get<size_t>(jc); 621 try 622 { 623 auto messages = journal.getMessages("", numLines); 624 if (!messages.empty()) 625 { 626 allMessages.push_back(std::move(messages)); 627 } 628 } 629 catch (const std::exception& e) 630 { 631 log<level::ERR>( 632 fmt::format("Failed during journal collection: {}", e.what()) 633 .c_str()); 634 } 635 } 636 else if (std::holds_alternative<message::AppCaptureList>(jc)) 637 { 638 // Get journal entries based on the syslog id field. 639 const auto& sections = std::get<message::AppCaptureList>(jc); 640 for (const auto& [syslogID, numLines] : sections) 641 { 642 try 643 { 644 auto messages = journal.getMessages(syslogID, numLines); 645 if (!messages.empty()) 646 { 647 allMessages.push_back(std::move(messages)); 648 } 649 } 650 catch (const std::exception& e) 651 { 652 log<level::ERR>( 653 fmt::format("Failed during journal collection: {}", 654 e.what()) 655 .c_str()); 656 } 657 } 658 } 659 660 // Create the UserData sections 661 for (const auto& messages : allMessages) 662 { 663 auto buffer = util::flattenLines(messages); 664 665 // If the buffer is way too big, it can overflow the uint16_t 666 // PEL section size field that is checked below so do a cursory 667 // check here. 668 if (buffer.size() > _maxPELSize) 669 { 670 log<level::WARNING>( 671 "Journal UserData section does not fit in PEL, dropping"); 672 log<level::WARNING>(fmt::format("PEL size = {}, data size = {}", 673 size(), buffer.size()) 674 .c_str()); 675 continue; 676 } 677 678 // Sections must be 4 byte aligned. 679 while (buffer.size() % 4 != 0) 680 { 681 buffer.push_back(0); 682 } 683 684 auto ud = std::make_unique<UserData>( 685 static_cast<uint16_t>(ComponentID::phosphorLogging), 686 static_cast<uint8_t>(UserDataFormat::text), 687 static_cast<uint8_t>(UserDataFormatVersion::text), buffer); 688 689 if (size() + ud->header().size <= _maxPELSize) 690 { 691 _optionalSections.push_back(std::move(ud)); 692 } 693 else 694 { 695 // Don't attempt to shrink here since we'd be dropping the 696 // most recent journal entries which would be confusing. 697 log<level::WARNING>( 698 "Journal UserData section does not fit in PEL, dropping"); 699 log<level::WARNING>(fmt::format("PEL size = {}, UserData size = {}", 700 size(), ud->header().size) 701 .c_str()); 702 ud.reset(); 703 continue; 704 } 705 } 706 } 707 708 namespace util 709 { 710 711 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 712 { 713 auto jsonString = json.dump(); 714 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 715 716 // Pad to a 4 byte boundary 717 while ((jsonData.size() % 4) != 0) 718 { 719 jsonData.push_back(0); 720 } 721 722 return std::make_unique<UserData>( 723 static_cast<uint16_t>(ComponentID::phosphorLogging), 724 static_cast<uint8_t>(UserDataFormat::json), 725 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 726 } 727 728 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 729 { 730 assert(!ad.empty()); 731 nlohmann::json json; 732 733 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 734 if (ad.getValue("ESEL")) 735 { 736 auto newAD = ad; 737 newAD.remove("ESEL"); 738 json = newAD.toJSON(); 739 } 740 else 741 { 742 json = ad.toJSON(); 743 } 744 745 return makeJSONUserDataSection(json); 746 } 747 748 void addProcessNameToJSON(nlohmann::json& json, 749 const std::optional<std::string>& pid, 750 const DataInterfaceBase& dataIface) 751 { 752 std::string name{unknownValue}; 753 754 try 755 { 756 if (pid) 757 { 758 auto n = dataIface.getProcessName(*pid); 759 if (n) 760 { 761 name = *n; 762 } 763 } 764 } 765 catch (const std::exception& e) 766 {} 767 768 if (pid) 769 { 770 json["Process Name"] = std::move(name); 771 } 772 } 773 774 void addBMCFWVersionIDToJSON(nlohmann::json& json, 775 const DataInterfaceBase& dataIface) 776 { 777 auto id = dataIface.getBMCFWVersionID(); 778 if (id.empty()) 779 { 780 id = unknownValue; 781 } 782 783 json["FW Version ID"] = std::move(id); 784 } 785 786 std::string lastSegment(char separator, std::string data) 787 { 788 auto pos = data.find_last_of(separator); 789 if (pos != std::string::npos) 790 { 791 data = data.substr(pos + 1); 792 } 793 794 return data; 795 } 796 797 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface) 798 { 799 auto keyword = dataIface.getSystemIMKeyword(); 800 801 std::string value{}; 802 803 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) { 804 value += fmt::format("{:02X}", byte); 805 }); 806 807 json["System IM"] = value; 808 } 809 810 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 811 { 812 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 813 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 814 json["HostState"] = lastSegment('.', dataIface.getHostState()); 815 json["BootState"] = lastSegment('.', dataIface.getBootState()); 816 } 817 818 void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface) 819 { 820 auto seconds = dataIface.getUptimeInSeconds(); 821 if (seconds) 822 { 823 json["BMCUptime"] = dataIface.getBMCUptime(*seconds); 824 } 825 else 826 { 827 json["BMCUptime"] = ""; 828 } 829 json["BMCLoad"] = dataIface.getBMCLoadAvg(); 830 } 831 832 std::unique_ptr<UserData> 833 makeSysInfoUserDataSection(const AdditionalData& ad, 834 const DataInterfaceBase& dataIface, 835 bool addUptime) 836 { 837 nlohmann::json json; 838 839 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 840 addBMCFWVersionIDToJSON(json, dataIface); 841 addIMKeyword(json, dataIface); 842 addStatesToJSON(json, dataIface); 843 844 if (addUptime) 845 { 846 addBMCUptime(json, dataIface); 847 } 848 849 return makeJSONUserDataSection(json); 850 } 851 852 std::vector<uint8_t> readFD(int fd) 853 { 854 std::vector<uint8_t> data; 855 856 // Get the size 857 struct stat s; 858 int r = fstat(fd, &s); 859 if (r != 0) 860 { 861 auto e = errno; 862 log<level::ERR>("Could not get FFDC file size from FD", 863 entry("ERRNO=%d", e)); 864 return data; 865 } 866 867 if (0 == s.st_size) 868 { 869 log<level::ERR>("FFDC file is empty"); 870 return data; 871 } 872 873 data.resize(s.st_size); 874 875 // Make sure its at the beginning, as maybe another 876 // extension already used it. 877 r = lseek(fd, 0, SEEK_SET); 878 if (r == -1) 879 { 880 auto e = errno; 881 log<level::ERR>("Could not seek to beginning of FFDC file", 882 entry("ERRNO=%d", e)); 883 return data; 884 } 885 886 r = read(fd, data.data(), s.st_size); 887 if (r == -1) 888 { 889 auto e = errno; 890 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e)); 891 } 892 else if (r != s.st_size) 893 { 894 log<level::WARNING>("Could not read full FFDC file", 895 entry("FILE_SIZE=%d", s.st_size), 896 entry("SIZE_READ=%d", r)); 897 } 898 899 return data; 900 } 901 902 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID, 903 const PelFFDCfile& file) 904 { 905 auto data = readFD(file.fd); 906 907 if (data.empty()) 908 { 909 return std::unique_ptr<UserData>(); 910 } 911 912 // The data needs 4 Byte alignment, and save amount padded for the 913 // CBOR case. 914 uint32_t pad = 0; 915 while (data.size() % 4) 916 { 917 data.push_back(0); 918 pad++; 919 } 920 921 // For JSON, CBOR, and Text use our component ID, subType, and version, 922 // otherwise use the supplied ones. 923 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging); 924 uint8_t subType{}; 925 uint8_t version{}; 926 927 switch (file.format) 928 { 929 case UserDataFormat::json: 930 subType = static_cast<uint8_t>(UserDataFormat::json); 931 version = static_cast<uint8_t>(UserDataFormatVersion::json); 932 break; 933 case UserDataFormat::cbor: 934 subType = static_cast<uint8_t>(UserDataFormat::cbor); 935 version = static_cast<uint8_t>(UserDataFormatVersion::cbor); 936 937 // The CBOR parser will fail on the extra pad bytes since they 938 // aren't CBOR. Add the amount we padded to the end and other 939 // code will remove it all before parsing. 940 { 941 data.resize(data.size() + 4); 942 Stream stream{data}; 943 stream.offset(data.size() - 4); 944 stream << pad; 945 } 946 947 break; 948 case UserDataFormat::text: 949 subType = static_cast<uint8_t>(UserDataFormat::text); 950 version = static_cast<uint8_t>(UserDataFormatVersion::text); 951 break; 952 case UserDataFormat::custom: 953 default: 954 // Use the passed in values 955 compID = componentID; 956 subType = file.subType; 957 version = file.version; 958 break; 959 } 960 961 return std::make_unique<UserData>(compID, subType, version, data); 962 } 963 964 std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines) 965 { 966 std::vector<uint8_t> out; 967 968 for (const auto& line : lines) 969 { 970 out.insert(out.end(), line.begin(), line.end()); 971 972 if (out.back() != '\n') 973 { 974 out.push_back('\n'); 975 } 976 } 977 978 return out; 979 } 980 981 } // namespace util 982 983 } // namespace pels 984 } // namespace openpower 985