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 = std::any_of(ffdcFiles.begin(), ffdcFiles.end(), 68 [](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(_optionalSections.begin(), _optionalSections.end(), 313 [](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(), 558 [](auto& section) { 559 return section->header().id == 560 static_cast<uint16_t>(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 bool PEL::getDeconfigFlag() const 589 { 590 auto creator = static_cast<CreatorID>(_ph->creatorID()); 591 592 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot)) 593 { 594 auto src = primarySRC(); 595 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured); 596 } 597 return false; 598 } 599 600 bool PEL::getGuardFlag() const 601 { 602 auto creator = static_cast<CreatorID>(_ph->creatorID()); 603 604 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot)) 605 { 606 auto src = primarySRC(); 607 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::guarded); 608 } 609 return false; 610 } 611 612 void PEL::updateTerminateBitInSRCSection() 613 { 614 // Check for pel severity of type - 0x51 = critical error, system 615 // termination 616 if (_uh->severity() == 0x51) 617 { 618 // Get the primary SRC section 619 auto pSRC = primarySRC(); 620 if (pSRC) 621 { 622 (*pSRC)->setTerminateBit(); 623 } 624 } 625 } 626 627 void PEL::addJournalSections(const message::Entry& regEntry, 628 const JournalBase& journal) 629 { 630 if (!regEntry.journalCapture) 631 { 632 return; 633 } 634 635 // Write all unwritten journal data to disk. 636 journal.sync(); 637 638 const auto& jc = regEntry.journalCapture.value(); 639 std::vector<std::vector<std::string>> allMessages; 640 641 if (std::holds_alternative<size_t>(jc)) 642 { 643 // Get the previous numLines journal entries 644 const auto& numLines = std::get<size_t>(jc); 645 try 646 { 647 auto messages = journal.getMessages("", numLines); 648 if (!messages.empty()) 649 { 650 allMessages.push_back(std::move(messages)); 651 } 652 } 653 catch (const std::exception& e) 654 { 655 log<level::ERR>( 656 fmt::format("Failed during journal collection: {}", e.what()) 657 .c_str()); 658 } 659 } 660 else if (std::holds_alternative<message::AppCaptureList>(jc)) 661 { 662 // Get journal entries based on the syslog id field. 663 const auto& sections = std::get<message::AppCaptureList>(jc); 664 for (const auto& [syslogID, numLines] : sections) 665 { 666 try 667 { 668 auto messages = journal.getMessages(syslogID, numLines); 669 if (!messages.empty()) 670 { 671 allMessages.push_back(std::move(messages)); 672 } 673 } 674 catch (const std::exception& e) 675 { 676 log<level::ERR>( 677 fmt::format("Failed during journal collection: {}", 678 e.what()) 679 .c_str()); 680 } 681 } 682 } 683 684 // Create the UserData sections 685 for (const auto& messages : allMessages) 686 { 687 auto buffer = util::flattenLines(messages); 688 689 // If the buffer is way too big, it can overflow the uint16_t 690 // PEL section size field that is checked below so do a cursory 691 // check here. 692 if (buffer.size() > _maxPELSize) 693 { 694 log<level::WARNING>( 695 "Journal UserData section does not fit in PEL, dropping"); 696 log<level::WARNING>(fmt::format("PEL size = {}, data size = {}", 697 size(), buffer.size()) 698 .c_str()); 699 continue; 700 } 701 702 // Sections must be 4 byte aligned. 703 while (buffer.size() % 4 != 0) 704 { 705 buffer.push_back(0); 706 } 707 708 auto ud = std::make_unique<UserData>( 709 static_cast<uint16_t>(ComponentID::phosphorLogging), 710 static_cast<uint8_t>(UserDataFormat::text), 711 static_cast<uint8_t>(UserDataFormatVersion::text), buffer); 712 713 if (size() + ud->header().size <= _maxPELSize) 714 { 715 _optionalSections.push_back(std::move(ud)); 716 } 717 else 718 { 719 // Don't attempt to shrink here since we'd be dropping the 720 // most recent journal entries which would be confusing. 721 log<level::WARNING>( 722 "Journal UserData section does not fit in PEL, dropping"); 723 log<level::WARNING>(fmt::format("PEL size = {}, UserData size = {}", 724 size(), ud->header().size) 725 .c_str()); 726 ud.reset(); 727 continue; 728 } 729 } 730 } 731 732 namespace util 733 { 734 735 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 736 { 737 auto jsonString = json.dump(); 738 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 739 740 // Pad to a 4 byte boundary 741 while ((jsonData.size() % 4) != 0) 742 { 743 jsonData.push_back(0); 744 } 745 746 return std::make_unique<UserData>( 747 static_cast<uint16_t>(ComponentID::phosphorLogging), 748 static_cast<uint8_t>(UserDataFormat::json), 749 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 750 } 751 752 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 753 { 754 assert(!ad.empty()); 755 nlohmann::json json; 756 757 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 758 if (ad.getValue("ESEL")) 759 { 760 auto newAD = ad; 761 newAD.remove("ESEL"); 762 json = newAD.toJSON(); 763 } 764 else 765 { 766 json = ad.toJSON(); 767 } 768 769 return makeJSONUserDataSection(json); 770 } 771 772 void addProcessNameToJSON(nlohmann::json& json, 773 const std::optional<std::string>& pid, 774 const DataInterfaceBase& dataIface) 775 { 776 std::string name{unknownValue}; 777 778 try 779 { 780 if (pid) 781 { 782 auto n = dataIface.getProcessName(*pid); 783 if (n) 784 { 785 name = *n; 786 } 787 } 788 } 789 catch (const std::exception& e) 790 {} 791 792 if (pid) 793 { 794 json["Process Name"] = std::move(name); 795 } 796 } 797 798 void addBMCFWVersionIDToJSON(nlohmann::json& json, 799 const DataInterfaceBase& dataIface) 800 { 801 auto id = dataIface.getBMCFWVersionID(); 802 if (id.empty()) 803 { 804 id = unknownValue; 805 } 806 807 json["FW Version ID"] = std::move(id); 808 } 809 810 std::string lastSegment(char separator, std::string data) 811 { 812 auto pos = data.find_last_of(separator); 813 if (pos != std::string::npos) 814 { 815 data = data.substr(pos + 1); 816 } 817 818 return data; 819 } 820 821 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface) 822 { 823 auto keyword = dataIface.getSystemIMKeyword(); 824 825 std::string value{}; 826 827 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) { 828 value += fmt::format("{:02X}", byte); 829 }); 830 831 json["System IM"] = value; 832 } 833 834 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 835 { 836 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 837 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 838 json["HostState"] = lastSegment('.', dataIface.getHostState()); 839 json["BootState"] = lastSegment('.', dataIface.getBootState()); 840 } 841 842 void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface) 843 { 844 auto seconds = dataIface.getUptimeInSeconds(); 845 if (seconds) 846 { 847 json["BMCUptime"] = dataIface.getBMCUptime(*seconds); 848 } 849 else 850 { 851 json["BMCUptime"] = ""; 852 } 853 json["BMCLoad"] = dataIface.getBMCLoadAvg(); 854 } 855 856 std::unique_ptr<UserData> 857 makeSysInfoUserDataSection(const AdditionalData& ad, 858 const DataInterfaceBase& dataIface, 859 bool addUptime) 860 { 861 nlohmann::json json; 862 863 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 864 addBMCFWVersionIDToJSON(json, dataIface); 865 addIMKeyword(json, dataIface); 866 addStatesToJSON(json, dataIface); 867 868 if (addUptime) 869 { 870 addBMCUptime(json, dataIface); 871 } 872 873 return makeJSONUserDataSection(json); 874 } 875 876 std::vector<uint8_t> readFD(int fd) 877 { 878 std::vector<uint8_t> data; 879 880 // Get the size 881 struct stat s; 882 int r = fstat(fd, &s); 883 if (r != 0) 884 { 885 auto e = errno; 886 log<level::ERR>("Could not get FFDC file size from FD", 887 entry("ERRNO=%d", e)); 888 return data; 889 } 890 891 if (0 == s.st_size) 892 { 893 log<level::ERR>("FFDC file is empty"); 894 return data; 895 } 896 897 data.resize(s.st_size); 898 899 // Make sure its at the beginning, as maybe another 900 // extension already used it. 901 r = lseek(fd, 0, SEEK_SET); 902 if (r == -1) 903 { 904 auto e = errno; 905 log<level::ERR>("Could not seek to beginning of FFDC file", 906 entry("ERRNO=%d", e)); 907 return data; 908 } 909 910 r = read(fd, data.data(), s.st_size); 911 if (r == -1) 912 { 913 auto e = errno; 914 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e)); 915 } 916 else if (r != s.st_size) 917 { 918 log<level::WARNING>("Could not read full FFDC file", 919 entry("FILE_SIZE=%d", s.st_size), 920 entry("SIZE_READ=%d", r)); 921 } 922 923 return data; 924 } 925 926 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID, 927 const PelFFDCfile& file) 928 { 929 auto data = readFD(file.fd); 930 931 if (data.empty()) 932 { 933 return std::unique_ptr<UserData>(); 934 } 935 936 // The data needs 4 Byte alignment, and save amount padded for the 937 // CBOR case. 938 uint32_t pad = 0; 939 while (data.size() % 4) 940 { 941 data.push_back(0); 942 pad++; 943 } 944 945 // For JSON, CBOR, and Text use our component ID, subType, and version, 946 // otherwise use the supplied ones. 947 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging); 948 uint8_t subType{}; 949 uint8_t version{}; 950 951 switch (file.format) 952 { 953 case UserDataFormat::json: 954 subType = static_cast<uint8_t>(UserDataFormat::json); 955 version = static_cast<uint8_t>(UserDataFormatVersion::json); 956 break; 957 case UserDataFormat::cbor: 958 subType = static_cast<uint8_t>(UserDataFormat::cbor); 959 version = static_cast<uint8_t>(UserDataFormatVersion::cbor); 960 961 // The CBOR parser will fail on the extra pad bytes since they 962 // aren't CBOR. Add the amount we padded to the end and other 963 // code will remove it all before parsing. 964 { 965 data.resize(data.size() + 4); 966 Stream stream{data}; 967 stream.offset(data.size() - 4); 968 stream << pad; 969 } 970 971 break; 972 case UserDataFormat::text: 973 subType = static_cast<uint8_t>(UserDataFormat::text); 974 version = static_cast<uint8_t>(UserDataFormatVersion::text); 975 break; 976 case UserDataFormat::custom: 977 default: 978 // Use the passed in values 979 compID = componentID; 980 subType = file.subType; 981 version = file.version; 982 break; 983 } 984 985 return std::make_unique<UserData>(compID, subType, version, data); 986 } 987 988 std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines) 989 { 990 std::vector<uint8_t> out; 991 992 for (const auto& line : lines) 993 { 994 out.insert(out.end(), line.begin(), line.end()); 995 996 if (out.back() != '\n') 997 { 998 out.push_back('\n'); 999 } 1000 } 1001 1002 return out; 1003 } 1004 1005 } // namespace util 1006 1007 } // namespace pels 1008 } // namespace openpower 1009