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