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