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 // Now check if it has any type of callout 662 if (pel->isHwCalloutPresent()) 663 { 664 log<level::INFO>( 665 "QuiesceOnHwError enabled, PEL severity not nonError or recovered, " 666 "and callout is present"); 667 668 _logManager.quiesceOnError(pel->obmcLogID()); 669 } 670 } 671 672 std::string Manager::getEventId(const openpower::pels::PEL& pel) const 673 { 674 std::string str; 675 auto src = pel.primarySRC(); 676 if (src) 677 { 678 const auto& hexwords = (*src)->hexwordData(); 679 680 std::string refcode = (*src)->asciiString(); 681 size_t pos = refcode.find_last_not_of(0x20); 682 if (pos != std::string::npos) 683 { 684 refcode.erase(pos + 1); 685 } 686 str = refcode; 687 688 for (auto& value : hexwords) 689 { 690 str += " "; 691 str += getNumberString("%08X", value); 692 } 693 } 694 return str; 695 } 696 697 void Manager::updateEventId(std::unique_ptr<openpower::pels::PEL>& pel) 698 { 699 std::string eventIdStr = getEventId(*pel); 700 701 auto entryN = _logManager.entries.find(pel->obmcLogID()); 702 if (entryN != _logManager.entries.end()) 703 { 704 entryN->second->eventId(eventIdStr); 705 } 706 } 707 708 std::string Manager::getResolution(const openpower::pels::PEL& pel) const 709 { 710 std::string str; 711 std::string resolution; 712 auto src = pel.primarySRC(); 713 if (src) 714 { 715 // First extract the callout pointer and then go through 716 const auto& callouts = (*src)->callouts(); 717 namespace pv = openpower::pels::pel_values; 718 // All PELs dont have callout, check before parsing callout data 719 if (callouts) 720 { 721 const auto& entries = callouts->callouts(); 722 // Entry starts with index 1 723 uint8_t index = 1; 724 for (auto& entry : entries) 725 { 726 resolution += std::to_string(index) + ". "; 727 // Adding Location code to resolution 728 if (!entry->locationCode().empty()) 729 resolution += 730 "Location Code: " + entry->locationCode() + ", "; 731 if (entry->fruIdentity()) 732 { 733 // Get priority and set the resolution string 734 str = pv::getValue(entry->priority(), 735 pel_values::calloutPriorityValues, 736 pel_values::registryNamePos); 737 str[0] = toupper(str[0]); 738 resolution += "Priority: " + str + ", "; 739 if (entry->fruIdentity()->getPN().has_value()) 740 { 741 resolution += 742 "PN: " + entry->fruIdentity()->getPN().value() + 743 ", "; 744 } 745 if (entry->fruIdentity()->getSN().has_value()) 746 { 747 resolution += 748 "SN: " + entry->fruIdentity()->getSN().value() + 749 ", "; 750 } 751 if (entry->fruIdentity()->getCCIN().has_value()) 752 { 753 resolution += 754 "CCIN: " + entry->fruIdentity()->getCCIN().value() + 755 ", "; 756 } 757 // Add the maintenance procedure 758 if (entry->fruIdentity()->getMaintProc().has_value()) 759 { 760 resolution += 761 "Procedure: " + 762 entry->fruIdentity()->getMaintProc().value() + ", "; 763 } 764 } 765 resolution.resize(resolution.size() - 2); 766 resolution += "\n"; 767 index++; 768 } 769 } 770 } 771 return resolution; 772 } 773 774 void Manager::updateResolution(std::unique_ptr<openpower::pels::PEL>& pel) 775 { 776 std::string callouts = getResolution(*pel); 777 auto entryN = _logManager.entries.find(pel->obmcLogID()); 778 if (entryN != _logManager.entries.end()) 779 { 780 entryN->second->resolution(callouts, true); 781 } 782 } 783 784 void Manager::setEntryPath(uint32_t obmcLogID) 785 { 786 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 787 if (auto attributes = _repo.getPELAttributes(id); attributes) 788 { 789 auto& attr = attributes.value().get(); 790 auto entry = _logManager.entries.find(obmcLogID); 791 if (entry != _logManager.entries.end()) 792 { 793 entry->second->path(attr.path, true); 794 } 795 } 796 } 797 798 void Manager::setServiceProviderNotifyFlag(uint32_t obmcLogID) 799 { 800 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 801 if (auto attributes = _repo.getPELAttributes(id); attributes) 802 { 803 auto& attr = attributes.value().get(); 804 auto entry = _logManager.entries.find(obmcLogID); 805 if (entry != _logManager.entries.end()) 806 { 807 entry->second->serviceProviderNotify( 808 attr.actionFlags.test(callHomeFlagBit), true); 809 } 810 } 811 } 812 813 void Manager::createPELEntry(uint32_t obmcLogID, bool skipIaSignal) 814 { 815 std::map<std::string, PropertiesVariant> varData; 816 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 817 if (auto attributes = _repo.getPELAttributes(id); attributes) 818 { 819 namespace pv = openpower::pels::pel_values; 820 auto& attr = attributes.value().get(); 821 822 // get the hidden flag values 823 auto sevType = static_cast<SeverityType>(attr.severity & 0xF0); 824 auto isHidden = true; 825 if (((sevType != SeverityType::nonError) && 826 attr.actionFlags.test(reportFlagBit) && 827 !attr.actionFlags.test(hiddenFlagBit)) || 828 ((sevType == SeverityType::nonError) && 829 attr.actionFlags.test(serviceActionFlagBit))) 830 { 831 isHidden = false; 832 } 833 varData.emplace(std::string("Hidden"), isHidden); 834 varData.emplace( 835 std::string("Subsystem"), 836 pv::getValue(attr.subsystem, pel_values::subsystemValues)); 837 838 varData.emplace( 839 std::string("ManagementSystemAck"), 840 (attr.hmcState == TransmissionState::acked ? true : false)); 841 842 // Path to create PELEntry Interface is same as PEL 843 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 844 // Create Interface for PELEntry and set properties 845 auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(), path, 846 varData, obmcLogID, &_repo); 847 if (!skipIaSignal) 848 { 849 pelEntry->emit_added(); 850 } 851 _pelEntries.emplace(std::move(path), std::move(pelEntry)); 852 } 853 } 854 855 uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId) 856 { 857 Repository::LogID id{Repository::LogID::Obmc(bmcLogId)}; 858 if (auto logId = _repo.getLogID(id); !logId.has_value()) 859 { 860 throw common_error::InvalidArgument(); 861 } 862 else 863 { 864 return logId->pelID.id; 865 } 866 } 867 868 uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId) 869 { 870 Repository::LogID id{Repository::LogID::Pel(pelId)}; 871 if (auto logId = _repo.getLogID(id); !logId.has_value()) 872 { 873 throw common_error::InvalidArgument(); 874 } 875 else 876 { 877 return logId->obmcID.id; 878 } 879 } 880 881 void Manager::updateProgressSRC( 882 std::unique_ptr<openpower::pels::PEL>& pel) const 883 { 884 // Check for pel severity of type - 0x51 = critical error, system 885 // termination 886 if (pel->userHeader().severity() == 0x51) 887 { 888 auto src = pel->primarySRC(); 889 if (src) 890 { 891 std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct(); 892 uint64_t srcRefCode = 0; 893 894 // Read bytes from offset [40-47] e.g. BD8D1001 895 for (int i = 0; i < 8; i++) 896 { 897 srcRefCode |= 898 (static_cast<uint64_t>(asciiSRC[40 + i]) << (8 * i)); 899 } 900 901 try 902 { 903 _dataIface->createProgressSRC(srcRefCode, asciiSRC); 904 } 905 catch (std::exception& e) 906 { 907 // Exception - may be no boot progress interface on dbus 908 } 909 } 910 } 911 } 912 913 void Manager::scheduleObmcLogDelete(uint32_t obmcLogID) 914 { 915 _obmcLogDeleteEventSource = std::make_unique<sdeventplus::source::Defer>( 916 _event, std::bind(std::mem_fn(&Manager::deleteObmcLog), this, 917 std::placeholders::_1, obmcLogID)); 918 } 919 920 void Manager::deleteObmcLog(sdeventplus::source::EventBase&, uint32_t obmcLogID) 921 { 922 log<level::INFO>( 923 fmt::format("Removing event log with no PEL: {}", obmcLogID).c_str()); 924 _logManager.erase(obmcLogID); 925 _obmcLogDeleteEventSource.reset(); 926 } 927 928 } // namespace pels 929 } // namespace openpower 930