1 #include "create_pel.hpp"
2
3 #include "attributes_info.H"
4
5 #include "util.hpp"
6
7 #include <fcntl.h>
8 #include <libekb.H>
9 #include <libphal.H>
10 #include <unistd.h>
11
12 #include <phosphor-logging/elog.hpp>
13 #include <xyz/openbmc_project/Logging/Create/server.hpp>
14 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
15
16 #include <cerrno>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <format>
21 #include <map>
22 #include <stdexcept>
23 #include <string>
24 #include <tuple>
25 #include <vector>
26
27 namespace openpower
28 {
29 using namespace phosphor::logging;
30
31 namespace pel
32 {
33
34 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
35 constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";
36 constexpr auto opLoggingInterface = "org.open_power.Logging.PEL";
37
38 /**
39 * @brief get SBE special callout information
40 *
41 * This function add the special sbe callout in the user provided
42 * json callout list. includes BMC0002 procedure callout with
43 * high priority and processor callout with medium priority.
44 *
45 * @param[in] procTarget - pdbg processor target
46 * @param[out] jsonCalloutDataList - reference to json callout list
47 */
getSBECallout(struct pdbg_target * procTarget,json & jsonCalloutDataList)48 static void getSBECallout(struct pdbg_target* procTarget,
49 json& jsonCalloutDataList)
50 {
51 using namespace openpower::phal::pdbg;
52
53 json jsonProcedCallout;
54
55 // Add procedure callout
56 jsonProcedCallout["Procedure"] = "BMC0002";
57 jsonProcedCallout["Priority"] = "H";
58 jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout));
59 try
60 {
61 ATTR_LOCATION_CODE_Type locationCode;
62 // Initialize with default data.
63 memset(&locationCode, '\0', sizeof(locationCode));
64 // Get location code information
65 openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
66 json jsonProcCallout;
67 jsonProcCallout["LocationCode"] = locationCode;
68 jsonProcCallout["Deconfigured"] = false;
69 jsonProcCallout["Guarded"] = false;
70 jsonProcCallout["Priority"] = "M";
71 jsonCalloutDataList.emplace_back(std::move(jsonProcCallout));
72 }
73 catch (const std::exception& e)
74 {
75 log<level::ERR>(std::format("getLocationCode({}): Exception({})",
76 pdbg_target_path(procTarget), e.what())
77 .c_str());
78 }
79 }
80
createErrorPEL(const std::string & event,const json & calloutData,const FFDCData & ffdcData,const Severity severity)81 void createErrorPEL(const std::string& event, const json& calloutData,
82 const FFDCData& ffdcData, const Severity severity)
83 {
84 std::map<std::string, std::string> additionalData;
85 auto bus = sdbusplus::bus::new_default();
86 additionalData.emplace("_PID", std::to_string(getpid()));
87 for (auto& data : ffdcData)
88 {
89 additionalData.emplace(data);
90 }
91
92 try
93 {
94 FFDCFile ffdcFile(calloutData);
95
96 std::vector<std::tuple<sdbusplus::xyz::openbmc_project::Logging::
97 server::Create::FFDCFormat,
98 uint8_t, uint8_t, sdbusplus::message::unix_fd>>
99 pelCalloutInfo;
100
101 pelCalloutInfo.push_back(
102 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
103 Create::FFDCFormat::JSON,
104 static_cast<uint8_t>(0xCA),
105 static_cast<uint8_t>(0x01), ffdcFile.getFileFD()));
106
107 std::string service =
108 util::getService(bus, loggingObjectPath, loggingInterface);
109 auto method =
110 bus.new_method_call(service.c_str(), loggingObjectPath,
111 loggingInterface, "CreateWithFFDCFiles");
112 auto level =
113 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
114 severity);
115 method.append(event, level, additionalData, pelCalloutInfo);
116 auto resp = bus.call(method);
117 }
118 catch (const sdbusplus::exception_t& e)
119 {
120 log<level::ERR>(
121 std::format("D-Bus call exception",
122 "OBJPATH={}, INTERFACE={}, event={}, EXCEPTION={}",
123 loggingObjectPath, loggingInterface, event, e.what())
124 .c_str());
125 throw std::runtime_error(
126 "Error in invoking D-Bus logging create interface");
127 }
128 catch (const std::exception& e)
129 {
130 throw e;
131 }
132 }
133
createSbeErrorPEL(const std::string & event,const sbeError_t & sbeError,const FFDCData & ffdcData,struct pdbg_target * procTarget,const Severity severity)134 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
135 const FFDCData& ffdcData,
136 struct pdbg_target* procTarget,
137 const Severity severity)
138 {
139 uint32_t plid = 0;
140 std::map<std::string, std::string> additionalData;
141 auto bus = sdbusplus::bus::new_default();
142
143 additionalData.emplace("_PID", std::to_string(getpid()));
144 additionalData.emplace("SBE_ERR_MSG", sbeError.what());
145
146 for (auto& data : ffdcData)
147 {
148 additionalData.emplace(data);
149 }
150
151 std::vector<std::tuple<
152 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
153 uint8_t, uint8_t, sdbusplus::message::unix_fd>>
154 pelFFDCInfo;
155
156 // get SBE ffdc file descriptor
157 auto fd = sbeError.getFd();
158
159 // Negative fd value indicates error case or invalid file
160 // No need of special processing , just log error with additional ffdc.
161 if (fd > 0)
162 {
163 // Refer phosphor-logging/extensions/openpower-pels/README.md section
164 // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
165 // for details of related to createPEL with SBE FFDC information
166 // usin g CreateWithFFDCFiles api.
167 pelFFDCInfo.push_back(
168 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
169 Create::FFDCFormat::Custom,
170 static_cast<uint8_t>(0xCB),
171 static_cast<uint8_t>(0x01), sbeError.getFd()));
172 }
173
174 // Workaround : currently sbe_extract_rc hwp procedure based callout
175 // handling is not available. openbmc issue #2917
176 // As per discussion with RAS team adding additional callout for
177 // SBE timeout error case, till this hwp based error handling in place.
178 // Note: PEL needs ffdcFile file till pel creation. This is forced to
179 // define ffdcFile function level scope.
180 std::unique_ptr<FFDCFile> FFDCFilePtr;
181 try
182 {
183 if ((event == "org.open_power.Processor.Error.SbeBootTimeout") &&
184 (severity == Severity::Error))
185 {
186 json jsonCalloutDataList;
187 jsonCalloutDataList = json::array();
188 getSBECallout(procTarget, jsonCalloutDataList);
189 FFDCFilePtr = std::make_unique<FFDCFile>(jsonCalloutDataList);
190 pelFFDCInfo.push_back(std::make_tuple(
191 sdbusplus::xyz::openbmc_project::Logging::server::Create::
192 FFDCFormat::JSON,
193 static_cast<uint8_t>(0xCA), static_cast<uint8_t>(0x01),
194 FFDCFilePtr->getFileFD()));
195 }
196 }
197 catch (const std::exception& e)
198 {
199 log<level::ERR>(
200 std::format("Skipping SBE special callout due to Exception({})",
201 e.what())
202 .c_str());
203 }
204
205 try
206 {
207 std::string service =
208 util::getService(bus, loggingObjectPath, opLoggingInterface);
209 auto method =
210 bus.new_method_call(service.c_str(), loggingObjectPath,
211 opLoggingInterface, "CreatePELWithFFDCFiles");
212 auto level =
213 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
214 severity);
215 method.append(event, level, additionalData, pelFFDCInfo);
216 auto response = bus.call(method);
217
218 // reply will be tuple containing bmc log id, platform log id
219 std::tuple<uint32_t, uint32_t> reply = {0, 0};
220
221 // parse dbus response into reply
222 response.read(reply);
223 plid = std::get<1>(reply); // platform log id is tuple "second"
224 }
225 catch (const sdbusplus::exception_t& e)
226 {
227 log<level::ERR>(
228 std::format("D-Bus call exception",
229 "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
230 loggingObjectPath, loggingInterface, e.what())
231 .c_str());
232 throw std::runtime_error(
233 "Error in invoking D-Bus logging create interface");
234 }
235 catch (const std::exception& e)
236 {
237 throw e;
238 }
239 return plid;
240 }
241
createPEL(const std::string & event,const FFDCData & ffdcData,const Severity severity)242 void createPEL(const std::string& event, const FFDCData& ffdcData,
243 const Severity severity)
244 {
245 std::map<std::string, std::string> additionalData;
246 auto bus = sdbusplus::bus::new_default();
247
248 additionalData.emplace("_PID", std::to_string(getpid()));
249 for (auto& data : ffdcData)
250 {
251 additionalData.emplace(data);
252 }
253
254 try
255 {
256 std::string service =
257 util::getService(bus, loggingObjectPath, loggingInterface);
258 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
259 loggingInterface, "Create");
260 auto level =
261 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
262 severity);
263 method.append(event, level, additionalData);
264 auto resp = bus.call(method);
265 }
266 catch (const sdbusplus::exception_t& e)
267 {
268 log<level::ERR>(
269 std::format("sdbusplus D-Bus call exception",
270 "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
271 loggingObjectPath, loggingInterface, e.what())
272 .c_str());
273 ;
274
275 throw std::runtime_error(
276 "Error in invoking D-Bus logging create interface");
277 }
278 catch (const std::exception& e)
279 {
280 log<level::ERR>(
281 std::format("D-bus call exception", "EXCEPTION={}", e.what())
282 .c_str());
283 throw e;
284 }
285 }
286
FFDCFile(const json & pHALCalloutData)287 FFDCFile::FFDCFile(const json& pHALCalloutData) :
288 calloutData(pHALCalloutData.dump()),
289 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
290 {
291 prepareFFDCFile();
292 }
293
~FFDCFile()294 FFDCFile::~FFDCFile()
295 {
296 removeCalloutFile();
297 }
298
getFileFD() const299 int FFDCFile::getFileFD() const
300 {
301 return fileFD;
302 }
303
prepareFFDCFile()304 void FFDCFile::prepareFFDCFile()
305 {
306 createCalloutFile();
307 writeCalloutData();
308 setCalloutFileSeekPos();
309 }
310
createCalloutFile()311 void FFDCFile::createCalloutFile()
312 {
313 fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
314
315 if (fileFD == -1)
316 {
317 log<level::ERR>(std::format("Failed to create phalPELCallouts "
318 "file({}), errorno({}) and errormsg({})",
319 calloutFile, errno, strerror(errno))
320 .c_str());
321 throw std::runtime_error("Failed to create phalPELCallouts file");
322 }
323 }
324
writeCalloutData()325 void FFDCFile::writeCalloutData()
326 {
327 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
328
329 if (rc == -1)
330 {
331 log<level::ERR>(std::format("Failed to write phaPELCallout info "
332 "in file({}), errorno({}), errormsg({})",
333 calloutFile, errno, strerror(errno))
334 .c_str());
335 throw std::runtime_error("Failed to write phalPELCallouts info");
336 }
337 else if (rc != static_cast<ssize_t>(calloutData.size()))
338 {
339 log<level::WARNING>(std::format("Could not write all phal callout "
340 "info in file({}), written byte({}) "
341 "and total byte({})",
342 calloutFile, rc, calloutData.size())
343 .c_str());
344 }
345 }
346
setCalloutFileSeekPos()347 void FFDCFile::setCalloutFileSeekPos()
348 {
349 int rc = lseek(fileFD, 0, SEEK_SET);
350
351 if (rc == -1)
352 {
353 log<level::ERR>(std::format("Failed to set SEEK_SET for "
354 "phalPELCallouts in file({}), errorno({}) "
355 "and errormsg({})",
356 calloutFile, errno, strerror(errno))
357 .c_str());
358 throw std::runtime_error(
359 "Failed to set SEEK_SET for phalPELCallouts file");
360 }
361 }
362
removeCalloutFile()363 void FFDCFile::removeCalloutFile()
364 {
365 close(fileFD);
366 std::remove(calloutFile.c_str());
367 }
368
369 } // namespace pel
370 } // namespace openpower
371