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