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/oem/ibm/file_io.h>
8 #include <stdint.h>
9 #include <systemd/sd-bus.h>
10 #include <unistd.h>
11 
12 #include <org/open_power/Logging/PEL/server.hpp>
13 #include <phosphor-logging/lg2.hpp>
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 <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 using namespace sdbusplus::org::open_power::Logging::server;
30 
31 namespace detail
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  */
getEntryLevelFromPEL(const std::string & pelFileName)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             error("Unable to open PEL file '{FILE}'", "FILE", pelFileName);
86         }
87     }
88 
89     return Entry::Level::Error;
90 }
91 } // namespace detail
92 
readIntoMemory(uint32_t offset,uint32_t length,uint64_t address,oem_platform::Handler *)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(method, dbusTimeout);
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         error(
118             "Failed to get PEL D-Bus call for PEL ID '{FILE_HANDLE}', error - {ERROR}",
119             "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e);
120         return PLDM_ERROR;
121     }
122 
123     return PLDM_SUCCESS;
124 }
125 
read(uint32_t offset,uint32_t & length,Response & response,oem_platform::Handler *)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 = pldm::utils::DBusHandler().getService(logObjPath,
136                                                              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, dbusTimeout);
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             error("File lseek failed");
148             return PLDM_ERROR;
149         }
150         if (offset >= fileSize)
151         {
152             error(
153                 "Offset '{OFFSET}' exceeds file size {SIZE}, file handle {FILE_HANDLE}",
154                 "OFFSET", offset, "SIZE", fileSize, "FILE_HANDLE", fileHandle);
155             return PLDM_DATA_OUT_OF_RANGE;
156         }
157         if (offset + length > fileSize)
158         {
159             length = fileSize - offset;
160         }
161         auto rc = lseek(fd, offset, SEEK_SET);
162         if (rc == -1)
163         {
164             error("Failed to do file lseek at offset '{OFFSET}'", "OFFSET",
165                   offset);
166             return PLDM_ERROR;
167         }
168         size_t currSize = response.size();
169         response.resize(currSize + length);
170         auto filePos = reinterpret_cast<char*>(response.data());
171         filePos += currSize;
172         rc = ::read(fd, filePos, length);
173         if (rc == -1)
174         {
175             error(
176                 "Failed to do file read of length '{LENGTH}' at offset '{OFFSET}'",
177                 "LENGTH", length, "OFFSET", filePos);
178             return PLDM_ERROR;
179         }
180         if (rc != length)
181         {
182             error(
183                 "Mismatch between number of characters to read and the read length '{LENGTH}' and count '{RC}'",
184                 "LENGTH", length, "RC", rc);
185             return PLDM_ERROR;
186         }
187     }
188     catch (const std::exception& e)
189     {
190         error(
191             "Failed to get PEL D-Bus call on PEL ID {FILE_HANDLE}, error - {ERROR}",
192             "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e);
193         return PLDM_ERROR;
194     }
195     return PLDM_SUCCESS;
196 }
197 
writeFromMemory(uint32_t offset,uint32_t length,uint64_t address,oem_platform::Handler *)198 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length,
199                                 uint64_t address,
200                                 oem_platform::Handler* /*oemPlatformHandler*/)
201 {
202     char tmpFile[] = "/tmp/pel.XXXXXX";
203     int fd = mkstemp(tmpFile);
204     if (fd == -1)
205     {
206         error("Failed to create a temporary pel, error number - {ERROR_NUM}",
207               "ERROR_NUM", errno);
208         return PLDM_ERROR;
209     }
210     close(fd);
211     fs::path path(tmpFile);
212 
213     auto rc = transferFileData(path, false, offset, length, address);
214     if (rc == PLDM_SUCCESS)
215     {
216         rc = storePel(path.string());
217     }
218     return rc;
219 }
220 
fileAck(uint8_t fileStatus)221 int PelHandler::fileAck(uint8_t fileStatus)
222 {
223     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
224     static constexpr auto logInterface = "org.open_power.Logging.PEL";
225     static std::string service;
226     auto& bus = pldm::utils::DBusHandler::getBus();
227 
228     if (service.empty())
229     {
230         try
231         {
232             service = pldm::utils::DBusHandler().getService(logObjPath,
233                                                             logInterface);
234         }
235         catch (const sdbusplus::exception_t& e)
236         {
237             error(
238                 "Failed to do mapper call when trying to find logging service "
239                 "to ack PEL ID '{FILE_HANDLE}', error - {ERROR}",
240                 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e);
241             return PLDM_ERROR;
242         }
243     }
244 
245     if (fileStatus == PLDM_SUCCESS)
246     {
247         try
248         {
249             auto method = bus.new_method_call(service.c_str(), logObjPath,
250                                               logInterface, "HostAck");
251             method.append(fileHandle);
252             bus.call_noreply(method, dbusTimeout);
253         }
254         catch (const std::exception& e)
255         {
256             error(
257                 "Failure in HostReject ack D-Bus call on PEL ID '{FILE_HANDLE}', error - {ERROR}",
258                 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e);
259             return PLDM_ERROR;
260         }
261     }
262     else
263     {
264         PEL::RejectionReason reason{};
265         if (fileStatus == PLDM_FULL_FILE_DISCARDED)
266         {
267             reason = PEL::RejectionReason::HostFull;
268         }
269         else if (fileStatus == PLDM_ERROR_FILE_DISCARDED)
270         {
271             reason = PEL::RejectionReason::BadPEL;
272         }
273         else
274         {
275             error(
276                 "Invalid file status '{STATUS}' in PEL file ack response for PEL '{FILE_HANDLE}'",
277                 "STATUS", lg2::hex, fileStatus, "FILE_HANDLE", lg2::hex,
278                 fileHandle);
279             return PLDM_ERROR;
280         }
281 
282         try
283         {
284             auto method = bus.new_method_call(service.c_str(), logObjPath,
285                                               logInterface, "HostReject");
286             method.append(fileHandle, reason);
287             bus.call_noreply(method, dbusTimeout);
288         }
289         catch (const std::exception& e)
290         {
291             error("Failure in HostReject D-Bus call on PEL ID '{FILE_HANDLE}', "
292                   "error - {ERROR}, status - {STATUS}",
293                   "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e, "STATUS",
294                   lg2::hex, fileStatus);
295             return PLDM_ERROR;
296         }
297     }
298 
299     return PLDM_SUCCESS;
300 }
301 
storePel(std::string && pelFileName)302 int PelHandler::storePel(std::string&& pelFileName)
303 {
304     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
305     static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create";
306 
307     auto& bus = pldm::utils::DBusHandler::getBus();
308 
309     try
310     {
311         auto service = pldm::utils::DBusHandler().getService(logObjPath,
312                                                              logInterface);
313         using namespace sdbusplus::xyz::openbmc_project::Logging::server;
314         std::map<std::string, std::string> addlData{};
315         auto severity =
316             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
317                 detail::getEntryLevelFromPEL(pelFileName));
318         addlData.emplace("RAWPEL", std::move(pelFileName));
319 
320         auto method = bus.new_method_call(service.c_str(), logObjPath,
321                                           logInterface, "Create");
322         method.append("xyz.openbmc_project.Host.Error.Event", severity,
323                       addlData);
324         bus.call_noreply(method, dbusTimeout);
325     }
326     catch (const std::exception& e)
327     {
328         error(
329             "Failed to make a d-bus call to PEL daemon, PEL file name '{FILE}', ERROR - {ERROR}",
330             "FILE", pelFileName, "ERROR", e);
331         return PLDM_ERROR;
332     }
333 
334     return PLDM_SUCCESS;
335 }
336 
write(const char * buffer,uint32_t offset,uint32_t & length,oem_platform::Handler *)337 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length,
338                       oem_platform::Handler* /*oemPlatformHandler*/)
339 {
340     int rc = PLDM_SUCCESS;
341 
342     if (offset > 0)
343     {
344         error("Offset '{OFFSET}' is non zero", "OFFSET", offset);
345         return PLDM_ERROR;
346     }
347 
348     char tmpFile[] = "/tmp/pel.XXXXXX";
349     auto fd = mkstemp(tmpFile);
350     if (fd == -1)
351     {
352         error("Failed to create a temporary PEL, error number - {ERROR_NUM}",
353               "ERROR_NUM", errno);
354         return PLDM_ERROR;
355     }
356 
357     size_t written = 0;
358     do
359     {
360         if ((rc = ::write(fd, buffer, length - written)) == -1)
361         {
362             break;
363         }
364         written += rc;
365         buffer += rc;
366     } while (rc && written < length);
367     close(fd);
368 
369     if (rc == -1)
370     {
371         error(
372             "Failed to do file write of length '{LENGTH}' at offset '{OFFSET}', error number - {ERROR_NUM}",
373             "LENGTH", length, "OFFSET", offset, "ERROR_NUM", errno);
374         fs::remove(tmpFile);
375         return PLDM_ERROR;
376     }
377 
378     if (written == length)
379     {
380         fs::path path(tmpFile);
381         rc = storePel(path.string());
382         if (rc != PLDM_SUCCESS)
383         {
384             error(
385                 "Failed to save PEL in temp file '{FILE}', response code '{RC}'",
386                 "RC", rc, "FILE", tmpFile);
387         }
388     }
389 
390     return rc;
391 }
392 
393 } // namespace responder
394 } // namespace pldm
395