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
createSbeErrorPEL(const std::string & event,const sbeError_t & sbeError,const FFDCData & ffdcData,const Severity severity,const std::optional<PELFFDCInfo> & pelFFDCInfoOpt)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 // using 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 =
74 util::getService(bus, opLoggingInterface, loggingObjectPath);
75 auto method =
76 bus.new_method_call(service.c_str(), loggingObjectPath,
77 opLoggingInterface, "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
convertSeverityToEnum(uint8_t severity)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
processFFDCPackets(const openpower::phal::sbeError_t & sbeError,const std::string & event,openpower::dump::pel::FFDCData & pelAdditionalData)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
FFDCFile(const json & pHALCalloutData)160 FFDCFile::FFDCFile(const json& pHALCalloutData) :
161 calloutData(pHALCalloutData.dump()),
162 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
163 {
164 prepareFFDCFile();
165 }
166
~FFDCFile()167 FFDCFile::~FFDCFile()
168 {
169 removeCalloutFile();
170 }
171
getFileFD() const172 int FFDCFile::getFileFD() const
173 {
174 return fileFD;
175 }
176
prepareFFDCFile()177 void FFDCFile::prepareFFDCFile()
178 {
179 createCalloutFile();
180 writeCalloutData();
181 setCalloutFileSeekPos();
182 }
183
createCalloutFile()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
writeCalloutData()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
setCalloutFileSeekPos()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
removeCalloutFile()235 void FFDCFile::removeCalloutFile()
236 {
237 close(fileFD);
238 std::remove(calloutFile.c_str());
239 }
240
241 } // namespace openpower::dump::pel
242