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 <systemd/sd-bus.h>
9 #include <unistd.h>
10
11 #include <org/open_power/Logging/PEL/server.hpp>
12 #include <phosphor-logging/lg2.hpp>
13 #include <sdbusplus/server.hpp>
14 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
15
16 #include <cstdint>
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 =
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, 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 =
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, 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 =
233 pldm::utils::DBusHandler().getService(logObjPath, 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 =
312 pldm::utils::DBusHandler().getService(logObjPath, 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
325 auto callback = [pelFileName](sdbusplus::message_t&& msg) {
326 if (msg.is_method_error())
327 {
328 error(
329 "Async DBus call failed for PEL file name - '{FILE}', ERROR - {ERROR}",
330 "FILE", pelFileName, "ERROR", msg.get_error()->message);
331 }
332 };
333
334 [[maybe_unused]] auto slot = method.call_async(callback);
335 }
336 catch (const std::exception& e)
337 {
338 error(
339 "Failed to make a d-bus call to PEL daemon, PEL file name '{FILE}', ERROR - {ERROR}",
340 "FILE", pelFileName, "ERROR", e);
341 return PLDM_ERROR;
342 }
343
344 return PLDM_SUCCESS;
345 }
346
write(const char * buffer,uint32_t offset,uint32_t & length,oem_platform::Handler *)347 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length,
348 oem_platform::Handler* /*oemPlatformHandler*/)
349 {
350 int rc = PLDM_SUCCESS;
351
352 if (offset > 0)
353 {
354 error("Offset '{OFFSET}' is non zero", "OFFSET", offset);
355 return PLDM_ERROR;
356 }
357
358 char tmpFile[] = "/tmp/pel.XXXXXX";
359 auto fd = mkstemp(tmpFile);
360 if (fd == -1)
361 {
362 error("Failed to create a temporary PEL, error number - {ERROR_NUM}",
363 "ERROR_NUM", errno);
364 return PLDM_ERROR;
365 }
366
367 size_t written = 0;
368 do
369 {
370 if ((rc = ::write(fd, buffer, length - written)) == -1)
371 {
372 break;
373 }
374 written += rc;
375 buffer += rc;
376 } while (rc && written < length);
377 close(fd);
378
379 if (rc == -1)
380 {
381 error(
382 "Failed to do file write of length '{LENGTH}' at offset '{OFFSET}', error number - {ERROR_NUM}",
383 "LENGTH", length, "OFFSET", offset, "ERROR_NUM", errno);
384 fs::remove(tmpFile);
385 return PLDM_ERROR;
386 }
387
388 if (written == length)
389 {
390 fs::path path(tmpFile);
391 rc = storePel(path.string());
392 if (rc != PLDM_SUCCESS)
393 {
394 error(
395 "Failed to save PEL in temp file '{FILE}', response code '{RC}'",
396 "RC", rc, "FILE", tmpFile);
397 }
398 }
399
400 return rc;
401 }
402
403 } // namespace responder
404 } // namespace pldm
405