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