1 #include "config.h"
2 
3 #include "file_io_type_pel.hpp"
4 
5 #include "libpldm/base.h"
6 #include "oem/ibm/libpldm/file_io.h"
7 
8 #include "common/utils.hpp"
9 #include "xyz/openbmc_project/Common/error.hpp"
10 
11 #include <stdint.h>
12 #include <systemd/sd-bus.h>
13 #include <unistd.h>
14 
15 #include <sdbusplus/server.hpp>
16 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
17 
18 #include <exception>
19 #include <filesystem>
20 #include <fstream>
21 #include <iostream>
22 #include <vector>
23 
24 namespace pldm
25 {
26 namespace responder
27 {
28 
29 using namespace sdbusplus::xyz::openbmc_project::Logging::server;
30 
31 namespace detail
32 {
33 
34 /**
35  * @brief Finds the Entry::Level value for the severity of the PEL
36  *        passed in.
37  *
38  * The severity byte is at offset 10 in the User Header section,
39  * which is always after the 48 byte Private Header section.
40  *
41  * @param[in] pelFileName - The file containing the PEL
42  *
43  * @return Entry::Level - The severity value for the Entry
44  */
45 Entry::Level getEntryLevelFromPEL(const std::string& pelFileName)
46 {
47     const std::map<uint8_t, Entry::Level> severityMap{
48         {0x00, Entry::Level::Informational}, // Informational event
49         {0x10, Entry::Level::Warning},       // Recoverable error
50         {0x20, Entry::Level::Warning},       // Predictive error
51         {0x40, Entry::Level::Error},         // Unrecoverable error
52         {0x50, Entry::Level::Error},         // Critical error
53         {0x60, Entry::Level::Error},         // Error from a diagnostic test
54         {0x70, Entry::Level::Warning}        // Recoverable symptom
55     };
56 
57     const size_t severityOffset = 0x3A;
58 
59     size_t size = 0;
60     if (fs::exists(pelFileName))
61     {
62         size = fs::file_size(pelFileName);
63     }
64 
65     if (size > severityOffset)
66     {
67         std::ifstream pel{pelFileName};
68         if (pel.good())
69         {
70             pel.seekg(severityOffset);
71 
72             uint8_t sev;
73             pel.read(reinterpret_cast<char*>(&sev), 1);
74 
75             // Get the type
76             sev = sev & 0xF0;
77 
78             auto entry = severityMap.find(sev);
79             if (entry != severityMap.end())
80             {
81                 return entry->second;
82             }
83         }
84         else
85         {
86             std::cerr << "Unable to open PEL file " << pelFileName << "\n";
87         }
88     }
89 
90     return Entry::Level::Error;
91 }
92 } // namespace detail
93 
94 int PelHandler::readIntoMemory(uint32_t offset, uint32_t& length,
95                                uint64_t address,
96                                oem_platform::Handler* /*oemPlatformHandler*/)
97 {
98     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
99     static constexpr auto logInterface = "org.open_power.Logging.PEL";
100 
101     auto& bus = pldm::utils::DBusHandler::getBus();
102 
103     try
104     {
105         auto service =
106             pldm::utils::DBusHandler().getService(logObjPath, logInterface);
107         auto method = bus.new_method_call(service.c_str(), logObjPath,
108                                           logInterface, "GetPEL");
109         method.append(fileHandle);
110         auto reply = bus.call(method);
111         sdbusplus::message::unix_fd fd{};
112         reply.read(fd);
113         auto rc = transferFileData(fd, true, offset, length, address);
114         return rc;
115     }
116     catch (const std::exception& e)
117     {
118         std::cerr << "GetPEL D-Bus call failed, PEL id = " << fileHandle
119                   << ", error = " << e.what() << "\n";
120         return PLDM_ERROR;
121     }
122 
123     return PLDM_SUCCESS;
124 }
125 
126 int PelHandler::read(uint32_t offset, uint32_t& length, Response& response,
127                      oem_platform::Handler* /*oemPlatformHandler*/)
128 {
129     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
130     static constexpr auto logInterface = "org.open_power.Logging.PEL";
131     auto& bus = pldm::utils::DBusHandler::getBus();
132 
133     try
134     {
135         auto service =
136             pldm::utils::DBusHandler().getService(logObjPath, logInterface);
137         auto method = bus.new_method_call(service.c_str(), logObjPath,
138                                           logInterface, "GetPEL");
139         method.append(fileHandle);
140         auto reply = bus.call(method);
141         sdbusplus::message::unix_fd fd{};
142         reply.read(fd);
143 
144         off_t fileSize = lseek(fd, 0, SEEK_END);
145         if (fileSize == -1)
146         {
147             std::cerr << "file seek failed";
148             return PLDM_ERROR;
149         }
150         if (offset >= fileSize)
151         {
152             std::cerr << "Offset exceeds file size, OFFSET=" << offset
153                       << " FILE_SIZE=" << fileSize << std::endl;
154             return PLDM_DATA_OUT_OF_RANGE;
155         }
156         if (offset + length > fileSize)
157         {
158             length = fileSize - offset;
159         }
160         auto rc = lseek(fd, offset, SEEK_SET);
161         if (rc == -1)
162         {
163             std::cerr << "file seek failed";
164             return PLDM_ERROR;
165         }
166         size_t currSize = response.size();
167         response.resize(currSize + length);
168         auto filePos = reinterpret_cast<char*>(response.data());
169         filePos += currSize;
170         rc = ::read(fd, filePos, length);
171         if (rc == -1)
172         {
173             std::cerr << "file read failed";
174             return PLDM_ERROR;
175         }
176         if (rc != length)
177         {
178             std::cerr << "mismatch between number of characters to read and "
179                       << "the length read, LENGTH=" << length << " COUNT=" << rc
180                       << std::endl;
181             return PLDM_ERROR;
182         }
183     }
184     catch (const std::exception& e)
185     {
186         std::cerr << "GetPEL D-Bus call failed";
187         return PLDM_ERROR;
188     }
189     return PLDM_SUCCESS;
190 }
191 
192 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length,
193                                 uint64_t address,
194                                 oem_platform::Handler* /*oemPlatformHandler*/)
195 {
196     char tmpFile[] = "/tmp/pel.XXXXXX";
197     int fd = mkstemp(tmpFile);
198     if (fd == -1)
199     {
200         std::cerr << "failed to create a temporary pel, ERROR=" << errno
201                   << "\n";
202         return PLDM_ERROR;
203     }
204     close(fd);
205     fs::path path(tmpFile);
206 
207     auto rc = transferFileData(path, false, offset, length, address);
208     if (rc == PLDM_SUCCESS)
209     {
210         rc = storePel(path.string());
211     }
212     return rc;
213 }
214 
215 int PelHandler::fileAck(uint8_t /*fileStatus*/)
216 {
217     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
218     static constexpr auto logInterface = "org.open_power.Logging.PEL";
219     auto& bus = pldm::utils::DBusHandler::getBus();
220 
221     try
222     {
223         auto service =
224             pldm::utils::DBusHandler().getService(logObjPath, logInterface);
225         auto method = bus.new_method_call(service.c_str(), logObjPath,
226                                           logInterface, "HostAck");
227         method.append(fileHandle);
228         bus.call_noreply(method);
229     }
230     catch (const std::exception& e)
231     {
232         std::cerr << "HostAck D-Bus call failed";
233         return PLDM_ERROR;
234     }
235 
236     return PLDM_SUCCESS;
237 }
238 
239 int PelHandler::storePel(std::string&& pelFileName)
240 {
241     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
242     static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create";
243 
244     auto& bus = pldm::utils::DBusHandler::getBus();
245 
246     try
247     {
248         auto service =
249             pldm::utils::DBusHandler().getService(logObjPath, logInterface);
250         using namespace sdbusplus::xyz::openbmc_project::Logging::server;
251         std::map<std::string, std::string> addlData{};
252         auto severity =
253             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
254                 detail::getEntryLevelFromPEL(pelFileName));
255         addlData.emplace("RAWPEL", std::move(pelFileName));
256 
257         auto method = bus.new_method_call(service.c_str(), logObjPath,
258                                           logInterface, "Create");
259         method.append("xyz.openbmc_project.Host.Error.Event", severity,
260                       addlData);
261         bus.call_noreply(method);
262     }
263     catch (const std::exception& e)
264     {
265         std::cerr << "failed to make a d-bus call to PEL daemon, ERROR="
266                   << e.what() << "\n";
267         return PLDM_ERROR;
268     }
269 
270     return PLDM_SUCCESS;
271 }
272 
273 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length,
274                       oem_platform::Handler* /*oemPlatformHandler*/)
275 {
276     int rc = PLDM_SUCCESS;
277 
278     if (offset > 0)
279     {
280         std::cerr << "Offset is non zero \n";
281         return PLDM_ERROR;
282     }
283 
284     char tmpFile[] = "/tmp/pel.XXXXXX";
285     auto fd = mkstemp(tmpFile);
286     if (fd == -1)
287     {
288         std::cerr << "failed to create a temporary pel, ERROR=" << errno
289                   << "\n";
290         return PLDM_ERROR;
291     }
292 
293     size_t written = 0;
294     do
295     {
296         if ((rc = ::write(fd, buffer, length - written)) == -1)
297         {
298             break;
299         }
300         written += rc;
301         buffer += rc;
302     } while (rc && written < length);
303     close(fd);
304 
305     if (rc == -1)
306     {
307         std::cerr << "file write failed, ERROR=" << errno
308                   << ", LENGTH=" << length << ", OFFSET=" << offset << "\n";
309         fs::remove(tmpFile);
310         return PLDM_ERROR;
311     }
312 
313     if (written == length)
314     {
315         fs::path path(tmpFile);
316         rc = storePel(path.string());
317         if (rc != PLDM_SUCCESS)
318         {
319             std::cerr << "save PEL failed, ERROR = " << rc
320                       << "tmpFile = " << tmpFile << "\n";
321         }
322     }
323 
324     return rc;
325 }
326 
327 } // namespace responder
328 } // namespace pldm
329