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