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