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