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