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 constexpr auto entryInterface = "xyz.openbmc_project.Logging.Entry";
35 constexpr auto opEntryInterface = "org.open_power.Logging.PEL.Entry";
36
createSbeErrorPEL(const std::string & event,const sbeError_t & sbeError,const FFDCData & ffdcData,const Severity severity,const std::optional<PELFFDCInfo> & pelFFDCInfoOpt)37 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
38 const FFDCData& ffdcData, const Severity severity,
39 const std::optional<PELFFDCInfo>& pelFFDCInfoOpt)
40 {
41 uint32_t plid = 0;
42 std::unordered_map<std::string, std::string> additionalData = {
43 {"_PID", std::to_string(getpid())}, {"SBE_ERR_MSG", sbeError.what()}};
44 auto bus = sdbusplus::bus::new_default();
45
46 additionalData.emplace("_PID", std::to_string(getpid()));
47 additionalData.emplace("SBE_ERR_MSG", sbeError.what());
48
49 for (auto& data : ffdcData)
50 {
51 additionalData.emplace(data);
52 }
53
54 PELFFDCInfo pelFFDCInfo;
55 if (pelFFDCInfoOpt)
56 {
57 pelFFDCInfo = *pelFFDCInfoOpt;
58 }
59
60 // Negative fd value indicates error case or invalid file
61 // No need of special processing , just log error with additional ffdc.
62 else if (sbeError.getFd() > 0)
63 {
64 // Refer phosphor-logging/extensions/openpower-pels/README.md section
65 // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
66 // for details of related to createPEL with SBE FFDC information
67 // using CreateWithFFDCFiles api.
68 pelFFDCInfo.emplace_back(std::make_tuple(
69 sdbusplus::xyz::openbmc_project::Logging::server::Create::
70 FFDCFormat::Custom,
71 FFDC_FORMAT_SUBTYPE, FFDC_FORMAT_VERSION, sbeError.getFd()));
72 }
73 try
74 {
75 auto service =
76 util::getService(bus, opLoggingInterface, loggingObjectPath);
77 auto method =
78 bus.new_method_call(service.c_str(), loggingObjectPath,
79 opLoggingInterface, "CreatePELWithFFDCFiles");
80 auto level =
81 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
82 severity);
83 method.append(event, level, additionalData, pelFFDCInfo);
84 auto response = bus.call(method);
85
86 // reply will be tuple containing bmc log id, platform log id
87 std::tuple<uint32_t, uint32_t> reply = {0, 0};
88
89 // parse dbus response into reply
90 response.read(reply);
91 plid = std::get<0>(reply); // dbus entry id is tuple "first"
92 }
93 catch (const sdbusplus::exception_t& e)
94 {
95 lg2::error(
96 "D-Bus call exception OBJPATH={OBJPATH}, INTERFACE={INTERFACE}, "
97 "EXCEPTION={ERROR}",
98 "OBJPATH", loggingObjectPath, "INTERFACE", loggingInterface,
99 "ERROR", e);
100
101 throw;
102 }
103 catch (const std::exception& e)
104 {
105 throw;
106 }
107
108 return plid;
109 }
110
convertSeverityToEnum(uint8_t severity)111 openpower::dump::pel::Severity convertSeverityToEnum(uint8_t severity)
112 {
113 switch (severity)
114 {
115 case openpower::phal::FAPI2_ERRL_SEV_RECOVERED:
116 return openpower::dump::pel::Severity::Informational;
117 case openpower::phal::FAPI2_ERRL_SEV_PREDICTIVE:
118 return openpower::dump::pel::Severity::Warning;
119 case openpower::phal::FAPI2_ERRL_SEV_UNRECOVERABLE:
120 return openpower::dump::pel::Severity::Error;
121 default:
122 return openpower::dump::pel::Severity::Error;
123 }
124 }
125
processFFDCPackets(const openpower::phal::sbeError_t & sbeError,const std::string & event,openpower::dump::pel::FFDCData & pelAdditionalData)126 std::vector<uint32_t> processFFDCPackets(
127 const openpower::phal::sbeError_t& sbeError, const std::string& event,
128 openpower::dump::pel::FFDCData& pelAdditionalData)
129 {
130 const auto& ffdcFileList = sbeError.getFfdcFileList();
131 std::vector<uint32_t> logIdList;
132 for (const auto& [slid, ffdcTuple] : ffdcFileList)
133 {
134 uint8_t severity;
135 int fd;
136 std::filesystem::path path;
137 std::tie(severity, fd, path) = ffdcTuple;
138
139 Severity logSeverity = convertSeverityToEnum(severity);
140
141 if (logSeverity != openpower::dump::pel::Severity::Informational)
142 {
143 lg2::info(
144 "Changing severity from {SEV_ORIG} to informational in the "
145 "dumping path",
146 "SEV_ORIG", logSeverity);
147 logSeverity = openpower::dump::pel::Severity::Informational;
148 }
149
150 PELFFDCInfo pelFFDCInfo;
151 pelFFDCInfo.emplace_back(
152 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
153 Create::FFDCFormat::Custom,
154 FFDC_FORMAT_SUBTYPE, FFDC_FORMAT_VERSION, fd));
155
156 auto logId = openpower::dump::pel::createSbeErrorPEL(
157 event, sbeError, pelAdditionalData, logSeverity);
158 lg2::info("Logged PEL {PELID} for SLID {SLID}", "PELID", logId, "SLID",
159 slid);
160 logIdList.push_back(logId);
161 }
162 return logIdList;
163 }
164
getLogInfo(uint32_t logId)165 std::tuple<uint32_t, std::string> getLogInfo(uint32_t logId)
166 {
167 uint32_t pelId;
168 std::string src;
169 try
170 {
171 auto bus = sdbusplus::bus::new_default();
172 const auto loggingEntryObjectPath =
173 std::string(loggingObjectPath) + "/entry/" + std::to_string(logId);
174 auto service =
175 util::getService(bus, entryInterface, loggingEntryObjectPath);
176 auto method =
177 bus.new_method_call(service.c_str(), loggingEntryObjectPath.c_str(),
178 "org.freedesktop.DBus.Properties", "Get");
179 method.append(opEntryInterface);
180 method.append("PlatformLogID");
181 auto response = bus.call(method);
182 std::variant<uint32_t, std::string> v;
183 response.read(v);
184 pelId = std::get<uint32_t>(v);
185 method =
186 bus.new_method_call(service.c_str(), loggingEntryObjectPath.c_str(),
187 "org.freedesktop.DBus.Properties", "Get");
188 method.append(entryInterface);
189 method.append("EventId");
190 response = bus.call(method);
191 response.read(v);
192 std::istringstream iss(std::get<std::string>(v));
193 iss >> src;
194 }
195 catch (const sdbusplus::exception_t& e)
196 {
197 lg2::error("D-Bus call exception "
198 "EXCEPTION={ERROR}",
199 "ERROR", e);
200 throw;
201 }
202 catch (const std::exception& e)
203 {
204 throw;
205 }
206
207 return std::tuple<uint32_t, std::string>(pelId, src);
208 }
209
FFDCFile(const json & pHALCalloutData)210 FFDCFile::FFDCFile(const json& pHALCalloutData) :
211 calloutData(pHALCalloutData.dump()),
212 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
213 {
214 prepareFFDCFile();
215 }
216
~FFDCFile()217 FFDCFile::~FFDCFile()
218 {
219 removeCalloutFile();
220 }
221
getFileFD() const222 int FFDCFile::getFileFD() const
223 {
224 return fileFD;
225 }
226
prepareFFDCFile()227 void FFDCFile::prepareFFDCFile()
228 {
229 createCalloutFile();
230 writeCalloutData();
231 setCalloutFileSeekPos();
232 }
233
createCalloutFile()234 void FFDCFile::createCalloutFile()
235 {
236 fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
237
238 if (fileFD == -1)
239 {
240 lg2::error("Failed to create phalPELCallouts file({FILE}), "
241 "errorno({ERRNO}) and errormsg({ERRORMSG})",
242 "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
243 strerror(errno));
244 throw std::runtime_error("Failed to create phalPELCallouts file");
245 }
246 }
247
writeCalloutData()248 void FFDCFile::writeCalloutData()
249 {
250 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
251
252 if (rc == -1)
253 {
254 lg2::error("Failed to write phaPELCallout info in file({FILE}), "
255 "errorno({ERRNO}), errormsg({ERRORMSG})",
256 "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
257 strerror(errno));
258 throw std::runtime_error("Failed to write phalPELCallouts info");
259 }
260 else if (rc != static_cast<ssize_t>(calloutData.size()))
261 {
262 lg2::warning("Could not write all phal callout info in file({FILE}), "
263 "written byte({WRITTEN}), total byte({TOTAL})",
264 "FILE", calloutFile, "WRITTEN", rc, "TOTAL",
265 calloutData.size());
266 }
267 }
268
setCalloutFileSeekPos()269 void FFDCFile::setCalloutFileSeekPos()
270 {
271 int rc = lseek(fileFD, 0, SEEK_SET);
272
273 if (rc == -1)
274 {
275 lg2::error("Failed to set SEEK_SET for phalPELCallouts in "
276 "file({FILE}), errorno({ERRNO}), errormsg({ERRORMSG})",
277 "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
278 strerror(errno));
279
280 throw std::runtime_error(
281 "Failed to set SEEK_SET for phalPELCallouts file");
282 }
283 }
284
removeCalloutFile()285 void FFDCFile::removeCalloutFile()
286 {
287 close(fileFD);
288 std::remove(calloutFile.c_str());
289 }
290
291 } // namespace openpower::dump::pel
292