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