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