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