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 381 _repo.add(pel); 382 383 if (_repo.sizeWarning()) 384 { 385 scheduleRepoPrune(); 386 } 387 388 auto src = pel->primarySRC(); 389 if (src) 390 { 391 auto m = fmt::format("Created PEL {:#x} (BMC ID {}) with SRC {}", 392 pel->id(), pel->obmcLogID(), 393 (*src)->asciiString()); 394 while (m.back() == ' ') 395 { 396 m.pop_back(); 397 } 398 log<level::INFO>(m.c_str()); 399 } 400 401 // Check for severity 0x51 and update boot progress SRC 402 updateProgressSRC(pel); 403 404 // Activate any resulting service indicators if necessary 405 auto policy = service_indicators::getPolicy(*_dataIface); 406 policy->activate(*pel); 407 408 updateDBusSeverity(*pel); 409 updateEventId(pel); 410 updateResolution(*pel); 411 createPELEntry(obmcLogID); 412 413 // Check if firmware should quiesce system due to error 414 checkPelAndQuiesce(pel); 415 } 416 417 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID) 418 { 419 Repository::LogID id{Repository::LogID::Pel(pelID)}; 420 std::optional<int> fd; 421 422 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID)); 423 424 try 425 { 426 fd = _repo.getPELFD(id); 427 } 428 catch (const std::exception& e) 429 { 430 throw common_error::InternalFailure(); 431 } 432 433 if (!fd) 434 { 435 throw common_error::InvalidArgument(); 436 } 437 438 scheduleFDClose(*fd); 439 440 return *fd; 441 } 442 443 void Manager::scheduleFDClose(int fd) 444 { 445 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>( 446 _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd, 447 std::placeholders::_1)); 448 } 449 450 void Manager::closeFD(int fd, sdeventplus::source::EventBase& /*source*/) 451 { 452 close(fd); 453 _fdCloserEventSource.reset(); 454 } 455 456 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID) 457 { 458 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 459 std::optional<std::vector<uint8_t>> data; 460 461 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID)); 462 463 try 464 { 465 data = _repo.getPELData(id); 466 } 467 catch (const std::exception& e) 468 { 469 throw common_error::InternalFailure(); 470 } 471 472 if (!data) 473 { 474 throw common_error::InvalidArgument(); 475 } 476 477 return *data; 478 } 479 480 void Manager::hostAck(uint32_t pelID) 481 { 482 Repository::LogID id{Repository::LogID::Pel(pelID)}; 483 484 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID)); 485 486 if (!_repo.hasPEL(id)) 487 { 488 throw common_error::InvalidArgument(); 489 } 490 491 if (_hostNotifier) 492 { 493 _hostNotifier->ackPEL(pelID); 494 } 495 } 496 497 void Manager::hostReject(uint32_t pelID, RejectionReason reason) 498 { 499 Repository::LogID id{Repository::LogID::Pel(pelID)}; 500 501 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID), 502 entry("REASON=%d", static_cast<int>(reason))); 503 504 if (!_repo.hasPEL(id)) 505 { 506 throw common_error::InvalidArgument(); 507 } 508 509 if (reason == RejectionReason::BadPEL) 510 { 511 AdditionalData data; 512 data.add("BAD_ID", getNumberString("0x%08X", pelID)); 513 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost", 514 Entry::Level::Informational, data); 515 if (_hostNotifier) 516 { 517 _hostNotifier->setBadPEL(pelID); 518 } 519 } 520 else if ((reason == RejectionReason::HostFull) && _hostNotifier) 521 { 522 _hostNotifier->setHostFull(pelID); 523 } 524 } 525 526 void Manager::scheduleRepoPrune() 527 { 528 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>( 529 _event, std::bind(std::mem_fn(&Manager::pruneRepo), this, 530 std::placeholders::_1)); 531 } 532 533 void Manager::pruneRepo(sdeventplus::source::EventBase& /*source*/) 534 { 535 auto idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation(); 536 537 auto idsToDelete = _repo.prune(idsWithHwIsoEntry); 538 539 // Remove the OpenBMC event logs for the PELs that were just removed. 540 std::for_each(idsToDelete.begin(), idsToDelete.end(), 541 [this](auto id) { this->_logManager.erase(id); }); 542 543 _repoPrunerEventSource.reset(); 544 } 545 546 void Manager::setupPELDeleteWatch() 547 { 548 _pelFileDeleteFD = inotify_init1(IN_NONBLOCK); 549 if (-1 == _pelFileDeleteFD) 550 { 551 auto e = errno; 552 std::string msg = "inotify_init1 failed with errno " + 553 std::to_string(e); 554 log<level::ERR>(msg.c_str()); 555 abort(); 556 } 557 558 _pelFileDeleteWatchFD = inotify_add_watch( 559 _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE); 560 if (-1 == _pelFileDeleteWatchFD) 561 { 562 auto e = errno; 563 std::string msg = "inotify_add_watch failed with error " + 564 std::to_string(e); 565 log<level::ERR>(msg.c_str()); 566 abort(); 567 } 568 569 _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>( 570 _event, _pelFileDeleteFD, EPOLLIN, 571 std::bind(std::mem_fn(&Manager::pelFileDeleted), this, 572 std::placeholders::_1, std::placeholders::_2, 573 std::placeholders::_3)); 574 } 575 576 void Manager::pelFileDeleted(sdeventplus::source::IO& /*io*/, int /*fd*/, 577 uint32_t revents) 578 { 579 if (!(revents & EPOLLIN)) 580 { 581 return; 582 } 583 584 // An event for 1 PEL uses 48B. When all PELs are deleted at once, 585 // as many events as there is room for can be handled in one callback. 586 // A size of 2000 will allow 41 to be processed, with additional 587 // callbacks being needed to process the remaining ones. 588 std::array<uint8_t, 2000> data{}; 589 auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size()); 590 if (bytesRead < 0) 591 { 592 auto e = errno; 593 std::string msg = "Failed reading data from inotify event, errno = " + 594 std::to_string(e); 595 log<level::ERR>(msg.c_str()); 596 abort(); 597 } 598 599 auto offset = 0; 600 while (offset < bytesRead) 601 { 602 auto event = reinterpret_cast<inotify_event*>(&data[offset]); 603 if (event->mask & IN_DELETE) 604 { 605 std::string filename{event->name}; 606 607 // Get the PEL ID from the filename and tell the 608 // repo it's been removed, and then delete the BMC 609 // event log if it's there. 610 auto pos = filename.find_first_of('_'); 611 if (pos != std::string::npos) 612 { 613 try 614 { 615 auto idString = filename.substr(pos + 1); 616 auto pelID = std::stoul(idString, nullptr, 16); 617 618 Repository::LogID id{Repository::LogID::Pel(pelID)}; 619 auto removedLogID = _repo.remove(id); 620 if (removedLogID) 621 { 622 _logManager.erase(removedLogID->obmcID.id); 623 } 624 } 625 catch (const std::exception& e) 626 { 627 log<level::INFO>("Could not find PEL ID from its filename", 628 entry("FILENAME=%s", filename.c_str())); 629 } 630 } 631 } 632 633 offset += offsetof(inotify_event, name) + event->len; 634 } 635 } 636 637 std::tuple<uint32_t, uint32_t> Manager::createPELWithFFDCFiles( 638 std::string message, Entry::Level severity, 639 std::map<std::string, std::string> additionalData, 640 std::vector<std::tuple< 641 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, 642 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 643 fFDC) 644 { 645 _logManager.createWithFFDC(message, severity, additionalData, fFDC); 646 647 return {_logManager.lastEntryID(), _repo.lastPelID()}; 648 } 649 650 std::string Manager::getPELJSON(uint32_t obmcLogID) 651 { 652 // Throws InvalidArgument if not found 653 auto pelID = getPELIdFromBMCLogId(obmcLogID); 654 655 auto cmd = fmt::format("/usr/bin/peltool -i {:#x}", pelID); 656 657 FILE* pipe = popen(cmd.c_str(), "r"); 658 if (!pipe) 659 { 660 log<level::ERR>(fmt::format("Error running {}", cmd).c_str()); 661 throw common_error::InternalFailure(); 662 } 663 664 std::string output; 665 std::array<char, 1024> buffer; 666 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) 667 { 668 output.append(buffer.data()); 669 } 670 671 int rc = pclose(pipe); 672 if (WEXITSTATUS(rc) != 0) 673 { 674 log<level::ERR>( 675 fmt::format("Error running {}, rc = {}", cmd, rc).c_str()); 676 throw common_error::InternalFailure(); 677 } 678 679 return output; 680 } 681 682 void Manager::checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel) 683 { 684 if ((pel->userHeader().severity() == 685 static_cast<uint8_t>(SeverityType::nonError)) || 686 (pel->userHeader().severity() == 687 static_cast<uint8_t>(SeverityType::recovered))) 688 { 689 log<level::DEBUG>( 690 "PEL severity informational or recovered. no quiesce needed"); 691 return; 692 } 693 if (!_logManager.isQuiesceOnErrorEnabled()) 694 { 695 log<level::DEBUG>("QuiesceOnHwError not enabled, no quiesce needed"); 696 return; 697 } 698 699 CreatorID creatorID{pel->privateHeader().creatorID()}; 700 701 if ((creatorID != CreatorID::openBMC) && 702 (creatorID != CreatorID::hostboot) && 703 (creatorID != CreatorID::ioDrawer) && (creatorID != CreatorID::occ) && 704 (creatorID != CreatorID::phyp)) 705 { 706 return; 707 } 708 709 // Now check if it has any type of callout 710 if (pel->isHwCalloutPresent()) 711 { 712 log<level::INFO>( 713 "QuiesceOnHwError enabled, PEL severity not nonError or recovered, " 714 "and callout is present"); 715 716 _logManager.quiesceOnError(pel->obmcLogID()); 717 } 718 } 719 720 std::string Manager::getEventId(const openpower::pels::PEL& pel) const 721 { 722 std::string str; 723 auto src = pel.primarySRC(); 724 if (src) 725 { 726 const auto& hexwords = (*src)->hexwordData(); 727 728 std::string refcode = (*src)->asciiString(); 729 size_t pos = refcode.find_last_not_of(0x20); 730 if (pos != std::string::npos) 731 { 732 refcode.erase(pos + 1); 733 } 734 str = refcode; 735 736 for (auto& value : hexwords) 737 { 738 str += " "; 739 str += getNumberString("%08X", value); 740 } 741 } 742 return sanitizeFieldForDBus(str); 743 } 744 745 void Manager::updateEventId(std::unique_ptr<openpower::pels::PEL>& pel) 746 { 747 std::string eventIdStr = getEventId(*pel); 748 749 auto entryN = _logManager.entries.find(pel->obmcLogID()); 750 if (entryN != _logManager.entries.end()) 751 { 752 entryN->second->eventId(eventIdStr); 753 } 754 } 755 756 std::string Manager::sanitizeFieldForDBus(std::string field) 757 { 758 std::for_each(field.begin(), field.end(), [](char& ch) { 759 if (((ch < ' ') || (ch > '~')) && (ch != '\n') && (ch != '\t')) 760 { 761 ch = ' '; 762 } 763 }); 764 return field; 765 } 766 767 std::string Manager::getResolution(const openpower::pels::PEL& pel) const 768 { 769 std::string str; 770 std::string resolution; 771 auto src = pel.primarySRC(); 772 if (src) 773 { 774 // First extract the callout pointer and then go through 775 const auto& callouts = (*src)->callouts(); 776 namespace pv = openpower::pels::pel_values; 777 // All PELs dont have callout, check before parsing callout data 778 if (callouts) 779 { 780 const auto& entries = callouts->callouts(); 781 // Entry starts with index 1 782 uint8_t index = 1; 783 for (auto& entry : entries) 784 { 785 resolution += std::to_string(index) + ". "; 786 // Adding Location code to resolution 787 if (!entry->locationCode().empty()) 788 resolution += "Location Code: " + entry->locationCode() + 789 ", "; 790 if (entry->fruIdentity()) 791 { 792 // Get priority and set the resolution string 793 str = pv::getValue(entry->priority(), 794 pel_values::calloutPriorityValues, 795 pel_values::registryNamePos); 796 str[0] = toupper(str[0]); 797 resolution += "Priority: " + str + ", "; 798 if (entry->fruIdentity()->getPN().has_value()) 799 { 800 resolution += 801 "PN: " + entry->fruIdentity()->getPN().value() + 802 ", "; 803 } 804 if (entry->fruIdentity()->getSN().has_value()) 805 { 806 resolution += 807 "SN: " + entry->fruIdentity()->getSN().value() + 808 ", "; 809 } 810 if (entry->fruIdentity()->getCCIN().has_value()) 811 { 812 resolution += 813 "CCIN: " + entry->fruIdentity()->getCCIN().value() + 814 ", "; 815 } 816 // Add the maintenance procedure 817 if (entry->fruIdentity()->getMaintProc().has_value()) 818 { 819 resolution += 820 "Procedure: " + 821 entry->fruIdentity()->getMaintProc().value() + ", "; 822 } 823 } 824 resolution.resize(resolution.size() - 2); 825 resolution += "\n"; 826 index++; 827 } 828 } 829 } 830 return sanitizeFieldForDBus(resolution); 831 } 832 833 bool Manager::updateResolution(const openpower::pels::PEL& pel) 834 { 835 std::string callouts = getResolution(pel); 836 auto entryN = _logManager.entries.find(pel.obmcLogID()); 837 if (entryN != _logManager.entries.end()) 838 { 839 entryN->second->resolution(callouts, true); 840 } 841 842 return false; 843 } 844 845 void Manager::updateDBusSeverity(const openpower::pels::PEL& pel) 846 { 847 // The final severity of the PEL may not agree with the 848 // original severity of the D-Bus event log. Update the 849 // D-Bus property to match in some cases. This is to 850 // ensure there isn't a Critical or Warning Redfish event 851 // log for an informational or recovered PEL (or vice versa). 852 // This doesn't make an explicit call to serialize the new 853 // event log property value because updateEventId() is called 854 // right after this and will do it. 855 auto sevType = 856 static_cast<SeverityType>(pel.userHeader().severity() & 0xF0); 857 858 auto entryN = _logManager.entries.find(pel.obmcLogID()); 859 if (entryN != _logManager.entries.end()) 860 { 861 auto newSeverity = fixupLogSeverity(entryN->second->severity(), 862 sevType); 863 if (newSeverity) 864 { 865 log<level::INFO>( 866 fmt::format( 867 "Changing event log {} severity from {} " 868 "to {} to match PEL", 869 entryN->second->id(), 870 Entry::convertLevelToString(entryN->second->severity()), 871 Entry::convertLevelToString(*newSeverity)) 872 .c_str()); 873 874 entryN->second->severity(*newSeverity, true); 875 } 876 } 877 } 878 879 void Manager::setEntryPath(uint32_t obmcLogID) 880 { 881 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 882 if (auto attributes = _repo.getPELAttributes(id); attributes) 883 { 884 auto& attr = attributes.value().get(); 885 auto entry = _logManager.entries.find(obmcLogID); 886 if (entry != _logManager.entries.end()) 887 { 888 entry->second->path(attr.path, true); 889 } 890 } 891 } 892 893 void Manager::setServiceProviderNotifyFlag(uint32_t obmcLogID) 894 { 895 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 896 if (auto attributes = _repo.getPELAttributes(id); attributes) 897 { 898 auto& attr = attributes.value().get(); 899 auto entry = _logManager.entries.find(obmcLogID); 900 if (entry != _logManager.entries.end()) 901 { 902 if (attr.actionFlags.test(callHomeFlagBit)) 903 { 904 entry->second->serviceProviderNotify(Entry::Notify::Notify); 905 } 906 else 907 { 908 entry->second->serviceProviderNotify(Entry::Notify::Inhibit); 909 } 910 } 911 } 912 } 913 914 void Manager::createPELEntry(uint32_t obmcLogID, bool skipIaSignal) 915 { 916 std::map<std::string, PropertiesVariant> varData; 917 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 918 if (auto attributes = _repo.getPELAttributes(id); attributes) 919 { 920 namespace pv = openpower::pels::pel_values; 921 auto& attr = attributes.value().get(); 922 923 // get the hidden flag values 924 auto sevType = static_cast<SeverityType>(attr.severity & 0xF0); 925 auto isHidden = true; 926 if (((sevType != SeverityType::nonError) && 927 attr.actionFlags.test(reportFlagBit) && 928 !attr.actionFlags.test(hiddenFlagBit)) || 929 ((sevType == SeverityType::nonError) && 930 attr.actionFlags.test(serviceActionFlagBit))) 931 { 932 isHidden = false; 933 } 934 varData.emplace(std::string("Hidden"), isHidden); 935 varData.emplace( 936 std::string("Subsystem"), 937 pv::getValue(attr.subsystem, pel_values::subsystemValues)); 938 939 varData.emplace( 940 std::string("ManagementSystemAck"), 941 (attr.hmcState == TransmissionState::acked ? true : false)); 942 943 // Path to create PELEntry Interface is same as PEL 944 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 945 // Create Interface for PELEntry and set properties 946 auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(), path, 947 varData, obmcLogID, &_repo); 948 if (!skipIaSignal) 949 { 950 pelEntry->emit_added(); 951 } 952 _pelEntries.emplace(std::move(path), std::move(pelEntry)); 953 } 954 } 955 956 uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId) 957 { 958 Repository::LogID id{Repository::LogID::Obmc(bmcLogId)}; 959 if (auto logId = _repo.getLogID(id); !logId.has_value()) 960 { 961 throw common_error::InvalidArgument(); 962 } 963 else 964 { 965 return logId->pelID.id; 966 } 967 } 968 969 uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId) 970 { 971 Repository::LogID id{Repository::LogID::Pel(pelId)}; 972 if (auto logId = _repo.getLogID(id); !logId.has_value()) 973 { 974 throw common_error::InvalidArgument(); 975 } 976 else 977 { 978 return logId->obmcID.id; 979 } 980 } 981 982 void Manager::updateProgressSRC( 983 std::unique_ptr<openpower::pels::PEL>& pel) const 984 { 985 // Check for pel severity of type - 0x51 = critical error, system 986 // termination 987 if (pel->userHeader().severity() == 0x51) 988 { 989 auto src = pel->primarySRC(); 990 if (src) 991 { 992 std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct(); 993 uint64_t srcRefCode = 0; 994 995 // Read bytes from offset [40-47] e.g. BD8D1001 996 for (int i = 0; i < 8; i++) 997 { 998 srcRefCode |= (static_cast<uint64_t>(asciiSRC[40 + i]) 999 << (8 * i)); 1000 } 1001 1002 try 1003 { 1004 _dataIface->createProgressSRC(srcRefCode, asciiSRC); 1005 } 1006 catch (std::exception& e) 1007 { 1008 // Exception - may be no boot progress interface on dbus 1009 } 1010 } 1011 } 1012 } 1013 1014 void Manager::scheduleObmcLogDelete(uint32_t obmcLogID) 1015 { 1016 _obmcLogDeleteEventSource = std::make_unique<sdeventplus::source::Defer>( 1017 _event, std::bind(std::mem_fn(&Manager::deleteObmcLog), this, 1018 std::placeholders::_1, obmcLogID)); 1019 } 1020 1021 void Manager::deleteObmcLog(sdeventplus::source::EventBase&, uint32_t obmcLogID) 1022 { 1023 log<level::INFO>( 1024 fmt::format("Removing event log with no PEL: {}", obmcLogID).c_str()); 1025 _logManager.erase(obmcLogID); 1026 _obmcLogDeleteEventSource.reset(); 1027 } 1028 1029 } // namespace pels 1030 } // namespace openpower 1031