1 #include "create_pel.hpp"
2 
3 #include "attributes_info.H"
4 
5 #include "util.hpp"
6 
7 #include <fcntl.h>
8 #include <fmt/format.h>
9 #include <libekb.H>
10 #include <libphal.H>
11 #include <unistd.h>
12 
13 #include <phosphor-logging/elog.hpp>
14 #include <xyz/openbmc_project/Logging/Create/server.hpp>
15 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
16 
17 #include <cerrno>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
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  */
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>(fmt::format("getLocationCode({}): Exception({})",
76                                     pdbg_target_path(procTarget), e.what())
77                             .c_str());
78     }
79 }
80 
81 void createErrorPEL(const std::string& event, const json& calloutData,
82                     const FFDCData& ffdcData)
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                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
115                     Error);
116         method.append(event, level, additionalData, pelCalloutInfo);
117         auto resp = bus.call(method);
118     }
119     catch (const sdbusplus::exception::exception& e)
120     {
121         log<level::ERR>(
122             fmt::format("D-Bus call exception",
123                         "OBJPATH={}, INTERFACE={}, event={}, EXCEPTION={}",
124                         loggingObjectPath, loggingInterface, event, e.what())
125                 .c_str());
126         throw std::runtime_error(
127             "Error in invoking D-Bus logging create interface");
128     }
129     catch (const std::exception& e)
130     {
131         throw e;
132     }
133 }
134 
135 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
136                            const FFDCData& ffdcData,
137                            struct pdbg_target* procTarget,
138                            const Severity severity)
139 {
140     uint32_t plid = 0;
141     std::map<std::string, std::string> additionalData;
142     auto bus = sdbusplus::bus::new_default();
143 
144     additionalData.emplace("_PID", std::to_string(getpid()));
145     additionalData.emplace("SBE_ERR_MSG", sbeError.what());
146 
147     for (auto& data : ffdcData)
148     {
149         additionalData.emplace(data);
150     }
151 
152     std::vector<std::tuple<
153         sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
154         uint8_t, uint8_t, sdbusplus::message::unix_fd>>
155         pelFFDCInfo;
156 
157     // get SBE ffdc file descriptor
158     auto fd = sbeError.getFd();
159 
160     // Negative fd value indicates error case or invalid file
161     // No need of special processing , just log error with additional ffdc.
162     if (fd > 0)
163     {
164         // Refer phosphor-logging/extensions/openpower-pels/README.md section
165         // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
166         // for details of related to createPEL with SBE FFDC information
167         // usin g CreateWithFFDCFiles api.
168         pelFFDCInfo.push_back(
169             std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
170                                 Create::FFDCFormat::Custom,
171                             static_cast<uint8_t>(0xCB),
172                             static_cast<uint8_t>(0x01), sbeError.getFd()));
173     }
174 
175     // Workaround : currently sbe_extract_rc hwp procedure based callout
176     // handling is not available. openbmc issue #2917
177     // As per discussion with RAS team adding additional callout for
178     // SBE timeout error case, till this hwp based error handling in place.
179     // Note: PEL needs ffdcFile file till pel creation. This is forced to
180     // define ffdcFile function level scope.
181     std::unique_ptr<FFDCFile> FFDCFilePtr;
182     try
183     {
184         if ((event == "org.open_power.Processor.Error.SbeBootTimeout") &&
185             (severity == Severity::Error))
186         {
187             json jsonCalloutDataList;
188             jsonCalloutDataList = json::array();
189             getSBECallout(procTarget, jsonCalloutDataList);
190             FFDCFilePtr = std::make_unique<FFDCFile>(jsonCalloutDataList);
191             pelFFDCInfo.push_back(std::make_tuple(
192                 sdbusplus::xyz::openbmc_project::Logging::server::Create::
193                     FFDCFormat::JSON,
194                 static_cast<uint8_t>(0xCA), static_cast<uint8_t>(0x01),
195                 FFDCFilePtr->getFileFD()));
196         }
197     }
198     catch (const std::exception& e)
199     {
200         log<level::ERR>(
201             fmt::format("Skipping SBE special callout due to Exception({})",
202                         e.what())
203                 .c_str());
204     }
205 
206     try
207     {
208         std::string service =
209             util::getService(bus, loggingObjectPath, opLoggingInterface);
210         auto method =
211             bus.new_method_call(service.c_str(), loggingObjectPath,
212                                 opLoggingInterface, "CreatePELWithFFDCFiles");
213         auto level =
214             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
215                 severity);
216         method.append(event, level, additionalData, pelFFDCInfo);
217         auto response = bus.call(method);
218 
219         // reply will be tuple containing bmc log id, platform log id
220         std::tuple<uint32_t, uint32_t> reply = {0, 0};
221 
222         // parse dbus response into reply
223         response.read(reply);
224         plid = std::get<1>(reply); // platform log id is tuple "second"
225     }
226     catch (const sdbusplus::exception::exception& e)
227     {
228         log<level::ERR>(fmt::format("D-Bus call exception",
229                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
230                                     loggingObjectPath, loggingInterface,
231                                     e.what())
232                             .c_str());
233         throw std::runtime_error(
234             "Error in invoking D-Bus logging create interface");
235     }
236     catch (const std::exception& e)
237     {
238         throw e;
239     }
240     return plid;
241 }
242 
243 void createPEL(const std::string& event, const FFDCData& ffdcData)
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                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
263                     Error);
264         method.append(event, level, additionalData);
265         auto resp = bus.call(method);
266     }
267     catch (const sdbusplus::exception::exception& e)
268     {
269         log<level::ERR>(fmt::format("sdbusplus D-Bus call exception",
270                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
271                                     loggingObjectPath, loggingInterface,
272                                     e.what())
273                             .c_str());
274         ;
275 
276         throw std::runtime_error(
277             "Error in invoking D-Bus logging create interface");
278     }
279     catch (const std::exception& e)
280     {
281         log<level::ERR>(
282             fmt::format("D-bus call exception", "EXCEPTION={}", e.what())
283                 .c_str());
284         throw e;
285     }
286 }
287 
288 FFDCFile::FFDCFile(const json& pHALCalloutData) :
289     calloutData(pHALCalloutData.dump()),
290     calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
291 {
292     prepareFFDCFile();
293 }
294 
295 FFDCFile::~FFDCFile()
296 {
297     removeCalloutFile();
298 }
299 
300 int FFDCFile::getFileFD() const
301 {
302     return fileFD;
303 }
304 
305 void FFDCFile::prepareFFDCFile()
306 {
307     createCalloutFile();
308     writeCalloutData();
309     setCalloutFileSeekPos();
310 }
311 
312 void FFDCFile::createCalloutFile()
313 {
314     fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
315 
316     if (fileFD == -1)
317     {
318         log<level::ERR>(fmt::format("Failed to create phalPELCallouts "
319                                     "file({}), errorno({}) and errormsg({})",
320                                     calloutFile, errno, strerror(errno))
321                             .c_str());
322         throw std::runtime_error("Failed to create phalPELCallouts file");
323     }
324 }
325 
326 void FFDCFile::writeCalloutData()
327 {
328     ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
329 
330     if (rc == -1)
331     {
332         log<level::ERR>(fmt::format("Failed to write phaPELCallout info "
333                                     "in file({}), errorno({}), errormsg({})",
334                                     calloutFile, errno, strerror(errno))
335                             .c_str());
336         throw std::runtime_error("Failed to write phalPELCallouts info");
337     }
338     else if (rc != static_cast<ssize_t>(calloutData.size()))
339     {
340         log<level::WARNING>(fmt::format("Could not write all phal callout "
341                                         "info in file({}), written byte({}) "
342                                         "and total byte({})",
343                                         calloutFile, rc, calloutData.size())
344                                 .c_str());
345     }
346 }
347 
348 void FFDCFile::setCalloutFileSeekPos()
349 {
350     int rc = lseek(fileFD, 0, SEEK_SET);
351 
352     if (rc == -1)
353     {
354         log<level::ERR>(fmt::format("Failed to set SEEK_SET for "
355                                     "phalPELCallouts in file({}), errorno({}) "
356                                     "and errormsg({})",
357                                     calloutFile, errno, strerror(errno))
358                             .c_str());
359         throw std::runtime_error(
360             "Failed to set SEEK_SET for phalPELCallouts file");
361     }
362 }
363 
364 void FFDCFile::removeCalloutFile()
365 {
366     close(fileFD);
367     std::remove(calloutFile.c_str());
368 }
369 
370 } // namespace pel
371 } // namespace openpower
372