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