1 #include "create_pel.hpp"
2 
3 #include "util.hpp"
4 
5 #include <fcntl.h>
6 #include <fmt/format.h>
7 #include <libekb.H>
8 #include <unistd.h>
9 
10 #include <phosphor-logging/elog.hpp>
11 #include <xyz/openbmc_project/Logging/Create/server.hpp>
12 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
13 
14 #include <cerrno>
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstring>
18 #include <map>
19 #include <stdexcept>
20 #include <string>
21 #include <tuple>
22 #include <vector>
23 
24 namespace openpower
25 {
26 using namespace phosphor::logging;
27 
28 namespace pel
29 {
30 
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 
35 void createBootErrorPEL(const FFDCData& ffdcData, const json& calloutData)
36 {
37     std::map<std::string, std::string> additionalData;
38     auto bus = sdbusplus::bus::new_default();
39     additionalData.emplace("_PID", std::to_string(getpid()));
40     for (auto& data : ffdcData)
41     {
42         additionalData.emplace(data);
43     }
44 
45     try
46     {
47         FFDCFile ffdcFile(calloutData);
48 
49         std::vector<std::tuple<sdbusplus::xyz::openbmc_project::Logging::
50                                    server::Create::FFDCFormat,
51                                uint8_t, uint8_t, sdbusplus::message::unix_fd>>
52             pelCalloutInfo;
53 
54         pelCalloutInfo.push_back(
55             std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
56                                 Create::FFDCFormat::JSON,
57                             static_cast<uint8_t>(0xCA),
58                             static_cast<uint8_t>(0x01), ffdcFile.getFileFD()));
59 
60         static constexpr auto bootErrorMessage =
61             "org.open_power.PHAL.Error.Boot";
62         std::string service =
63             util::getService(bus, loggingObjectPath, loggingInterface);
64         auto method =
65             bus.new_method_call(service.c_str(), loggingObjectPath,
66                                 loggingInterface, "CreateWithFFDCFiles");
67         auto level =
68             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
69                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
70                     Error);
71         method.append(bootErrorMessage, level, additionalData, pelCalloutInfo);
72         auto resp = bus.call(method);
73     }
74     catch (const sdbusplus::exception::exception& e)
75     {
76         log<level::ERR>("D-Bus call exception",
77                         entry("OBJPATH=%s", loggingObjectPath),
78                         entry("INTERFACE=%s", loggingInterface),
79                         entry("EXCEPTION=%s", e.what()));
80 
81         throw std::runtime_error(
82             "Error in invoking D-Bus logging create interface");
83     }
84     catch (const std::exception& e)
85     {
86         throw e;
87     }
88 }
89 
90 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
91                            const FFDCData& ffdcData)
92 {
93     uint32_t plid = 0;
94     std::map<std::string, std::string> additionalData;
95     auto bus = sdbusplus::bus::new_default();
96 
97     additionalData.emplace("_PID", std::to_string(getpid()));
98     additionalData.emplace("SBE_ERR_MSG", sbeError.what());
99 
100     for (auto& data : ffdcData)
101     {
102         additionalData.emplace(data);
103     }
104 
105     std::vector<std::tuple<
106         sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
107         uint8_t, uint8_t, sdbusplus::message::unix_fd>>
108         pelFFDCInfo;
109 
110     // get SBE ffdc file descriptor
111     auto fd = sbeError.getFd();
112 
113     // Negative fd value indicates error case or invalid file
114     // No need of special processing , just log error with additional ffdc.
115     if (fd > 0)
116     {
117         // Refer phosphor-logging/extensions/openpower-pels/README.md section
118         // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
119         // for details of related to createPEL with SBE FFDC information
120         // usin g CreateWithFFDCFiles api.
121         pelFFDCInfo.push_back(
122             std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
123                                 Create::FFDCFormat::Custom,
124                             static_cast<uint8_t>(0xCB),
125                             static_cast<uint8_t>(0x01), sbeError.getFd()));
126     }
127     try
128     {
129         std::string service =
130             util::getService(bus, loggingObjectPath, opLoggingInterface);
131         auto method =
132             bus.new_method_call(service.c_str(), loggingObjectPath,
133                                 opLoggingInterface, "CreatePELWithFFDCFiles");
134         auto level =
135             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
136                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
137                     Error);
138         method.append(event, level, additionalData, pelFFDCInfo);
139         auto response = bus.call(method);
140 
141         // reply will be tuple containing bmc log id, platform log id
142         std::tuple<uint32_t, uint32_t> reply = {0, 0};
143 
144         // parse dbus response into reply
145         response.read(reply);
146         plid = std::get<1>(reply); // platform log id is tuple "second"
147     }
148     catch (const sdbusplus::exception::exception& e)
149     {
150         log<level::ERR>(fmt::format("D-Bus call exception",
151                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
152                                     loggingObjectPath, loggingInterface,
153                                     e.what())
154                             .c_str());
155         throw std::runtime_error(
156             "Error in invoking D-Bus logging create interface");
157     }
158     catch (const std::exception& e)
159     {
160         throw e;
161     }
162 
163     return plid;
164 }
165 
166 void createPEL(const std::string& event, const FFDCData& ffdcData)
167 {
168     std::map<std::string, std::string> additionalData;
169     auto bus = sdbusplus::bus::new_default();
170 
171     additionalData.emplace("_PID", std::to_string(getpid()));
172     for (auto& data : ffdcData)
173     {
174         additionalData.emplace(data);
175     }
176 
177     try
178     {
179         std::string service =
180             util::getService(bus, loggingObjectPath, loggingInterface);
181         auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
182                                           loggingInterface, "Create");
183         auto level =
184             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
185                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
186                     Error);
187         method.append(event, level, additionalData);
188         auto resp = bus.call(method);
189     }
190     catch (const sdbusplus::exception::exception& e)
191     {
192         log<level::ERR>(fmt::format("sdbusplus D-Bus call exception",
193                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
194                                     loggingObjectPath, loggingInterface,
195                                     e.what())
196                             .c_str());
197         ;
198 
199         throw std::runtime_error(
200             "Error in invoking D-Bus logging create interface");
201     }
202     catch (const std::exception& e)
203     {
204         log<level::ERR>(
205             fmt::format("D-bus call exception", "EXCEPTION={}", e.what())
206                 .c_str());
207         throw e;
208     }
209 }
210 
211 FFDCFile::FFDCFile(const json& pHALCalloutData) :
212     calloutData(pHALCalloutData.dump()),
213     calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
214 {
215     prepareFFDCFile();
216 }
217 
218 FFDCFile::~FFDCFile()
219 {
220     removeCalloutFile();
221 }
222 
223 int FFDCFile::getFileFD() const
224 {
225     return fileFD;
226 }
227 
228 void FFDCFile::prepareFFDCFile()
229 {
230     createCalloutFile();
231     writeCalloutData();
232     setCalloutFileSeekPos();
233 }
234 
235 void FFDCFile::createCalloutFile()
236 {
237     fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
238 
239     if (fileFD == -1)
240     {
241         log<level::ERR>(fmt::format("Failed to create phalPELCallouts "
242                                     "file({}), errorno({}) and errormsg({})",
243                                     calloutFile, errno, strerror(errno))
244                             .c_str());
245         throw std::runtime_error("Failed to create phalPELCallouts file");
246     }
247 }
248 
249 void FFDCFile::writeCalloutData()
250 {
251     ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
252 
253     if (rc == -1)
254     {
255         log<level::ERR>(fmt::format("Failed to write phaPELCallout info "
256                                     "in file({}), errorno({}), errormsg({})",
257                                     calloutFile, errno, strerror(errno))
258                             .c_str());
259         throw std::runtime_error("Failed to write phalPELCallouts info");
260     }
261     else if (rc != static_cast<ssize_t>(calloutData.size()))
262     {
263         log<level::WARNING>(fmt::format("Could not write all phal callout "
264                                         "info in file({}), written byte({}) "
265                                         "and total byte({})",
266                                         calloutFile, rc, calloutData.size())
267                                 .c_str());
268     }
269 }
270 
271 void FFDCFile::setCalloutFileSeekPos()
272 {
273     int rc = lseek(fileFD, 0, SEEK_SET);
274 
275     if (rc == -1)
276     {
277         log<level::ERR>(fmt::format("Failed to set SEEK_SET for "
278                                     "phalPELCallouts in file({}), errorno({}) "
279                                     "and errormsg({})",
280                                     calloutFile, errno, strerror(errno))
281                             .c_str());
282         throw std::runtime_error(
283             "Failed to set SEEK_SET for phalPELCallouts file");
284     }
285 }
286 
287 void FFDCFile::removeCalloutFile()
288 {
289     close(fileFD);
290     std::remove(calloutFile.c_str());
291 }
292 
293 } // namespace pel
294 } // namespace openpower
295