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 <filesystem> 30 #include <fstream> 31 #include <locale> 32 #include <xyz/openbmc_project/Common/error.hpp> 33 #include <xyz/openbmc_project/Logging/Create/server.hpp> 34 35 namespace openpower 36 { 37 namespace pels 38 { 39 40 using namespace phosphor::logging; 41 namespace fs = std::filesystem; 42 namespace rg = openpower::pels::message; 43 44 namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error; 45 46 using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create; 47 48 namespace additional_data 49 { 50 constexpr auto rawPEL = "RAWPEL"; 51 constexpr auto esel = "ESEL"; 52 constexpr auto error = "ERROR_NAME"; 53 } // namespace additional_data 54 55 constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default"; 56 57 Manager::~Manager() 58 { 59 if (_pelFileDeleteFD != -1) 60 { 61 if (_pelFileDeleteWatchFD != -1) 62 { 63 inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD); 64 } 65 close(_pelFileDeleteFD); 66 } 67 } 68 69 void Manager::create(const std::string& message, uint32_t obmcLogID, 70 uint64_t timestamp, Entry::Level severity, 71 const std::vector<std::string>& additionalData, 72 const std::vector<std::string>& associations, 73 const FFDCEntries& ffdc) 74 { 75 AdditionalData ad{additionalData}; 76 77 // If a PEL was passed in via a filename or in an ESEL, 78 // use that. Otherwise, create one. 79 auto rawPelPath = ad.getValue(additional_data::rawPEL); 80 if (rawPelPath) 81 { 82 addRawPEL(*rawPelPath, obmcLogID); 83 } 84 else 85 { 86 auto esel = ad.getValue(additional_data::esel); 87 if (esel) 88 { 89 addESELPEL(*esel, obmcLogID); 90 } 91 else 92 { 93 createPEL(message, obmcLogID, timestamp, severity, additionalData, 94 associations, ffdc); 95 } 96 } 97 98 setEntryPath(obmcLogID); 99 setServiceProviderNotifyFlag(obmcLogID); 100 } 101 102 void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID) 103 { 104 if (fs::exists(rawPelPath)) 105 { 106 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary); 107 108 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file), 109 std::istreambuf_iterator<char>()); 110 if (file.fail()) 111 { 112 log<level::ERR>("Filesystem error reading a raw PEL", 113 entry("PELFILE=%s", rawPelPath.c_str()), 114 entry("OBMCLOGID=%d", obmcLogID)); 115 // TODO, Decide what to do here. Maybe nothing. 116 return; 117 } 118 119 file.close(); 120 121 addPEL(data, obmcLogID); 122 123 std::error_code ec; 124 fs::remove(rawPelPath, ec); 125 } 126 else 127 { 128 log<level::ERR>("Raw PEL file from BMC event log does not exist", 129 entry("PELFILE=%s", (rawPelPath).c_str()), 130 entry("OBMCLOGID=%d", obmcLogID)); 131 } 132 } 133 134 void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID) 135 { 136 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID); 137 if (pel->valid()) 138 { 139 // PELs created by others still need this field set by us. 140 pel->setCommitTime(); 141 142 // Assign Id other than to Hostbot PEL 143 if ((pel->privateHeader()).creatorID() != 144 static_cast<uint8_t>(CreatorID::hostboot)) 145 { 146 pel->assignID(); 147 } 148 else 149 { 150 const Repository::LogID id{Repository::LogID::Pel(pel->id())}; 151 auto result = _repo.hasPEL(id); 152 if (result) 153 { 154 log<level::WARNING>( 155 fmt::format("Duplicate HostBoot PEL Id {:#X} found; " 156 "moving it to archive folder", 157 pel->id()) 158 .c_str()); 159 160 _repo.archivePEL(*pel); 161 162 // No need to keep around the openBMC event log entry 163 scheduleObmcLogDelete(obmcLogID); 164 return; 165 } 166 } 167 168 // Update System Info to Extended User Data 169 pel->updateSysInfoInExtendedUserDataSection(*_dataIface); 170 171 // Check for severity 0x51 and update boot progress SRC 172 updateProgressSRC(pel); 173 174 try 175 { 176 log<level::DEBUG>( 177 fmt::format("Adding external PEL {:#x} (BMC ID {}) to repo", 178 pel->id(), obmcLogID) 179 .c_str()); 180 181 _repo.add(pel); 182 183 if (_repo.sizeWarning()) 184 { 185 scheduleRepoPrune(); 186 } 187 188 // Activate any resulting service indicators if necessary 189 auto policy = service_indicators::getPolicy(*_dataIface); 190 policy->activate(*pel); 191 } 192 catch (const std::exception& e) 193 { 194 // Probably a full or r/o filesystem, not much we can do. 195 log<level::ERR>("Unable to add PEL to Repository", 196 entry("PEL_ID=0x%X", pel->id())); 197 } 198 199 updateEventId(pel); 200 updateResolution(*pel); 201 createPELEntry(obmcLogID); 202 203 // Check if firmware should quiesce system due to error 204 checkPelAndQuiesce(pel); 205 } 206 else 207 { 208 log<level::ERR>("Invalid PEL received from the host", 209 entry("OBMCLOGID=%d", obmcLogID)); 210 211 AdditionalData ad; 212 ad.add("PLID", getNumberString("0x%08X", pel->plid())); 213 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID)); 214 ad.add("PEL_SIZE", std::to_string(pelData.size())); 215 216 std::string asciiString; 217 auto src = pel->primarySRC(); 218 if (src) 219 { 220 asciiString = (*src)->asciiString(); 221 } 222 223 ad.add("SRC", asciiString); 224 225 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL", 226 Entry::Level::Error, ad); 227 228 // Save it to a file for debug in the lab. Just keep the latest. 229 // Not adding it to the PEL because it could already be max size 230 // and don't want to truncate an already invalid PEL. 231 std::ofstream pelFile{getPELRepoPath() / "badPEL"}; 232 pelFile.write(reinterpret_cast<const char*>(pelData.data()), 233 pelData.size()); 234 235 // No need to keep around the openBMC event log entry 236 scheduleObmcLogDelete(obmcLogID); 237 } 238 } 239 240 void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID) 241 { 242 std::vector<uint8_t> data; 243 244 log<level::DEBUG>("Adding PEL from ESEL", 245 entry("OBMC_LOG_ID=%d", obmcLogID)); 246 247 try 248 { 249 data = std::move(eselToRawData(esel)); 250 } 251 catch (const std::exception& e) 252 { 253 // Try to add it below anyway, so it follows the usual bad data path. 254 log<level::ERR>("Problems converting ESEL string to a byte vector"); 255 } 256 257 addPEL(data, obmcLogID); 258 } 259 260 std::vector<uint8_t> Manager::eselToRawData(const std::string& esel) 261 { 262 std::vector<uint8_t> data; 263 std::string byteString; 264 265 // As the eSEL string looks like: "50 48 00 ab ..." there are 3 266 // characters per raw byte, and since the actual PEL data starts 267 // at the 16th byte, the code will grab the PEL data starting at 268 // offset 48 in the string. 269 static constexpr size_t pelStart = 16 * 3; 270 271 if (esel.size() <= pelStart) 272 { 273 log<level::ERR>("ESEL data too short", 274 entry("ESEL_SIZE=%d", esel.size())); 275 276 throw std::length_error("ESEL data too short"); 277 } 278 279 for (size_t i = pelStart; i < esel.size(); i += 3) 280 { 281 if (i + 1 < esel.size()) 282 { 283 byteString = esel.substr(i, 2); 284 data.push_back(std::stoi(byteString, nullptr, 16)); 285 } 286 else 287 { 288 log<level::ERR>("ESEL data too short", 289 entry("ESEL_SIZE=%d", esel.size())); 290 throw std::length_error("ESEL data too short"); 291 } 292 } 293 294 return data; 295 } 296 297 void Manager::erase(uint32_t obmcLogID) 298 { 299 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 300 301 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 302 _pelEntries.erase(path); 303 _repo.remove(id); 304 } 305 306 bool Manager::isDeleteProhibited(uint32_t /*obmcLogID*/) 307 { 308 return false; 309 } 310 311 PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc) 312 { 313 PelFFDC pelFFDC; 314 315 std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) { 316 PelFFDCfile pf; 317 pf.subType = std::get<ffdcSubtypePos>(f); 318 pf.version = std::get<ffdcVersionPos>(f); 319 pf.fd = std::get<ffdcFDPos>(f); 320 321 switch (std::get<ffdcFormatPos>(f)) 322 { 323 case Create::FFDCFormat::JSON: 324 pf.format = UserDataFormat::json; 325 break; 326 case Create::FFDCFormat::CBOR: 327 pf.format = UserDataFormat::cbor; 328 break; 329 case Create::FFDCFormat::Text: 330 pf.format = UserDataFormat::text; 331 break; 332 case Create::FFDCFormat::Custom: 333 pf.format = UserDataFormat::custom; 334 break; 335 } 336 337 pelFFDC.push_back(pf); 338 }); 339 340 return pelFFDC; 341 } 342 343 void Manager::createPEL(const std::string& message, uint32_t obmcLogID, 344 uint64_t timestamp, 345 phosphor::logging::Entry::Level severity, 346 const std::vector<std::string>& additionalData, 347 const std::vector<std::string>& /*associations*/, 348 const FFDCEntries& ffdc) 349 { 350 auto entry = _registry.lookup(message, rg::LookupType::name); 351 auto pelFFDC = convertToPelFFDC(ffdc); 352 AdditionalData ad{additionalData}; 353 std::string msg; 354 355 if (!entry) 356 { 357 // Instead, get the default entry that means there is no 358 // other matching entry. This error will still use the 359 // AdditionalData values of the original error, and this 360 // code will add the error message value that wasn't found 361 // to this AD. This way, there will at least be a PEL, 362 // possibly with callouts, to allow users to debug the 363 // issue that caused the error even without its own PEL. 364 msg = "Event not found in PEL message registry: " + message; 365 log<level::INFO>(msg.c_str()); 366 367 entry = _registry.lookup(defaultLogMessage, rg::LookupType::name); 368 if (!entry) 369 { 370 log<level::ERR>("Default event not found in PEL message registry"); 371 return; 372 } 373 374 ad.add(additional_data::error, message); 375 } 376 377 auto pel = std::make_unique<openpower::pels::PEL>( 378 *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface); 379 380 _repo.add(pel); 381 382 if (_repo.sizeWarning()) 383 { 384 scheduleRepoPrune(); 385 } 386 387 auto src = pel->primarySRC(); 388 if (src) 389 { 390 auto msg = 391 fmt::format("Created PEL {:#x} (BMC ID {}) with SRC {}", pel->id(), 392 pel->obmcLogID(), (*src)->asciiString()); 393 while (msg.back() == ' ') 394 { 395 msg.pop_back(); 396 } 397 log<level::INFO>(msg.c_str()); 398 } 399 400 // Check for severity 0x51 and update boot progress SRC 401 updateProgressSRC(pel); 402 403 // Activate any resulting service indicators if necessary 404 auto policy = service_indicators::getPolicy(*_dataIface); 405 policy->activate(*pel); 406 407 updateDBusSeverity(*pel); 408 updateEventId(pel); 409 updateResolution(*pel); 410 createPELEntry(obmcLogID); 411 412 // Check if firmware should quiesce system due to error 413 checkPelAndQuiesce(pel); 414 } 415 416 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID) 417 { 418 Repository::LogID id{Repository::LogID::Pel(pelID)}; 419 std::optional<int> fd; 420 421 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID)); 422 423 try 424 { 425 fd = _repo.getPELFD(id); 426 } 427 catch (const std::exception& e) 428 { 429 throw common_error::InternalFailure(); 430 } 431 432 if (!fd) 433 { 434 throw common_error::InvalidArgument(); 435 } 436 437 scheduleFDClose(*fd); 438 439 return *fd; 440 } 441 442 void Manager::scheduleFDClose(int fd) 443 { 444 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>( 445 _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd, 446 std::placeholders::_1)); 447 } 448 449 void Manager::closeFD(int fd, sdeventplus::source::EventBase& /*source*/) 450 { 451 close(fd); 452 _fdCloserEventSource.reset(); 453 } 454 455 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID) 456 { 457 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 458 std::optional<std::vector<uint8_t>> data; 459 460 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID)); 461 462 try 463 { 464 data = _repo.getPELData(id); 465 } 466 catch (const std::exception& e) 467 { 468 throw common_error::InternalFailure(); 469 } 470 471 if (!data) 472 { 473 throw common_error::InvalidArgument(); 474 } 475 476 return *data; 477 } 478 479 void Manager::hostAck(uint32_t pelID) 480 { 481 Repository::LogID id{Repository::LogID::Pel(pelID)}; 482 483 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID)); 484 485 if (!_repo.hasPEL(id)) 486 { 487 throw common_error::InvalidArgument(); 488 } 489 490 if (_hostNotifier) 491 { 492 _hostNotifier->ackPEL(pelID); 493 } 494 } 495 496 void Manager::hostReject(uint32_t pelID, RejectionReason reason) 497 { 498 Repository::LogID id{Repository::LogID::Pel(pelID)}; 499 500 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID), 501 entry("REASON=%d", static_cast<int>(reason))); 502 503 if (!_repo.hasPEL(id)) 504 { 505 throw common_error::InvalidArgument(); 506 } 507 508 if (reason == RejectionReason::BadPEL) 509 { 510 AdditionalData data; 511 data.add("BAD_ID", getNumberString("0x%08X", pelID)); 512 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost", 513 Entry::Level::Informational, data); 514 if (_hostNotifier) 515 { 516 _hostNotifier->setBadPEL(pelID); 517 } 518 } 519 else if ((reason == RejectionReason::HostFull) && _hostNotifier) 520 { 521 _hostNotifier->setHostFull(pelID); 522 } 523 } 524 525 void Manager::scheduleRepoPrune() 526 { 527 _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>( 528 _event, std::bind(std::mem_fn(&Manager::pruneRepo), this, 529 std::placeholders::_1)); 530 } 531 532 void Manager::pruneRepo(sdeventplus::source::EventBase& /*source*/) 533 { 534 auto idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation(); 535 536 auto idsToDelete = _repo.prune(idsWithHwIsoEntry); 537 538 // Remove the OpenBMC event logs for the PELs that were just removed. 539 std::for_each(idsToDelete.begin(), idsToDelete.end(), 540 [this](auto id) { this->_logManager.erase(id); }); 541 542 _repoPrunerEventSource.reset(); 543 } 544 545 void Manager::setupPELDeleteWatch() 546 { 547 _pelFileDeleteFD = inotify_init1(IN_NONBLOCK); 548 if (-1 == _pelFileDeleteFD) 549 { 550 auto e = errno; 551 std::string msg = 552 "inotify_init1 failed with errno " + std::to_string(e); 553 log<level::ERR>(msg.c_str()); 554 abort(); 555 } 556 557 _pelFileDeleteWatchFD = inotify_add_watch( 558 _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE); 559 if (-1 == _pelFileDeleteWatchFD) 560 { 561 auto e = errno; 562 std::string msg = 563 "inotify_add_watch failed with error " + std::to_string(e); 564 log<level::ERR>(msg.c_str()); 565 abort(); 566 } 567 568 _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>( 569 _event, _pelFileDeleteFD, EPOLLIN, 570 std::bind(std::mem_fn(&Manager::pelFileDeleted), this, 571 std::placeholders::_1, std::placeholders::_2, 572 std::placeholders::_3)); 573 } 574 575 void Manager::pelFileDeleted(sdeventplus::source::IO& /*io*/, int /*fd*/, 576 uint32_t revents) 577 { 578 if (!(revents & EPOLLIN)) 579 { 580 return; 581 } 582 583 // An event for 1 PEL uses 48B. When all PELs are deleted at once, 584 // as many events as there is room for can be handled in one callback. 585 // A size of 2000 will allow 41 to be processed, with additional 586 // callbacks being needed to process the remaining ones. 587 std::array<uint8_t, 2000> data{}; 588 auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size()); 589 if (bytesRead < 0) 590 { 591 auto e = errno; 592 std::string msg = "Failed reading data from inotify event, errno = " + 593 std::to_string(e); 594 log<level::ERR>(msg.c_str()); 595 abort(); 596 } 597 598 auto offset = 0; 599 while (offset < bytesRead) 600 { 601 auto event = reinterpret_cast<inotify_event*>(&data[offset]); 602 if (event->mask & IN_DELETE) 603 { 604 std::string filename{event->name}; 605 606 // Get the PEL ID from the filename and tell the 607 // repo it's been removed, and then delete the BMC 608 // event log if it's there. 609 auto pos = filename.find_first_of('_'); 610 if (pos != std::string::npos) 611 { 612 try 613 { 614 auto idString = filename.substr(pos + 1); 615 auto pelID = std::stoul(idString, nullptr, 16); 616 617 Repository::LogID id{Repository::LogID::Pel(pelID)}; 618 auto removedLogID = _repo.remove(id); 619 if (removedLogID) 620 { 621 _logManager.erase(removedLogID->obmcID.id); 622 } 623 } 624 catch (const std::exception& e) 625 { 626 log<level::INFO>("Could not find PEL ID from its filename", 627 entry("FILENAME=%s", filename.c_str())); 628 } 629 } 630 } 631 632 offset += offsetof(inotify_event, name) + event->len; 633 } 634 } 635 636 std::tuple<uint32_t, uint32_t> Manager::createPELWithFFDCFiles( 637 std::string message, Entry::Level severity, 638 std::map<std::string, std::string> additionalData, 639 std::vector<std::tuple< 640 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, 641 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 642 fFDC) 643 { 644 _logManager.createWithFFDC(message, severity, additionalData, fFDC); 645 646 return {_logManager.lastEntryID(), _repo.lastPelID()}; 647 } 648 649 std::string Manager::getPELJSON(uint32_t obmcLogID) 650 { 651 Repository::LogID id{Repository::LogID::Obmc(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 += 790 "Location Code: " + entry->locationCode() + ", "; 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 = 863 fixupLogSeverity(entryN->second->severity(), 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 entry->second->serviceProviderNotify( 904 attr.actionFlags.test(callHomeFlagBit), true); 905 } 906 } 907 } 908 909 void Manager::createPELEntry(uint32_t obmcLogID, bool skipIaSignal) 910 { 911 std::map<std::string, PropertiesVariant> varData; 912 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 913 if (auto attributes = _repo.getPELAttributes(id); attributes) 914 { 915 namespace pv = openpower::pels::pel_values; 916 auto& attr = attributes.value().get(); 917 918 // get the hidden flag values 919 auto sevType = static_cast<SeverityType>(attr.severity & 0xF0); 920 auto isHidden = true; 921 if (((sevType != SeverityType::nonError) && 922 attr.actionFlags.test(reportFlagBit) && 923 !attr.actionFlags.test(hiddenFlagBit)) || 924 ((sevType == SeverityType::nonError) && 925 attr.actionFlags.test(serviceActionFlagBit))) 926 { 927 isHidden = false; 928 } 929 varData.emplace(std::string("Hidden"), isHidden); 930 varData.emplace( 931 std::string("Subsystem"), 932 pv::getValue(attr.subsystem, pel_values::subsystemValues)); 933 934 varData.emplace( 935 std::string("ManagementSystemAck"), 936 (attr.hmcState == TransmissionState::acked ? true : false)); 937 938 // Path to create PELEntry Interface is same as PEL 939 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 940 // Create Interface for PELEntry and set properties 941 auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(), path, 942 varData, obmcLogID, &_repo); 943 if (!skipIaSignal) 944 { 945 pelEntry->emit_added(); 946 } 947 _pelEntries.emplace(std::move(path), std::move(pelEntry)); 948 } 949 } 950 951 uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId) 952 { 953 Repository::LogID id{Repository::LogID::Obmc(bmcLogId)}; 954 if (auto logId = _repo.getLogID(id); !logId.has_value()) 955 { 956 throw common_error::InvalidArgument(); 957 } 958 else 959 { 960 return logId->pelID.id; 961 } 962 } 963 964 uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId) 965 { 966 Repository::LogID id{Repository::LogID::Pel(pelId)}; 967 if (auto logId = _repo.getLogID(id); !logId.has_value()) 968 { 969 throw common_error::InvalidArgument(); 970 } 971 else 972 { 973 return logId->obmcID.id; 974 } 975 } 976 977 void Manager::updateProgressSRC( 978 std::unique_ptr<openpower::pels::PEL>& pel) const 979 { 980 // Check for pel severity of type - 0x51 = critical error, system 981 // termination 982 if (pel->userHeader().severity() == 0x51) 983 { 984 auto src = pel->primarySRC(); 985 if (src) 986 { 987 std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct(); 988 uint64_t srcRefCode = 0; 989 990 // Read bytes from offset [40-47] e.g. BD8D1001 991 for (int i = 0; i < 8; i++) 992 { 993 srcRefCode |= 994 (static_cast<uint64_t>(asciiSRC[40 + i]) << (8 * i)); 995 } 996 997 try 998 { 999 _dataIface->createProgressSRC(srcRefCode, asciiSRC); 1000 } 1001 catch (std::exception& e) 1002 { 1003 // Exception - may be no boot progress interface on dbus 1004 } 1005 } 1006 } 1007 } 1008 1009 void Manager::scheduleObmcLogDelete(uint32_t obmcLogID) 1010 { 1011 _obmcLogDeleteEventSource = std::make_unique<sdeventplus::source::Defer>( 1012 _event, std::bind(std::mem_fn(&Manager::deleteObmcLog), this, 1013 std::placeholders::_1, obmcLogID)); 1014 } 1015 1016 void Manager::deleteObmcLog(sdeventplus::source::EventBase&, uint32_t obmcLogID) 1017 { 1018 log<level::INFO>( 1019 fmt::format("Removing event log with no PEL: {}", obmcLogID).c_str()); 1020 _logManager.erase(obmcLogID); 1021 _obmcLogDeleteEventSource.reset(); 1022 } 1023 1024 } // namespace pels 1025 } // namespace openpower 1026