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 <sys/stat.h> 40 #include <unistd.h> 41 42 #include <phosphor-logging/lg2.hpp> 43 44 #include <format> 45 #include <iostream> 46 47 namespace openpower 48 { 49 namespace pels 50 { 51 namespace pv = openpower::pels::pel_values; 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, const JournalBase& journal) 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 = std::any_of(ffdcFiles.begin(), ffdcFiles.end(), 67 [](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 = std::make_unique<sbe::SbeFFDC>(additionalData, 77 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 = std::make_unique<SRC>(regEntry, additionalData, callouts, 113 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 lg2::info("{NAME}: {MSG}", "NAME", name, "MSG", message); 192 } 193 } 194 } 195 196 addJournalSections(regEntry, journal); 197 198 _ph->setSectionCount(2 + _optionalSections.size()); 199 200 checkRulesAndFix(); 201 } 202 203 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {} 204 205 PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID) 206 { 207 populateFromRawData(data, obmcLogID); 208 } 209 210 void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID) 211 { 212 Stream pelData{data}; 213 _ph = std::make_unique<PrivateHeader>(pelData); 214 if (obmcLogID != 0) 215 { 216 _ph->setOBMCLogID(obmcLogID); 217 } 218 219 _uh = std::make_unique<UserHeader>(pelData); 220 221 // Use the section factory to create the rest of the objects 222 for (size_t i = 2; i < _ph->sectionCount(); i++) 223 { 224 auto section = section_factory::create(pelData); 225 _optionalSections.push_back(std::move(section)); 226 } 227 } 228 229 bool PEL::valid() const 230 { 231 bool valid = _ph->valid(); 232 233 if (valid) 234 { 235 valid = _uh->valid(); 236 } 237 238 if (valid) 239 { 240 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(), 241 [](const auto& section) { return section->valid(); })) 242 { 243 valid = false; 244 } 245 } 246 247 return valid; 248 } 249 250 void PEL::setCommitTime() 251 { 252 auto now = std::chrono::system_clock::now(); 253 _ph->setCommitTimestamp(getBCDTime(now)); 254 } 255 256 void PEL::assignID() 257 { 258 _ph->setID(generatePELID()); 259 } 260 261 void PEL::flatten(std::vector<uint8_t>& pelBuffer) const 262 { 263 Stream pelData{pelBuffer}; 264 265 if (!valid()) 266 { 267 lg2::warning("Unflattening an invalid PEL"); 268 } 269 270 _ph->flatten(pelData); 271 _uh->flatten(pelData); 272 273 for (auto& section : _optionalSections) 274 { 275 section->flatten(pelData); 276 } 277 } 278 279 std::vector<uint8_t> PEL::data() const 280 { 281 std::vector<uint8_t> pelData; 282 flatten(pelData); 283 return pelData; 284 } 285 286 size_t PEL::size() const 287 { 288 size_t size = 0; 289 290 if (_ph) 291 { 292 size += _ph->header().size; 293 } 294 295 if (_uh) 296 { 297 size += _uh->header().size; 298 } 299 300 for (const auto& section : _optionalSections) 301 { 302 size += section->header().size; 303 } 304 305 return size; 306 } 307 308 std::optional<SRC*> PEL::primarySRC() const 309 { 310 auto src = std::find_if(_optionalSections.begin(), _optionalSections.end(), 311 [](auto& section) { 312 return section->header().id == 313 static_cast<uint16_t>(SectionID::primarySRC); 314 }); 315 if (src != _optionalSections.end()) 316 { 317 return static_cast<SRC*>(src->get()); 318 } 319 320 return std::nullopt; 321 } 322 323 void PEL::checkRulesAndFix() 324 { 325 // Only fix if the action flags are at their default value which 326 // means they weren't specified in the registry. Otherwise 327 // assume the user knows what they are doing. 328 if (_uh->actionFlags() == actionFlagsDefault) 329 { 330 auto [actionFlags, eventType] = pel_rules::check(0, _uh->eventType(), 331 _uh->severity()); 332 333 _uh->setActionFlags(actionFlags); 334 _uh->setEventType(eventType); 335 } 336 } 337 338 void PEL::printSectionInJSON(const Section& section, std::string& buf, 339 std::map<uint16_t, size_t>& pluralSections, 340 message::Registry& registry, 341 const std::vector<std::string>& plugins, 342 uint8_t creatorID) const 343 { 344 char tmpB[5]; 345 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8), 346 static_cast<uint8_t>(section.header().id)}; 347 sprintf(tmpB, "%c%c", id[0], id[1]); 348 std::string sectionID(tmpB); 349 std::string sectionName = pv::sectionTitles.count(sectionID) 350 ? pv::sectionTitles.at(sectionID) 351 : "Unknown Section"; 352 353 // Add a count if there are multiple of this type of section 354 auto count = pluralSections.find(section.header().id); 355 if (count != pluralSections.end()) 356 { 357 sectionName += " " + std::to_string(count->second); 358 count->second++; 359 } 360 361 if (section.valid()) 362 { 363 std::optional<std::string> json; 364 if (sectionID == "PS" || sectionID == "SS") 365 { 366 json = section.getJSON(registry, plugins, creatorID); 367 } 368 else if ((sectionID == "UD") || (sectionID == "ED")) 369 { 370 json = section.getJSON(creatorID, plugins); 371 } 372 else 373 { 374 json = section.getJSON(creatorID); 375 } 376 377 buf += "\"" + sectionName + "\": {\n"; 378 379 if (json) 380 { 381 buf += *json + "\n},\n"; 382 } 383 else 384 { 385 jsonInsert(buf, pv::sectionVer, 386 getNumberString("%d", section.header().version), 1); 387 jsonInsert(buf, pv::subSection, 388 getNumberString("%d", section.header().subType), 1); 389 jsonInsert(buf, pv::createdBy, 390 getNumberString("0x%X", section.header().componentID), 391 1); 392 393 std::vector<uint8_t> data; 394 Stream s{data}; 395 section.flatten(s); 396 std::string dstr = 397 dumpHex(std::data(data) + SectionHeader::flattenedSize(), 398 data.size() - SectionHeader::flattenedSize(), 2); 399 std::string jsonIndent(indentLevel, 0x20); 400 buf += jsonIndent + "\"Data\": [\n"; 401 buf += dstr; 402 buf += jsonIndent + "]\n"; 403 buf += "},\n"; 404 } 405 } 406 else 407 { 408 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n"; 409 } 410 } 411 412 std::map<uint16_t, size_t> PEL::getPluralSections() const 413 { 414 std::map<uint16_t, size_t> sectionCounts; 415 416 for (const auto& section : optionalSections()) 417 { 418 if (sectionCounts.find(section->header().id) == sectionCounts.end()) 419 { 420 sectionCounts[section->header().id] = 1; 421 } 422 else 423 { 424 sectionCounts[section->header().id]++; 425 } 426 } 427 428 std::map<uint16_t, size_t> sections; 429 for (const auto& [id, count] : sectionCounts) 430 { 431 if (count > 1) 432 { 433 // Start with 0 here and printSectionInJSON() 434 // will increment it as it goes. 435 sections.emplace(id, 0); 436 } 437 } 438 439 return sections; 440 } 441 442 void PEL::toJSON(message::Registry& registry, 443 const std::vector<std::string>& plugins) const 444 { 445 auto sections = getPluralSections(); 446 447 std::string buf = "{\n"; 448 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins, 449 _ph->creatorID()); 450 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins, 451 _ph->creatorID()); 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 lg2::warning("Could not shrink UserData section. Dropping. " 475 "Section size = {SSIZE}, Component ID = {COMP_ID}, " 476 "Subtype = {SUBTYPE}, Version = {VERSION}", 477 "SSIZE", userData->header().size, "COMP_ID", 478 userData->header().componentID, "SUBTYPE", 479 userData->header().subType, "VERSION", 480 userData->header().version); 481 return false; 482 } 483 } 484 else 485 { 486 _optionalSections.push_back(std::move(userData)); 487 } 488 return true; 489 } 490 491 nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles) 492 { 493 nlohmann::json callouts; 494 495 for (const auto& file : ffdcFiles) 496 { 497 if ((file.format == UserDataFormat::json) && 498 (file.subType == jsonCalloutSubtype)) 499 { 500 auto data = util::readFD(file.fd); 501 if (data.empty()) 502 { 503 throw std::runtime_error{ 504 "Could not get data from JSON callout file descriptor"}; 505 } 506 507 std::string jsonString{data.begin(), data.begin() + data.size()}; 508 509 callouts = nlohmann::json::parse(jsonString); 510 break; 511 } 512 } 513 514 return callouts; 515 } 516 517 bool PEL::isHwCalloutPresent() const 518 { 519 auto pSRC = primarySRC(); 520 if (!pSRC) 521 { 522 return false; 523 } 524 525 bool calloutPresent = false; 526 if ((*pSRC)->callouts()) 527 { 528 for (auto& i : (*pSRC)->callouts()->callouts()) 529 { 530 if (((*i).fruIdentity())) 531 { 532 auto& fruId = (*i).fruIdentity(); 533 if ((*fruId).failingComponentType() == 534 src::FRUIdentity::hardwareFRU) 535 { 536 calloutPresent = true; 537 break; 538 } 539 } 540 } 541 } 542 543 return calloutPresent; 544 } 545 546 void PEL::updateSysInfoInExtendedUserDataSection( 547 const DataInterfaceBase& dataIface) 548 { 549 const AdditionalData additionalData; 550 551 // Check for PEL from Hostboot 552 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot)) 553 { 554 // Get the ED section from PEL 555 auto op = std::find_if(_optionalSections.begin(), 556 _optionalSections.end(), [](auto& section) { 557 return section->header().id == 558 static_cast<uint16_t>(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 bool PEL::getDeconfigFlag() const 587 { 588 auto creator = static_cast<CreatorID>(_ph->creatorID()); 589 590 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot)) 591 { 592 auto src = primarySRC(); 593 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured); 594 } 595 return false; 596 } 597 598 bool PEL::getGuardFlag() const 599 { 600 auto creator = static_cast<CreatorID>(_ph->creatorID()); 601 602 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot)) 603 { 604 auto src = primarySRC(); 605 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::guarded); 606 } 607 return false; 608 } 609 610 void PEL::updateTerminateBitInSRCSection() 611 { 612 // Check for pel severity of type - 0x51 = critical error, system 613 // termination 614 if (_uh->severity() == 0x51) 615 { 616 // Get the primary SRC section 617 auto pSRC = primarySRC(); 618 if (pSRC) 619 { 620 (*pSRC)->setTerminateBit(); 621 } 622 } 623 } 624 625 void PEL::addJournalSections(const message::Entry& regEntry, 626 const JournalBase& journal) 627 { 628 if (!regEntry.journalCapture) 629 { 630 return; 631 } 632 633 // Write all unwritten journal data to disk. 634 journal.sync(); 635 636 const auto& jc = regEntry.journalCapture.value(); 637 std::vector<std::vector<std::string>> allMessages; 638 639 if (std::holds_alternative<size_t>(jc)) 640 { 641 // Get the previous numLines journal entries 642 const auto& numLines = std::get<size_t>(jc); 643 try 644 { 645 auto messages = journal.getMessages("", numLines); 646 if (!messages.empty()) 647 { 648 allMessages.push_back(std::move(messages)); 649 } 650 } 651 catch (const std::exception& e) 652 { 653 lg2::error("Failed during journal collection: {ERROR}", "ERROR", e); 654 } 655 } 656 else if (std::holds_alternative<message::AppCaptureList>(jc)) 657 { 658 // Get journal entries based on the syslog id field. 659 const auto& sections = std::get<message::AppCaptureList>(jc); 660 for (const auto& [syslogID, numLines] : sections) 661 { 662 try 663 { 664 auto messages = journal.getMessages(syslogID, numLines); 665 if (!messages.empty()) 666 { 667 allMessages.push_back(std::move(messages)); 668 } 669 } 670 catch (const std::exception& e) 671 { 672 lg2::error("Failed during journal collection: {ERROR}", "ERROR", 673 e); 674 } 675 } 676 } 677 678 // Create the UserData sections 679 for (const auto& messages : allMessages) 680 { 681 auto buffer = util::flattenLines(messages); 682 683 // If the buffer is way too big, it can overflow the uint16_t 684 // PEL section size field that is checked below so do a cursory 685 // check here. 686 if (buffer.size() > _maxPELSize) 687 { 688 lg2::warning( 689 "Journal UserData section does not fit in PEL, dropping. " 690 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}", 691 "PEL_SIZE", size(), "DATA_SIZE", buffer.size()); 692 continue; 693 } 694 695 // Sections must be 4 byte aligned. 696 while (buffer.size() % 4 != 0) 697 { 698 buffer.push_back(0); 699 } 700 701 auto ud = std::make_unique<UserData>( 702 static_cast<uint16_t>(ComponentID::phosphorLogging), 703 static_cast<uint8_t>(UserDataFormat::text), 704 static_cast<uint8_t>(UserDataFormatVersion::text), buffer); 705 706 if (size() + ud->header().size <= _maxPELSize) 707 { 708 _optionalSections.push_back(std::move(ud)); 709 } 710 else 711 { 712 // Don't attempt to shrink here since we'd be dropping the 713 // most recent journal entries which would be confusing. 714 lg2::warning( 715 "Journal UserData section does not fit in PEL, dropping. " 716 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}", 717 "PEL_SIZE", size(), "DATA_SIZE", buffer.size()); 718 ud.reset(); 719 continue; 720 } 721 } 722 } 723 724 namespace util 725 { 726 727 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 728 { 729 auto jsonString = json.dump(); 730 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 731 732 // Pad to a 4 byte boundary 733 while ((jsonData.size() % 4) != 0) 734 { 735 jsonData.push_back(0); 736 } 737 738 return std::make_unique<UserData>( 739 static_cast<uint16_t>(ComponentID::phosphorLogging), 740 static_cast<uint8_t>(UserDataFormat::json), 741 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 742 } 743 744 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 745 { 746 assert(!ad.empty()); 747 nlohmann::json json; 748 749 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 750 if (ad.getValue("ESEL")) 751 { 752 auto newAD = ad; 753 newAD.remove("ESEL"); 754 json = newAD.toJSON(); 755 } 756 else 757 { 758 json = ad.toJSON(); 759 } 760 761 return makeJSONUserDataSection(json); 762 } 763 764 void addProcessNameToJSON(nlohmann::json& json, 765 const std::optional<std::string>& pid, 766 const DataInterfaceBase& dataIface) 767 { 768 std::string name{unknownValue}; 769 770 try 771 { 772 if (pid) 773 { 774 auto n = dataIface.getProcessName(*pid); 775 if (n) 776 { 777 name = *n; 778 } 779 } 780 } 781 catch (const std::exception& e) 782 {} 783 784 if (pid) 785 { 786 json["Process Name"] = std::move(name); 787 } 788 } 789 790 void addBMCFWVersionIDToJSON(nlohmann::json& json, 791 const DataInterfaceBase& dataIface) 792 { 793 auto id = dataIface.getBMCFWVersionID(); 794 if (id.empty()) 795 { 796 id = unknownValue; 797 } 798 799 json["FW Version ID"] = std::move(id); 800 } 801 802 std::string lastSegment(char separator, std::string data) 803 { 804 auto pos = data.find_last_of(separator); 805 if (pos != std::string::npos) 806 { 807 data = data.substr(pos + 1); 808 } 809 810 return data; 811 } 812 813 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface) 814 { 815 auto keyword = dataIface.getSystemIMKeyword(); 816 817 std::string value{}; 818 819 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) { 820 value += std::format("{:02X}", byte); 821 }); 822 823 json["System IM"] = value; 824 } 825 826 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 827 { 828 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 829 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 830 json["HostState"] = lastSegment('.', dataIface.getHostState()); 831 json["BootState"] = lastSegment('.', dataIface.getBootState()); 832 } 833 834 void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface) 835 { 836 auto seconds = dataIface.getUptimeInSeconds(); 837 if (seconds) 838 { 839 json["BMCUptime"] = dataIface.getBMCUptime(*seconds); 840 } 841 else 842 { 843 json["BMCUptime"] = ""; 844 } 845 json["BMCLoad"] = dataIface.getBMCLoadAvg(); 846 } 847 848 std::unique_ptr<UserData> 849 makeSysInfoUserDataSection(const AdditionalData& ad, 850 const DataInterfaceBase& dataIface, 851 bool addUptime) 852 { 853 nlohmann::json json; 854 855 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 856 addBMCFWVersionIDToJSON(json, dataIface); 857 addIMKeyword(json, dataIface); 858 addStatesToJSON(json, dataIface); 859 860 if (addUptime) 861 { 862 addBMCUptime(json, dataIface); 863 } 864 865 return makeJSONUserDataSection(json); 866 } 867 868 std::vector<uint8_t> readFD(int fd) 869 { 870 std::vector<uint8_t> data; 871 872 // Get the size 873 struct stat s; 874 int r = fstat(fd, &s); 875 if (r != 0) 876 { 877 auto e = errno; 878 lg2::error("Could not get FFDC file size from FD, errno = {ERRNO}", 879 "ERRNO", e); 880 return data; 881 } 882 883 if (0 == s.st_size) 884 { 885 lg2::error("FFDC file is empty"); 886 return data; 887 } 888 889 data.resize(s.st_size); 890 891 // Make sure its at the beginning, as maybe another 892 // extension already used it. 893 r = lseek(fd, 0, SEEK_SET); 894 if (r == -1) 895 { 896 auto e = errno; 897 lg2::error("Could not seek to beginning of FFDC file, errno = {ERRNO}", 898 "ERRNO", e); 899 return data; 900 } 901 902 r = read(fd, data.data(), s.st_size); 903 if (r == -1) 904 { 905 auto e = errno; 906 lg2::error("Could not read FFDC file, errno = {ERRNO}", "ERRNO", e); 907 } 908 else if (r != s.st_size) 909 { 910 lg2::warning("Could not read full FFDC file. " 911 "File size = {FSIZE}, Size read = {SIZE_READ}", 912 "FSIZE", s.st_size, "SIZE_READ", r); 913 } 914 915 return data; 916 } 917 918 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID, 919 const PelFFDCfile& file) 920 { 921 auto data = readFD(file.fd); 922 923 if (data.empty()) 924 { 925 return std::unique_ptr<UserData>(); 926 } 927 928 // The data needs 4 Byte alignment, and save amount padded for the 929 // CBOR case. 930 uint32_t pad = 0; 931 while (data.size() % 4) 932 { 933 data.push_back(0); 934 pad++; 935 } 936 937 // For JSON, CBOR, and Text use our component ID, subType, and version, 938 // otherwise use the supplied ones. 939 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging); 940 uint8_t subType{}; 941 uint8_t version{}; 942 943 switch (file.format) 944 { 945 case UserDataFormat::json: 946 subType = static_cast<uint8_t>(UserDataFormat::json); 947 version = static_cast<uint8_t>(UserDataFormatVersion::json); 948 break; 949 case UserDataFormat::cbor: 950 subType = static_cast<uint8_t>(UserDataFormat::cbor); 951 version = static_cast<uint8_t>(UserDataFormatVersion::cbor); 952 953 // The CBOR parser will fail on the extra pad bytes since they 954 // aren't CBOR. Add the amount we padded to the end and other 955 // code will remove it all before parsing. 956 { 957 data.resize(data.size() + 4); 958 Stream stream{data}; 959 stream.offset(data.size() - 4); 960 stream << pad; 961 } 962 963 break; 964 case UserDataFormat::text: 965 subType = static_cast<uint8_t>(UserDataFormat::text); 966 version = static_cast<uint8_t>(UserDataFormatVersion::text); 967 break; 968 case UserDataFormat::custom: 969 default: 970 // Use the passed in values 971 compID = componentID; 972 subType = file.subType; 973 version = file.version; 974 break; 975 } 976 977 return std::make_unique<UserData>(compID, subType, version, data); 978 } 979 980 std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines) 981 { 982 std::vector<uint8_t> out; 983 984 for (const auto& line : lines) 985 { 986 out.insert(out.end(), line.begin(), line.end()); 987 988 if (out.back() != '\n') 989 { 990 out.push_back('\n'); 991 } 992 } 993 994 return out; 995 } 996 997 } // namespace util 998 999 } // namespace pels 1000 } // namespace openpower 1001