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 "manager.hpp" 17 18 #include "additional_data.hpp" 19 #include "elog_serialize.hpp" 20 #include "json_utils.hpp" 21 #include "pel.hpp" 22 #include "pel_entry.hpp" 23 #include "pel_values.hpp" 24 #include "service_indicators.hpp" 25 #include "severity.hpp" 26 27 #include <sys/inotify.h> 28 #include <unistd.h> 29 30 #include <phosphor-logging/lg2.hpp> 31 #include <xyz/openbmc_project/Common/error.hpp> 32 #include <xyz/openbmc_project/Logging/Create/server.hpp> 33 34 #include <filesystem> 35 #include <format> 36 #include <fstream> 37 38 namespace openpower 39 { 40 namespace pels 41 { 42 43 using namespace phosphor::logging; 44 namespace fs = std::filesystem; 45 namespace rg = openpower::pels::message; 46 47 namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error; 48 49 using Create = sdbusplus::server::xyz::openbmc_project::logging::Create; 50 51 namespace additional_data 52 { 53 constexpr auto rawPEL = "RAWPEL"; 54 constexpr auto esel = "ESEL"; 55 constexpr auto error = "ERROR_NAME"; 56 } // namespace additional_data 57 58 constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default"; 59 constexpr uint32_t bmcThermalCompID = 0x2700; 60 constexpr uint32_t bmcFansCompID = 0x2800; 61 62 Manager::~Manager() 63 { 64 if (_pelFileDeleteFD != -1) 65 { 66 if (_pelFileDeleteWatchFD != -1) 67 { 68 inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD); 69 } 70 close(_pelFileDeleteFD); 71 } 72 } 73 74 void Manager::create(const std::string& message, uint32_t obmcLogID, 75 uint64_t timestamp, Entry::Level severity, 76 const std::map<std::string, std::string>& additionalData, 77 const std::vector<std::string>& associations, 78 const FFDCEntries& ffdc) 79 { 80 AdditionalData ad{additionalData}; 81 82 // If a PEL was passed in via a filename or in an ESEL, 83 // use that. Otherwise, create one. 84 auto rawPelPath = ad.getValue(additional_data::rawPEL); 85 if (rawPelPath) 86 { 87 addRawPEL(*rawPelPath, obmcLogID); 88 } 89 else 90 { 91 auto esel = ad.getValue(additional_data::esel); 92 if (esel) 93 { 94 addESELPEL(*esel, obmcLogID); 95 } 96 else 97 { 98 createPEL(message, obmcLogID, timestamp, severity, additionalData, 99 associations, ffdc); 100 } 101 } 102 103 setEntryPath(obmcLogID); 104 setServiceProviderNotifyFlag(obmcLogID); 105 } 106 107 void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID) 108 { 109 if (fs::exists(rawPelPath)) 110 { 111 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary); 112 113 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file), 114 std::istreambuf_iterator<char>()); 115 if (file.fail()) 116 { 117 lg2::error( 118 "Filesystem error reading a raw PEL. File = {FILE}, obmcLogID = {LOGID}", 119 "FILE", rawPelPath, "LOGID", obmcLogID); 120 // TODO, Decide what to do here. Maybe nothing. 121 return; 122 } 123 124 file.close(); 125 126 addPEL(data, obmcLogID); 127 128 std::error_code ec; 129 fs::remove(rawPelPath, ec); 130 } 131 else 132 { 133 lg2::error( 134 "Raw PEL file from BMC event log does not exit. File = {FILE}, obmcLogID = {LOGID}", 135 "FILE", rawPelPath, "LOGID", obmcLogID); 136 } 137 } 138 139 void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID) 140 { 141 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID); 142 if (pel->valid()) 143 { 144 // PELs created by others still need this field set by us. 145 pel->setCommitTime(); 146 147 // Assign Id other than to Hostbot PEL 148 if ((pel->privateHeader()).creatorID() != 149 static_cast<uint8_t>(CreatorID::hostboot)) 150 { 151 pel->assignID(); 152 } 153 else 154 { 155 const Repository::LogID id{Repository::LogID::Pel(pel->id())}; 156 auto result = _repo.hasPEL(id); 157 if (result) 158 { 159 lg2::warning( 160 "Duplicate HostBoot PEL ID {ID} found, moving it to archive folder", 161 "ID", lg2::hex, pel->id()); 162 163 _repo.archivePEL(*pel); 164 165 // No need to keep around the openBMC event log entry 166 scheduleObmcLogDelete(obmcLogID); 167 return; 168 } 169 } 170 171 // Update System Info to Extended User Data 172 pel->updateSysInfoInExtendedUserDataSection(*_dataIface); 173 174 // Check for severity 0x51 and update boot progress SRC 175 updateProgressSRC(pel); 176 177 try 178 { 179 lg2::debug("Adding external PEL {ID} (BMC ID {BMCID}) to repo", 180 "ID", lg2::hex, pel->id(), "BMCID", obmcLogID); 181 _repo.add(pel); 182 183 if (_repo.sizeWarning()) 184 { 185 scheduleRepoPrune(); 186 } 187 188 // Activate any resulting service indicators if necessary 189 auto policy = service_indicators::getPolicy(*_dataIface); 190 policy->activate(*pel); 191 } 192 catch (const std::exception& e) 193 { 194 // Probably a full or r/o filesystem, not much we can do. 195 lg2::error("Unable to add PEL {ID} to Repository", "ID", lg2::hex, 196 pel->id()); 197 } 198 199 updateEventId(pel); 200 updateResolution(*pel); 201 serializeLogEntry(obmcLogID); 202 createPELEntry(obmcLogID); 203 204 // Check if firmware should quiesce system due to error 205 checkPelAndQuiesce(pel); 206 } 207 else 208 { 209 lg2::error("Invalid PEL received from the host. BMC ID = {ID}", "ID", 210 obmcLogID); 211 212 AdditionalData ad; 213 ad.add("PLID", getNumberString("0x%08X", pel->plid())); 214 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID)); 215 ad.add("PEL_SIZE", std::to_string(pelData.size())); 216 217 std::string asciiString; 218 auto src = pel->primarySRC(); 219 if (src) 220 { 221 asciiString = (*src)->asciiString(); 222 } 223 224 ad.add("SRC", asciiString); 225 226 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL", 227 Entry::Level::Error, ad); 228 229 // Save it to a file for debug in the lab. Just keep the latest. 230 // Not adding it to the PEL because it could already be max size 231 // and don't want to truncate an already invalid PEL. 232 std::ofstream pelFile{getPELRepoPath() / "badPEL"}; 233 pelFile.write(reinterpret_cast<const char*>(pelData.data()), 234 pelData.size()); 235 236 // No need to keep around the openBMC event log entry 237 scheduleObmcLogDelete(obmcLogID); 238 } 239 } 240 241 void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID) 242 { 243 std::vector<uint8_t> data; 244 245 lg2::debug("Adding PEL from ESEL. BMC ID = {ID}", "ID", obmcLogID); 246 247 try 248 { 249 data = eselToRawData(esel); 250 } 251 catch (const std::exception& e) 252 { 253 // Try to add it below anyway, so it follows the usual bad data path. 254 lg2::error("Problems converting ESEL string to a byte vector"); 255 } 256 257 addPEL(data, obmcLogID); 258 } 259 260 std::vector<uint8_t> Manager::eselToRawData(const std::string& esel) 261 { 262 std::vector<uint8_t> data; 263 std::string byteString; 264 265 // As the eSEL string looks like: "50 48 00 ab ..." there are 3 266 // characters per raw byte, and since the actual PEL data starts 267 // at the 16th byte, the code will grab the PEL data starting at 268 // offset 48 in the string. 269 static constexpr size_t pelStart = 16 * 3; 270 271 if (esel.size() <= pelStart) 272 { 273 lg2::error("ESEL data too short, length = {LEN}", "LEN", esel.size()); 274 throw std::length_error("ESEL data too short"); 275 } 276 277 for (size_t i = pelStart; i < esel.size(); i += 3) 278 { 279 if (i + 1 < esel.size()) 280 { 281 byteString = esel.substr(i, 2); 282 data.push_back(std::stoi(byteString, nullptr, 16)); 283 } 284 else 285 { 286 lg2::error("ESEL data too short, length = {LEN}", "LEN", 287 esel.size()); 288 throw std::length_error("ESEL data too short"); 289 } 290 } 291 292 return data; 293 } 294 295 void Manager::erase(uint32_t obmcLogID) 296 { 297 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 298 299 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 300 _pelEntries.erase(path); 301 _repo.remove(id); 302 } 303 304 void Manager::getLogIDWithHwIsolation(std::vector<uint32_t>& idsWithHwIsoEntry) 305 { 306 idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation(); 307 } 308 309 bool Manager::isDeleteProhibited(uint32_t obmcLogID) 310 { 311 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 312 if (!_repo.hasPEL(id)) 313 { 314 return false; 315 } 316 auto entryPath{std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID)}; 317 auto entry = _pelEntries.find(entryPath); 318 if (entry != _pelEntries.end()) 319 { 320 if (entry->second->guard()) 321 { 322 auto hwIsolationAssocPaths = _dataIface->getAssociatedPaths( 323 entryPath += "/isolated_hw_entry", "/", 0, 324 {"xyz.openbmc_project.HardwareIsolation.Entry"}); 325 if (!hwIsolationAssocPaths.empty()) 326 { 327 return true; 328 } 329 } 330 } 331 return false; 332 } 333 334 PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc) 335 { 336 PelFFDC pelFFDC; 337 338 std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) { 339 PelFFDCfile pf; 340 pf.subType = std::get<ffdcSubtypePos>(f); 341 pf.version = std::get<ffdcVersionPos>(f); 342 pf.fd = std::get<ffdcFDPos>(f); 343 344 switch (std::get<ffdcFormatPos>(f)) 345 { 346 case Create::FFDCFormat::JSON: 347 pf.format = UserDataFormat::json; 348 break; 349 case Create::FFDCFormat::CBOR: 350 pf.format = UserDataFormat::cbor; 351 break; 352 case Create::FFDCFormat::Text: 353 pf.format = UserDataFormat::text; 354 break; 355 case Create::FFDCFormat::Custom: 356 pf.format = UserDataFormat::custom; 357 break; 358 } 359 360 pelFFDC.push_back(pf); 361 }); 362 363 return pelFFDC; 364 } 365 366 void Manager::createPEL( 367 const std::string& message, uint32_t obmcLogID, uint64_t timestamp, 368 phosphor::logging::Entry::Level severity, 369 const std::map<std::string, std::string>& additionalData, 370 const std::vector<std::string>& /*associations*/, const FFDCEntries& ffdc) 371 { 372 auto start = std::chrono::steady_clock::now(); 373 auto entry = _registry.lookup(message, rg::LookupType::name); 374 auto pelFFDC = convertToPelFFDC(ffdc); 375 AdditionalData ad{additionalData}; 376 std::string msg; 377 378 if (!entry) 379 { 380 // Instead, get the default entry that means there is no 381 // other matching entry. This error will still use the 382 // AdditionalData values of the original error, and this 383 // code will add the error message value that wasn't found 384 // to this AD. This way, there will at least be a PEL, 385 // possibly with callouts, to allow users to debug the 386 // issue that caused the error even without its own PEL. 387 lg2::error("Event not found in PEL message registry: {MSG}", "MSG", 388 message); 389 390 entry = _registry.lookup(defaultLogMessage, rg::LookupType::name); 391 if (!entry) 392 { 393 lg2::error("Default event not found in PEL message registry"); 394 return; 395 } 396 397 ad.add(additional_data::error, message); 398 } 399 400 auto pel = std::make_unique<openpower::pels::PEL>( 401 *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface, 402 *_journal); 403 404 _repo.add(pel); 405 406 if (_repo.sizeWarning()) 407 { 408 scheduleRepoPrune(); 409 } 410 411 // Check for severity 0x51 and update boot progress SRC 412 updateProgressSRC(pel); 413 414 // Activate any resulting service indicators if necessary 415 auto policy = service_indicators::getPolicy(*_dataIface); 416 policy->activate(*pel); 417 418 updateDBusSeverity(*pel); 419 updateEventId(pel); 420 updateResolution(*pel); 421 serializeLogEntry(obmcLogID); 422 createPELEntry(obmcLogID); 423 424 auto src = pel->primarySRC(); 425 if (src) 426 { 427 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( 428 std::chrono::steady_clock::now() - start); 429 430 auto asciiString = (*src)->asciiString(); 431 while (asciiString.back() == ' ') 432 { 433 asciiString.pop_back(); 434 } 435 lg2::info("Created PEL {ID} (BMC ID {BMCID}) with SRC {SRC}", "ID", 436 lg2::hex, pel->id(), "BMCID", pel->obmcLogID(), "SRC", 437 asciiString, "PEL_CREATE_DURATION", duration.count()); 438 } 439 440 // Check if firmware should quiesce system due to error 441 checkPelAndQuiesce(pel); 442 } 443 444 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID) 445 { 446 Repository::LogID id{Repository::LogID::Pel(pelID)}; 447 std::optional<int> fd; 448 449 lg2::debug("getPEL {ID}", "ID", lg2::hex, pelID); 450 451 try 452 { 453 fd = _repo.getPELFD(id); 454 } 455 catch (const std::exception& e) 456 { 457 throw common_error::InternalFailure(); 458 } 459 460 if (!fd) 461 { 462 throw common_error::InvalidArgument(); 463 } 464 465 scheduleFDClose(*fd); 466 467 return *fd; 468 } 469 470 void Manager::scheduleFDClose(int fd) 471 { 472 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>( 473 _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd, 474 std::placeholders::_1)); 475 } 476 477 void Manager::closeFD(int fd, sdeventplus::source::EventBase& /*source*/) 478 { 479 close(fd); 480 _fdCloserEventSource.reset(); 481 } 482 483 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID) 484 { 485 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 486 std::optional<std::vector<uint8_t>> data; 487 488 lg2::debug("getPELFromOBMCID {BMCID}", "BMCID", obmcLogID); 489 490 try 491 { 492 data = _repo.getPELData(id); 493 } 494 catch (const std::exception& e) 495 { 496 throw common_error::InternalFailure(); 497 } 498 499 if (!data) 500 { 501 throw common_error::InvalidArgument(); 502 } 503 504 return *data; 505 } 506 507 void Manager::hostAck(uint32_t pelID) 508 { 509 Repository::LogID id{Repository::LogID::Pel(pelID)}; 510 511 lg2::debug("HostHack {ID}", "ID", lg2::hex, pelID); 512 513 if (!_repo.hasPEL(id)) 514 { 515 throw common_error::InvalidArgument(); 516 } 517 518 if (_hostNotifier) 519 { 520 _hostNotifier->ackPEL(pelID); 521 } 522 } 523 524 void Manager::hostReject(uint32_t pelID, RejectionReason reason) 525 { 526 Repository::LogID id{Repository::LogID::Pel(pelID)}; 527 528 lg2::debug("HostReject {ID}, reason = {REASON}", "ID", lg2::hex, pelID, 529 "REASON", reason); 530 531 if (!_repo.hasPEL(id)) 532 { 533 throw common_error::InvalidArgument(); 534 } 535 536 if (reason == RejectionReason::BadPEL) 537 { 538 AdditionalData data; 539 data.add("BAD_ID", getNumberString("0x%08X", pelID)); 540 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost", 541 Entry::Level::Informational, data); 542 if (_hostNotifier) 543 { 544 _hostNotifier->setBadPEL(pelID); 545 } 546 } 547 else if ((reason == RejectionReason::HostFull) && _hostNotifier) 548 { 549 _hostNotifier->setHostFull(pelID); 550 } 551 } 552 553 void Manager::scheduleRepoPrune() 554 { 555 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>( 556 _event, std::bind(std::mem_fn(&Manager::pruneRepo), this, 557 std::placeholders::_1)); 558 } 559 560 void Manager::pruneRepo(sdeventplus::source::EventBase& /*source*/) 561 { 562 auto idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation(); 563 564 auto idsToDelete = _repo.prune(idsWithHwIsoEntry); 565 566 // Remove the OpenBMC event logs for the PELs that were just removed. 567 std::for_each(idsToDelete.begin(), idsToDelete.end(), 568 [this](auto id) { this->_logManager.erase(id); }); 569 570 _repoPrunerEventSource.reset(); 571 } 572 573 void Manager::setupPELDeleteWatch() 574 { 575 _pelFileDeleteFD = inotify_init1(IN_NONBLOCK); 576 if (-1 == _pelFileDeleteFD) 577 { 578 auto e = errno; 579 lg2::error("inotify_init1 failed with errno {ERRNO}", "ERRNO", e); 580 abort(); 581 } 582 583 _pelFileDeleteWatchFD = inotify_add_watch( 584 _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE); 585 if (-1 == _pelFileDeleteWatchFD) 586 { 587 auto e = errno; 588 lg2::error("inotify_add_watch failed with errno {ERRNO}", "ERRNO", e); 589 abort(); 590 } 591 592 _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>( 593 _event, _pelFileDeleteFD, EPOLLIN, 594 std::bind(std::mem_fn(&Manager::pelFileDeleted), this, 595 std::placeholders::_1, std::placeholders::_2, 596 std::placeholders::_3)); 597 } 598 599 void Manager::pelFileDeleted(sdeventplus::source::IO& /*io*/, int /*fd*/, 600 uint32_t revents) 601 { 602 if (!(revents & EPOLLIN)) 603 { 604 return; 605 } 606 607 // An event for 1 PEL uses 48B. When all PELs are deleted at once, 608 // as many events as there is room for can be handled in one callback. 609 // A size of 2000 will allow 41 to be processed, with additional 610 // callbacks being needed to process the remaining ones. 611 std::array<uint8_t, 2000> data{}; 612 auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size()); 613 if (bytesRead < 0) 614 { 615 auto e = errno; 616 lg2::error("Failed reading data from inotify event, errno = {ERRNO}", 617 "ERRNO", e); 618 abort(); 619 } 620 621 auto offset = 0; 622 while (offset < bytesRead) 623 { 624 auto event = reinterpret_cast<inotify_event*>(&data[offset]); 625 if (event->mask & IN_DELETE) 626 { 627 std::string filename{event->name}; 628 629 // Get the PEL ID from the filename and tell the 630 // repo it's been removed, and then delete the BMC 631 // event log if it's there. 632 auto pos = filename.find_first_of('_'); 633 if (pos != std::string::npos) 634 { 635 try 636 { 637 auto idString = filename.substr(pos + 1); 638 auto pelID = std::stoul(idString, nullptr, 16); 639 640 Repository::LogID id{Repository::LogID::Pel(pelID)}; 641 auto removedLogID = _repo.remove(id); 642 if (removedLogID) 643 { 644 _logManager.erase(removedLogID->obmcID.id); 645 } 646 } 647 catch (const std::exception& e) 648 { 649 lg2::info("Could not find PEL ID from its filename {NAME}", 650 "NAME", filename); 651 } 652 } 653 } 654 655 offset += offsetof(inotify_event, name) + event->len; 656 } 657 } 658 659 std::tuple<uint32_t, uint32_t> Manager::createPELWithFFDCFiles( 660 std::string message, Entry::Level severity, 661 std::map<std::string, std::string> additionalData, 662 std::vector<std::tuple< 663 sdbusplus::server::xyz::openbmc_project::logging::Create::FFDCFormat, 664 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 665 fFDC) 666 { 667 _logManager.create(message, severity, additionalData, fFDC); 668 669 return {_logManager.lastEntryID(), _repo.lastPelID()}; 670 } 671 672 std::string Manager::getPELJSON(uint32_t obmcLogID) 673 { 674 // Throws InvalidArgument if not found 675 auto pelID = getPELIdFromBMCLogId(obmcLogID); 676 677 auto cmd = std::format("/usr/bin/peltool -i {:#x}", pelID); 678 679 FILE* pipe = popen(cmd.c_str(), "r"); 680 if (!pipe) 681 { 682 lg2::error("Error running cmd: {CMD}", "CMD", cmd); 683 throw common_error::InternalFailure(); 684 } 685 686 std::string output; 687 std::array<char, 1024> buffer; 688 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) 689 { 690 output.append(buffer.data()); 691 } 692 693 int rc = pclose(pipe); 694 if (WEXITSTATUS(rc) != 0) 695 { 696 lg2::error("Error running cmd: {CMD}, rc = {RC}", "CMD", cmd, "RC", rc); 697 throw common_error::InternalFailure(); 698 } 699 700 return output; 701 } 702 703 void Manager::checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel) 704 { 705 if ((pel->userHeader().severity() == 706 static_cast<uint8_t>(SeverityType::nonError)) || 707 (pel->userHeader().severity() == 708 static_cast<uint8_t>(SeverityType::recovered))) 709 { 710 lg2::debug( 711 "PEL severity informational or recovered. no quiesce needed"); 712 return; 713 } 714 if (!_logManager.isQuiesceOnErrorEnabled()) 715 { 716 lg2::debug("QuiesceOnHwError not enabled, no quiesce needed"); 717 return; 718 } 719 720 CreatorID creatorID{pel->privateHeader().creatorID()}; 721 722 if ((creatorID != CreatorID::openBMC) && 723 (creatorID != CreatorID::hostboot) && 724 (creatorID != CreatorID::ioDrawer) && (creatorID != CreatorID::occ) && 725 (creatorID != CreatorID::phyp)) 726 { 727 return; 728 } 729 730 // Now check if it has any type of callout 731 if (pel->isHwCalloutPresent()) 732 { 733 lg2::info( 734 "QuiesceOnHwError enabled, PEL severity not nonError or recovered, " 735 "and callout is present"); 736 737 _logManager.quiesceOnError(pel->obmcLogID()); 738 } 739 } 740 741 std::string Manager::getEventId(const openpower::pels::PEL& pel) const 742 { 743 std::string str; 744 auto src = pel.primarySRC(); 745 if (src) 746 { 747 const auto& hexwords = (*src)->hexwordData(); 748 749 std::string refcode = (*src)->asciiString(); 750 size_t pos = refcode.find_last_not_of(0x20); 751 if (pos != std::string::npos) 752 { 753 refcode.erase(pos + 1); 754 } 755 str = refcode; 756 757 for (auto& value : hexwords) 758 { 759 str += " "; 760 str += getNumberString("%08X", value); 761 } 762 } 763 return sanitizeFieldForDBus(str); 764 } 765 766 void Manager::updateEventId(std::unique_ptr<openpower::pels::PEL>& pel) 767 { 768 std::string eventIdStr = getEventId(*pel); 769 770 auto entryN = _logManager.entries.find(pel->obmcLogID()); 771 if (entryN != _logManager.entries.end()) 772 { 773 entryN->second->eventId(eventIdStr, true); 774 } 775 } 776 777 std::string Manager::sanitizeFieldForDBus(std::string field) 778 { 779 std::for_each(field.begin(), field.end(), [](char& ch) { 780 if (((ch < ' ') || (ch > '~')) && (ch != '\n') && (ch != '\t')) 781 { 782 ch = ' '; 783 } 784 }); 785 return field; 786 } 787 788 std::string Manager::getResolution(const openpower::pels::PEL& pel) const 789 { 790 std::string str; 791 std::string resolution; 792 auto src = pel.primarySRC(); 793 if (src) 794 { 795 // First extract the callout pointer and then go through 796 const auto& callouts = (*src)->callouts(); 797 namespace pv = openpower::pels::pel_values; 798 // All PELs dont have callout, check before parsing callout data 799 if (callouts) 800 { 801 const auto& entries = callouts->callouts(); 802 // Entry starts with index 1 803 uint8_t index = 1; 804 for (auto& entry : entries) 805 { 806 resolution += std::to_string(index) + ". "; 807 // Adding Location code to resolution 808 if (!entry->locationCode().empty()) 809 resolution += "Location Code: " + entry->locationCode() + 810 ", "; 811 if (entry->fruIdentity()) 812 { 813 // Get priority and set the resolution string 814 str = pv::getValue(entry->priority(), 815 pel_values::calloutPriorityValues, 816 pel_values::registryNamePos); 817 str[0] = toupper(str[0]); 818 resolution += "Priority: " + str + ", "; 819 if (entry->fruIdentity()->getPN().has_value()) 820 { 821 resolution += 822 "PN: " + entry->fruIdentity()->getPN().value() + 823 ", "; 824 } 825 if (entry->fruIdentity()->getSN().has_value()) 826 { 827 resolution += 828 "SN: " + entry->fruIdentity()->getSN().value() + 829 ", "; 830 } 831 if (entry->fruIdentity()->getCCIN().has_value()) 832 { 833 resolution += 834 "CCIN: " + entry->fruIdentity()->getCCIN().value() + 835 ", "; 836 } 837 // Add the maintenance procedure 838 if (entry->fruIdentity()->getMaintProc().has_value()) 839 { 840 resolution += 841 "Procedure: " + 842 entry->fruIdentity()->getMaintProc().value() + ", "; 843 } 844 } 845 resolution.resize(resolution.size() - 2); 846 resolution += "\n"; 847 index++; 848 } 849 } 850 } 851 return sanitizeFieldForDBus(resolution); 852 } 853 854 bool Manager::updateResolution(const openpower::pels::PEL& pel) 855 { 856 std::string callouts = getResolution(pel); 857 auto entryN = _logManager.entries.find(pel.obmcLogID()); 858 if (entryN != _logManager.entries.end()) 859 { 860 entryN->second->resolution(callouts, true); 861 } 862 863 return false; 864 } 865 866 void Manager::serializeLogEntry(uint32_t obmcLogID) 867 { 868 auto entryN = _logManager.entries.find(obmcLogID); 869 if (entryN != _logManager.entries.end()) 870 { 871 serialize(*entryN->second); 872 } 873 } 874 875 void Manager::updateDBusSeverity(const openpower::pels::PEL& pel) 876 { 877 // The final severity of the PEL may not agree with the 878 // original severity of the D-Bus event log. Update the 879 // D-Bus property to match in some cases. This is to 880 // ensure there isn't a Critical or Warning Redfish event 881 // log for an informational or recovered PEL (or vice versa). 882 // This doesn't make an explicit call to serialize the new 883 // event log property value because updateEventId() is called 884 // right after this and will do it. 885 auto sevType = 886 static_cast<SeverityType>(pel.userHeader().severity() & 0xF0); 887 888 auto entryN = _logManager.entries.find(pel.obmcLogID()); 889 if (entryN != _logManager.entries.end()) 890 { 891 auto newSeverity = 892 fixupLogSeverity(entryN->second->severity(), sevType); 893 if (newSeverity) 894 { 895 lg2::info("Changing event log {ID} severity from {OLD} " 896 "to {NEW} to match PEL", 897 "ID", lg2::hex, entryN->second->id(), "OLD", 898 Entry::convertLevelToString(entryN->second->severity()), 899 "NEW", Entry::convertLevelToString(*newSeverity)); 900 901 entryN->second->severity(*newSeverity, true); 902 } 903 } 904 } 905 906 void Manager::setEntryPath(uint32_t obmcLogID) 907 { 908 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 909 if (auto attributes = _repo.getPELAttributes(id); attributes) 910 { 911 auto& attr = attributes.value().get(); 912 auto entry = _logManager.entries.find(obmcLogID); 913 if (entry != _logManager.entries.end()) 914 { 915 entry->second->path(attr.path, true); 916 } 917 } 918 } 919 920 void Manager::setServiceProviderNotifyFlag(uint32_t obmcLogID) 921 { 922 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 923 if (auto attributes = _repo.getPELAttributes(id); attributes) 924 { 925 auto& attr = attributes.value().get(); 926 auto entry = _logManager.entries.find(obmcLogID); 927 if (entry != _logManager.entries.end()) 928 { 929 if (attr.actionFlags.test(callHomeFlagBit)) 930 { 931 entry->second->serviceProviderNotify(Entry::Notify::Notify, 932 true); 933 } 934 else 935 { 936 entry->second->serviceProviderNotify(Entry::Notify::Inhibit, 937 true); 938 } 939 } 940 } 941 } 942 943 void Manager::createPELEntry(uint32_t obmcLogID, bool skipIaSignal) 944 { 945 std::map<std::string, PropertiesVariant> varData; 946 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 947 if (auto attributes = _repo.getPELAttributes(id); attributes) 948 { 949 namespace pv = openpower::pels::pel_values; 950 auto& attr = attributes.value().get(); 951 952 // get the hidden flag values 953 auto sevType = static_cast<SeverityType>(attr.severity & 0xF0); 954 auto isHidden = true; 955 if (((sevType != SeverityType::nonError) && 956 attr.actionFlags.test(reportFlagBit) && 957 !attr.actionFlags.test(hiddenFlagBit)) || 958 ((sevType == SeverityType::nonError) && 959 attr.actionFlags.test(serviceActionFlagBit))) 960 { 961 isHidden = false; 962 } 963 varData.emplace(std::string("Hidden"), isHidden); 964 varData.emplace( 965 std::string("Subsystem"), 966 pv::getValue(attr.subsystem, pel_values::subsystemValues)); 967 968 varData.emplace( 969 std::string("ManagementSystemAck"), 970 (attr.hmcState == TransmissionState::acked ? true : false)); 971 972 varData.emplace("PlatformLogID", attr.plid); 973 varData.emplace("Deconfig", attr.deconfig); 974 varData.emplace("Guard", attr.guard); 975 varData.emplace("Timestamp", attr.creationTime); 976 977 // Path to create PELEntry Interface is same as PEL 978 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 979 // Create Interface for PELEntry and set properties 980 auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(), path, 981 varData, obmcLogID, &_repo); 982 if (!skipIaSignal) 983 { 984 pelEntry->emit_added(); 985 } 986 _pelEntries.emplace(std::move(path), std::move(pelEntry)); 987 } 988 } 989 990 uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId) 991 { 992 Repository::LogID id{Repository::LogID::Obmc(bmcLogId)}; 993 if (auto logId = _repo.getLogID(id); !logId.has_value()) 994 { 995 throw common_error::InvalidArgument(); 996 } 997 else 998 { 999 return logId->pelID.id; 1000 } 1001 } 1002 1003 uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId) 1004 { 1005 Repository::LogID id{Repository::LogID::Pel(pelId)}; 1006 if (auto logId = _repo.getLogID(id); !logId.has_value()) 1007 { 1008 throw common_error::InvalidArgument(); 1009 } 1010 else 1011 { 1012 return logId->obmcID.id; 1013 } 1014 } 1015 1016 void Manager::updateProgressSRC( 1017 std::unique_ptr<openpower::pels::PEL>& pel) const 1018 { 1019 const size_t refcodeBegin = 40; 1020 const size_t refcodeSize = 8; 1021 1022 // Check for pel severity of type - 0x51 = critical error, system 1023 // termination 1024 if (pel->userHeader().severity() == 0x51) 1025 { 1026 auto src = pel->primarySRC(); 1027 if (src) 1028 { 1029 std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct(); 1030 1031 if (asciiSRC.size() < (refcodeBegin + refcodeSize)) 1032 { 1033 lg2::error( 1034 "SRC struct is too short to get progress code ({SIZE})", 1035 "SIZE", asciiSRC.size()); 1036 return; 1037 } 1038 1039 // Pull the ASCII SRC from offset [40-47] e.g. BD8D1001 1040 std::vector<uint8_t> srcRefCode( 1041 asciiSRC.begin() + refcodeBegin, 1042 asciiSRC.begin() + refcodeBegin + refcodeSize); 1043 1044 try 1045 { 1046 _dataIface->createProgressSRC(srcRefCode, asciiSRC); 1047 } 1048 catch (const std::exception&) 1049 { 1050 // Exception - may be no boot progress interface on dbus 1051 } 1052 } 1053 } 1054 } 1055 1056 void Manager::scheduleObmcLogDelete(uint32_t obmcLogID) 1057 { 1058 _obmcLogDeleteEventSource = std::make_unique<sdeventplus::source::Defer>( 1059 _event, std::bind(std::mem_fn(&Manager::deleteObmcLog), this, 1060 std::placeholders::_1, obmcLogID)); 1061 } 1062 1063 void Manager::deleteObmcLog(sdeventplus::source::EventBase&, uint32_t obmcLogID) 1064 { 1065 lg2::info("Removing event log with no PEL: {BMCID}", "BMCID", obmcLogID); 1066 _logManager.erase(obmcLogID); 1067 _obmcLogDeleteEventSource.reset(); 1068 } 1069 1070 bool Manager::clearPowerThermalDeconfigFlag(const std::string& locationCode, 1071 openpower::pels::PEL& pel) 1072 { 1073 // The requirements state that only power-thermal or 1074 // fan PELs need their deconfig flag cleared. 1075 static const std::vector<uint32_t> compIDs{bmcThermalCompID, bmcFansCompID}; 1076 1077 if (std::find(compIDs.begin(), compIDs.end(), 1078 pel.privateHeader().header().componentID) == compIDs.end()) 1079 { 1080 return false; 1081 } 1082 1083 auto src = pel.primarySRC(); 1084 const auto& callouts = (*src)->callouts(); 1085 if (!callouts) 1086 { 1087 return false; 1088 } 1089 1090 for (const auto& callout : callouts->callouts()) 1091 { 1092 // Look for the passed in location code in a callout that 1093 // is either a normal HW callout or a symbolic FRU with 1094 // a trusted location code callout. 1095 if ((callout->locationCode() != locationCode) || 1096 !callout->fruIdentity()) 1097 { 1098 continue; 1099 } 1100 1101 if ((callout->fruIdentity()->failingComponentType() != 1102 src::FRUIdentity::hardwareFRU) && 1103 (callout->fruIdentity()->failingComponentType() != 1104 src::FRUIdentity::symbolicFRUTrustedLocCode)) 1105 { 1106 continue; 1107 } 1108 1109 lg2::info( 1110 "Clearing deconfig flag in PEL {ID} with SRC {SRC} because {LOC} was replaced", 1111 "ID", lg2::hex, pel.id(), "SRC", (*src)->asciiString().substr(0, 8), 1112 "LOC", locationCode); 1113 (*src)->clearErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured); 1114 return true; 1115 } 1116 return false; 1117 } 1118 1119 void Manager::hardwarePresent(const std::string& locationCode) 1120 { 1121 Repository::PELUpdateFunc handlePowerThermalHardwarePresent = 1122 [locationCode](openpower::pels::PEL& pel) { 1123 return Manager::clearPowerThermalDeconfigFlag(locationCode, pel); 1124 }; 1125 1126 // If the PEL was created by the BMC and has the deconfig flag set, 1127 // it's a candidate to have the deconfig flag cleared. 1128 for (const auto& [id, attributes] : _repo.getAttributesMap()) 1129 { 1130 if ((attributes.creator == static_cast<uint8_t>(CreatorID::openBMC)) && 1131 attributes.deconfig) 1132 { 1133 auto updated = _repo.updatePEL(attributes.path, 1134 handlePowerThermalHardwarePresent); 1135 1136 if (updated) 1137 { 1138 // Also update the property on D-Bus 1139 auto objPath = std::string(OBJ_ENTRY) + '/' + 1140 std::to_string(id.obmcID.id); 1141 auto entryN = _pelEntries.find(objPath); 1142 if (entryN != _pelEntries.end()) 1143 { 1144 entryN->second->deconfig(false); 1145 } 1146 else 1147 { 1148 lg2::error( 1149 "Could not find PEL Entry D-Bus object for {PATH}", 1150 "PATH", objPath); 1151 } 1152 } 1153 } 1154 } 1155 } 1156 1157 } // namespace pels 1158 } // namespace openpower 1159