1 #include "create_pel.hpp"
2 
3 #include "dump_utils.hpp"
4 #include "sbe_consts.hpp"
5 
6 #include <fcntl.h>
7 #include <libekb.H>
8 #include <unistd.h>
9 
10 #include <phosphor-logging/elog.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <xyz/openbmc_project/Logging/Create/server.hpp>
13 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
14 
15 #include <cerrno>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <format>
20 #include <map>
21 #include <stdexcept>
22 #include <string>
23 #include <tuple>
24 #include <vector>
25 
26 namespace openpower::dump::pel
27 {
28 
29 using namespace phosphor::logging;
30 using namespace openpower::dump::SBE;
31 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
32 constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";
33 constexpr auto opLoggingInterface = "org.open_power.Logging.PEL";
34 
35 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
36                            const FFDCData& ffdcData, const Severity severity,
37                            const std::optional<PELFFDCInfo>& pelFFDCInfoOpt)
38 {
39     uint32_t plid = 0;
40     std::unordered_map<std::string, std::string> additionalData = {
41         {"_PID", std::to_string(getpid())}, {"SBE_ERR_MSG", sbeError.what()}};
42     auto bus = sdbusplus::bus::new_default();
43 
44     additionalData.emplace("_PID", std::to_string(getpid()));
45     additionalData.emplace("SBE_ERR_MSG", sbeError.what());
46 
47     for (auto& data : ffdcData)
48     {
49         additionalData.emplace(data);
50     }
51 
52     PELFFDCInfo pelFFDCInfo;
53     if (pelFFDCInfoOpt)
54     {
55         pelFFDCInfo = *pelFFDCInfoOpt;
56     }
57 
58     // Negative fd value indicates error case or invalid file
59     // No need of special processing , just log error with additional ffdc.
60     else if (sbeError.getFd() > 0)
61     {
62         // Refer phosphor-logging/extensions/openpower-pels/README.md section
63         // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
64         // for details of related to createPEL with SBE FFDC information
65         // usin g CreateWithFFDCFiles api.
66         pelFFDCInfo.emplace_back(std::make_tuple(
67             sdbusplus::xyz::openbmc_project::Logging::server::Create::
68                 FFDCFormat::Custom,
69             FFDC_FORMAT_SUBTYPE, FFDC_FORMAT_VERSION, sbeError.getFd()));
70     }
71     try
72     {
73         auto service = util::getService(bus, opLoggingInterface,
74                                         loggingObjectPath);
75         auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
76                                           opLoggingInterface,
77                                           "CreatePELWithFFDCFiles");
78         auto level =
79             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
80                 severity);
81         method.append(event, level, additionalData, pelFFDCInfo);
82         auto response = bus.call(method);
83 
84         // reply will be tuple containing bmc log id, platform log id
85         std::tuple<uint32_t, uint32_t> reply = {0, 0};
86 
87         // parse dbus response into reply
88         response.read(reply);
89         plid = std::get<1>(reply); // platform log id is tuple "second"
90     }
91     catch (const sdbusplus::exception_t& e)
92     {
93         lg2::error(
94             "D-Bus call exception OBJPATH={OBJPATH}, INTERFACE={INTERFACE}, "
95             "EXCEPTION={ERROR}",
96             "OBJPATH", loggingObjectPath, "INTERFACE", loggingInterface,
97             "ERROR", e);
98 
99         throw;
100     }
101     catch (const std::exception& e)
102     {
103         throw;
104     }
105 
106     return plid;
107 }
108 
109 openpower::dump::pel::Severity convertSeverityToEnum(uint8_t severity)
110 {
111     switch (severity)
112     {
113         case openpower::phal::FAPI2_ERRL_SEV_RECOVERED:
114             return openpower::dump::pel::Severity::Informational;
115         case openpower::phal::FAPI2_ERRL_SEV_PREDICTIVE:
116             return openpower::dump::pel::Severity::Warning;
117         case openpower::phal::FAPI2_ERRL_SEV_UNRECOVERABLE:
118             return openpower::dump::pel::Severity::Error;
119         default:
120             return openpower::dump::pel::Severity::Error;
121     }
122 }
123 
124 void processFFDCPackets(const openpower::phal::sbeError_t& sbeError,
125                         const std::string& event,
126                         openpower::dump::pel::FFDCData& pelAdditionalData)
127 {
128     const auto& ffdcFileList = sbeError.getFfdcFileList();
129     for (const auto& [slid, ffdcTuple] : ffdcFileList)
130     {
131         uint8_t severity;
132         int fd;
133         std::filesystem::path path;
134         std::tie(severity, fd, path) = ffdcTuple;
135 
136         Severity logSeverity = convertSeverityToEnum(severity);
137 
138         if (logSeverity != openpower::dump::pel::Severity::Informational)
139         {
140             lg2::info(
141                 "Changing severity from {SEV_ORIG} to informational in the "
142                 "dumping path",
143                 "SEV_ORIG", logSeverity);
144             logSeverity = openpower::dump::pel::Severity::Informational;
145         }
146 
147         PELFFDCInfo pelFFDCInfo;
148         pelFFDCInfo.emplace_back(
149             std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
150                                 Create::FFDCFormat::Custom,
151                             FFDC_FORMAT_SUBTYPE, FFDC_FORMAT_VERSION, fd));
152 
153         auto logId = openpower::dump::pel::createSbeErrorPEL(
154             event, sbeError, pelAdditionalData, logSeverity);
155         lg2::info("Logged PEL {PELID} for SLID {SLID}", "PELID", logId, "SLID",
156                   slid);
157     }
158 }
159 
160 FFDCFile::FFDCFile(const json& pHALCalloutData) :
161     calloutData(pHALCalloutData.dump()),
162     calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
163 {
164     prepareFFDCFile();
165 }
166 
167 FFDCFile::~FFDCFile()
168 {
169     removeCalloutFile();
170 }
171 
172 int FFDCFile::getFileFD() const
173 {
174     return fileFD;
175 }
176 
177 void FFDCFile::prepareFFDCFile()
178 {
179     createCalloutFile();
180     writeCalloutData();
181     setCalloutFileSeekPos();
182 }
183 
184 void FFDCFile::createCalloutFile()
185 {
186     fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
187 
188     if (fileFD == -1)
189     {
190         lg2::error("Failed to create phalPELCallouts file({FILE}), "
191                    "errorno({ERRNO}) and errormsg({ERRORMSG})",
192                    "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
193                    strerror(errno));
194         throw std::runtime_error("Failed to create phalPELCallouts file");
195     }
196 }
197 
198 void FFDCFile::writeCalloutData()
199 {
200     ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
201 
202     if (rc == -1)
203     {
204         lg2::error("Failed to write phaPELCallout info in file({FILE}), "
205                    "errorno({ERRNO}), errormsg({ERRORMSG})",
206                    "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
207                    strerror(errno));
208         throw std::runtime_error("Failed to write phalPELCallouts info");
209     }
210     else if (rc != static_cast<ssize_t>(calloutData.size()))
211     {
212         lg2::warning("Could not write all phal callout info in file({FILE}), "
213                      "written byte({WRITTEN}), total byte({TOTAL})",
214                      "FILE", calloutFile, "WRITTEN", rc, "TOTAL",
215                      calloutData.size());
216     }
217 }
218 
219 void FFDCFile::setCalloutFileSeekPos()
220 {
221     int rc = lseek(fileFD, 0, SEEK_SET);
222 
223     if (rc == -1)
224     {
225         lg2::error("Failed to set SEEK_SET for phalPELCallouts in "
226                    "file({FILE}), errorno({ERRNO}), errormsg({ERRORMSG})",
227                    "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
228                    strerror(errno));
229 
230         throw std::runtime_error(
231             "Failed to set SEEK_SET for phalPELCallouts file");
232     }
233 }
234 
235 void FFDCFile::removeCalloutFile()
236 {
237     close(fileFD);
238     std::remove(calloutFile.c_str());
239 }
240 
241 } // namespace openpower::dump::pel
242