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