1 #include "file_io_type_dump.hpp"
2 
3 #include "common/utils.hpp"
4 #include "utils.hpp"
5 #include "xyz/openbmc_project/Common/error.hpp"
6 
7 #include <libpldm/base.h>
8 #include <libpldm/file_io.h>
9 #include <stdint.h>
10 #include <systemd/sd-bus.h>
11 #include <unistd.h>
12 
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/server.hpp>
15 #include <xyz/openbmc_project/Dump/NewDump/server.hpp>
16 
17 #include <exception>
18 #include <filesystem>
19 #include <iostream>
20 #include <type_traits>
21 
22 PHOSPHOR_LOG2_USING;
23 
24 using namespace pldm::responder::utils;
25 using namespace pldm::utils;
26 
27 namespace pldm
28 {
29 namespace responder
30 {
31 static constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
32 static constexpr auto dumpObjPath = "/xyz/openbmc_project/dump/system";
33 static constexpr auto systemDumpEntry = "xyz.openbmc_project.Dump.Entry.System";
34 static constexpr auto resDumpObjPath = "/xyz/openbmc_project/dump/resource";
35 static constexpr auto resDumpEntry = "com.ibm.Dump.Entry.Resource";
36 
37 // Resource dump file path to be deleted once hyperviosr validates the input
38 // parameters. Need to re-look in to this name when we support multiple
39 // resource dumps.
40 static constexpr auto resDumpDirPath = "/var/lib/pldm/resourcedump/1";
41 
42 int DumpHandler::fd = -1;
43 namespace fs = std::filesystem;
44 
45 std::string DumpHandler::findDumpObjPath(uint32_t fileHandle)
46 {
47     static constexpr auto DUMP_MANAGER_BUSNAME =
48         "xyz.openbmc_project.Dump.Manager";
49     static constexpr auto DUMP_MANAGER_PATH = "/xyz/openbmc_project/dump";
50     static constexpr auto OBJECT_MANAGER_INTERFACE =
51         "org.freedesktop.DBus.ObjectManager";
52     auto& bus = pldm::utils::DBusHandler::getBus();
53 
54     // Stores the current resource dump entry path
55     std::string curResDumpEntryPath{};
56 
57     dbus::ObjectValueTree objects;
58     // Select the dump entry interface for system dump or resource dump
59     DumpEntryInterface dumpEntryIntf = systemDumpEntry;
60     if ((dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP) ||
61         (dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS))
62     {
63         dumpEntryIntf = resDumpEntry;
64     }
65 
66     try
67     {
68         auto method =
69             bus.new_method_call(DUMP_MANAGER_BUSNAME, DUMP_MANAGER_PATH,
70                                 OBJECT_MANAGER_INTERFACE, "GetManagedObjects");
71 
72         auto reply = bus.call(
73             method,
74             std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
75         reply.read(objects);
76     }
77     catch (const sdbusplus::exception_t& e)
78     {
79         error(
80             "findDumpObjPath: Error {ERR_EXCEP} found with GetManagedObjects call in findDumpObjPath with objPath={OBJ_PATH} and intf={DUMP_INFT}",
81             "ERR_EXCEP", e.what(), "OBJ_PATH", DUMP_MANAGER_PATH, "DUMP_INFT",
82             dumpEntryIntf);
83         return curResDumpEntryPath;
84     }
85 
86     for (const auto& object : objects)
87     {
88         for (const auto& interface : object.second)
89         {
90             if (interface.first != dumpEntryIntf)
91             {
92                 continue;
93             }
94 
95             for (auto& propertyMap : interface.second)
96             {
97                 if (propertyMap.first == "SourceDumpId")
98                 {
99                     auto dumpIdPtr = std::get_if<uint32_t>(&propertyMap.second);
100                     if (dumpIdPtr != nullptr)
101                     {
102                         auto dumpId = *dumpIdPtr;
103                         if (fileHandle == dumpId)
104                         {
105                             curResDumpEntryPath = object.first.str;
106                             return curResDumpEntryPath;
107                         }
108                     }
109                     else
110                     {
111                         error(
112                             "Invalid SourceDumpId in curResDumpEntryPath {CUR_RES_DUMP_PATH} but continuing with next entry for a match...",
113                             "CUR_RES_DUMP_PATH", curResDumpEntryPath);
114                     }
115                 }
116             }
117         }
118     }
119     return curResDumpEntryPath;
120 }
121 
122 int DumpHandler::newFileAvailable(uint64_t length)
123 {
124     static constexpr auto dumpInterface = "xyz.openbmc_project.Dump.NewDump";
125     auto& bus = pldm::utils::DBusHandler::getBus();
126 
127     auto notifyObjPath = dumpObjPath;
128     if (dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP)
129     {
130         // Setting the Notify path for resource dump
131         notifyObjPath = resDumpObjPath;
132     }
133 
134     try
135     {
136         auto service = pldm::utils::DBusHandler().getService(notifyObjPath,
137                                                              dumpInterface);
138         using namespace sdbusplus::xyz::openbmc_project::Dump::server;
139         auto method = bus.new_method_call(service.c_str(), notifyObjPath,
140                                           dumpInterface, "Notify");
141         method.append(fileHandle, length);
142         bus.call_noreply(method);
143     }
144     catch (const std::exception& e)
145     {
146         error(
147             "newFileAvailable: Error {ERR_EXCEP} found while notifying new dump to dump manager with objPath={OBJ_PATH} and intf={DUMP_INTF}",
148             "ERR_EXCEP", e.what(), "OBJ_PATH", notifyObjPath, "DUMP_INTF",
149             dumpInterface);
150         return PLDM_ERROR;
151     }
152 
153     return PLDM_SUCCESS;
154 }
155 
156 std::string DumpHandler::getOffloadUri(uint32_t fileHandle)
157 {
158     auto path = findDumpObjPath(fileHandle);
159     if (path.empty())
160     {
161         return {};
162     }
163 
164     std::string socketInterface{};
165 
166     try
167     {
168         socketInterface =
169             pldm::utils::DBusHandler().getDbusProperty<std::string>(
170                 path.c_str(), "OffloadUri", dumpEntry);
171     }
172     catch (const std::exception& e)
173     {
174         error(
175             "getOffloadUri: Error {ERR_EXCEP} found while fetching the dump offload URI with objPath={OBJ_PATH} and intf={SOCKET_INTF}",
176             "ERR_EXCEP", e.what(), "OBJ_PATH", path.c_str(), "SOCKET_INTF",
177             socketInterface);
178     }
179 
180     return socketInterface;
181 }
182 
183 int DumpHandler::writeFromMemory(uint32_t, uint32_t length, uint64_t address,
184                                  oem_platform::Handler* /*oemPlatformHandler*/)
185 {
186     if (DumpHandler::fd == -1)
187     {
188         auto socketInterface = getOffloadUri(fileHandle);
189         int sock = setupUnixSocket(socketInterface);
190         if (sock < 0)
191         {
192             sock = -errno;
193             close(DumpHandler::fd);
194             error("DumpHandler::writeFromMemory: setupUnixSocket() failed");
195             std::remove(socketInterface.c_str());
196             return PLDM_ERROR;
197         }
198 
199         DumpHandler::fd = sock;
200     }
201     return transferFileDataToSocket(DumpHandler::fd, length, address);
202 }
203 
204 int DumpHandler::write(const char* buffer, uint32_t, uint32_t& length,
205                        oem_platform::Handler* /*oemPlatformHandler*/)
206 {
207     int rc = writeToUnixSocket(DumpHandler::fd, buffer, length);
208     if (rc < 0)
209     {
210         rc = -errno;
211         close(DumpHandler::fd);
212         auto socketInterface = getOffloadUri(fileHandle);
213         std::remove(socketInterface.c_str());
214         error("DumpHandler::write: writeToUnixSocket() failed");
215         return PLDM_ERROR;
216     }
217 
218     return PLDM_SUCCESS;
219 }
220 
221 int DumpHandler::fileAck(uint8_t fileStatus)
222 {
223     auto path = findDumpObjPath(fileHandle);
224     if (dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS)
225     {
226         if (fileStatus != PLDM_SUCCESS)
227         {
228             error("Failue in resource dump file ack");
229             pldm::utils::reportError(
230                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
231 
232             PropertyValue value{
233                 "xyz.openbmc_project.Common.Progress.OperationStatus.Failed"};
234             DBusMapping dbusMapping{path, "xyz.openbmc_project.Common.Progress",
235                                     "Status", "string"};
236             try
237             {
238                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
239             }
240             catch (const std::exception& e)
241             {
242                 error(
243                     "fileAck: Error {ERR_EXCEP} found while setting the dump progress status as Failed with objPath={OBJ_PATH} and intf=Common.Progress",
244                     "ERR_EXCEP", e.what(), "OBJ_PATH", path.c_str());
245             }
246         }
247 
248         if (fs::exists(resDumpDirPath))
249         {
250             fs::remove_all(resDumpDirPath);
251         }
252         return PLDM_SUCCESS;
253     }
254 
255     if (!path.empty())
256     {
257         if (fileStatus == PLDM_ERROR_FILE_DISCARDED)
258         {
259             uint32_t val = 0xFFFFFFFF;
260             PropertyValue value = static_cast<uint32_t>(val);
261             auto dumpIntf = resDumpEntry;
262 
263             if (dumpType == PLDM_FILE_TYPE_DUMP)
264             {
265                 dumpIntf = systemDumpEntry;
266             }
267 
268             DBusMapping dbusMapping{path.c_str(), dumpIntf, "SourceDumpId",
269                                     "uint32_t"};
270             try
271             {
272                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
273             }
274             catch (const std::exception& e)
275             {
276                 error(
277                     "fileAck: Failed to make a d-bus call to DUMP manager to reset source dump id of {FILE_PATH}, with ERROR={ERR_EXCEP}",
278                     "FILE_PATH", path.c_str(), "ERR_EXCEP", e.what());
279                 pldm::utils::reportError(
280                     "xyz.openbmc_project.bmc.PLDM.fileAck.SourceDumpIdResetFail");
281                 return PLDM_ERROR;
282             }
283 
284             auto& bus = pldm::utils::DBusHandler::getBus();
285             try
286             {
287                 auto method = bus.new_method_call(
288                     "xyz.openbmc_project.Dump.Manager", path.c_str(),
289                     "xyz.openbmc_project.Object.Delete", "Delete");
290                 bus.call(method,
291                          std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT))
292                              .count());
293             }
294             catch (const std::exception& e)
295             {
296                 error(
297                     "fileAck: Failed to make a d-bus method to delete the dump entry {FILE_PATH}, with ERROR={ERR_EXCEP}",
298                     "FILE_PATH", path.c_str(), "ERR_EXCEP", e.what());
299                 pldm::utils::reportError(
300                     "xyz.openbmc_project.bmc.PLDM.fileAck.DumpEntryDeleteFail");
301                 return PLDM_ERROR;
302             }
303             return PLDM_SUCCESS;
304         }
305 
306         if (dumpType == PLDM_FILE_TYPE_DUMP ||
307             dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP)
308         {
309             PropertyValue value{true};
310             DBusMapping dbusMapping{path, dumpEntry, "Offloaded", "bool"};
311             try
312             {
313                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
314             }
315             catch (const std::exception& e)
316             {
317                 error(
318                     "fileAck: Failed to make a d-bus method to set the dump offloaded property to true with path={FILE_PATH} and with ERROR={ERR_EXCEP}",
319                     "FILE_PATH", path.c_str(), "ERR_EXCEP", e.what());
320             }
321 
322             auto socketInterface = getOffloadUri(fileHandle);
323             std::remove(socketInterface.c_str());
324             if (DumpHandler::fd >= 0)
325             {
326                 close(DumpHandler::fd);
327                 DumpHandler::fd = -1;
328             }
329         }
330         return PLDM_SUCCESS;
331     }
332 
333     return PLDM_ERROR;
334 }
335 
336 int DumpHandler::readIntoMemory(uint32_t offset, uint32_t& length,
337                                 uint64_t address,
338                                 oem_platform::Handler* /*oemPlatformHandler*/)
339 {
340     if (dumpType != PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS)
341     {
342         return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
343     }
344     return transferFileData(resDumpDirPath, true, offset, length, address);
345 }
346 
347 int DumpHandler::read(uint32_t offset, uint32_t& length, Response& response,
348                       oem_platform::Handler* /*oemPlatformHandler*/)
349 {
350     if (dumpType != PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS)
351     {
352         return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
353     }
354     return readFile(resDumpDirPath, offset, length, response);
355 }
356 
357 } // namespace responder
358 } // namespace pldm
359