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     // Select the dump entry interface for system dump or resource dump
56     DumpEntryInterface dumpEntryIntf = systemDumpEntry;
57     if ((dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP) ||
58         (dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS))
59     {
60         dumpEntryIntf = resDumpEntry;
61     }
62 
63     try
64     {
65         auto method =
66             bus.new_method_call(DUMP_MANAGER_BUSNAME, DUMP_MANAGER_PATH,
67                                 OBJECT_MANAGER_INTERFACE, "GetManagedObjects");
68 
69         auto reply = bus.call(method);
70         reply.read(objects);
71     }
72     catch (const sdbusplus::exception_t& e)
73     {
74         std::cerr << "findDumpObjPath: Error " << e.what()
75                   << "found with GetManagedObjects call in findDumpObjPath "
76                   << "with objPath=" << DUMP_MANAGER_PATH
77                   << " and intf=" << dumpEntryIntf << "\n";
78         return curResDumpEntryPath;
79     }
80 
81     for (const auto& object : objects)
82     {
83         for (const auto& interface : object.second)
84         {
85             if (interface.first != dumpEntryIntf)
86             {
87                 continue;
88             }
89 
90             for (auto& propertyMap : interface.second)
91             {
92                 if (propertyMap.first == "SourceDumpId")
93                 {
94                     auto dumpIdPtr = std::get_if<uint32_t>(&propertyMap.second);
95                     if (dumpIdPtr != nullptr)
96                     {
97                         auto dumpId = *dumpIdPtr;
98                         if (fileHandle == dumpId)
99                         {
100                             curResDumpEntryPath = object.first.str;
101                             return curResDumpEntryPath;
102                         }
103                     }
104                     else
105                     {
106                         std::cerr
107                             << "Invalid SourceDumpId in curResDumpEntryPath "
108                             << curResDumpEntryPath
109                             << " but continuing with next entry for a match..."
110                             << "\n";
111                     }
112                 }
113             }
114         }
115     }
116     return curResDumpEntryPath;
117 }
118 
119 int DumpHandler::newFileAvailable(uint64_t length)
120 {
121     static constexpr auto dumpInterface = "xyz.openbmc_project.Dump.NewDump";
122     auto& bus = pldm::utils::DBusHandler::getBus();
123 
124     auto notifyObjPath = dumpObjPath;
125     if (dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP)
126     {
127         // Setting the Notify path for resource dump
128         notifyObjPath = resDumpObjPath;
129     }
130 
131     try
132     {
133         auto service =
134             pldm::utils::DBusHandler().getService(notifyObjPath, dumpInterface);
135         using namespace sdbusplus::xyz::openbmc_project::Dump::server;
136         auto method = bus.new_method_call(service.c_str(), notifyObjPath,
137                                           dumpInterface, "Notify");
138         method.append(fileHandle, length);
139         bus.call_noreply(method);
140     }
141     catch (const std::exception& e)
142     {
143         std::cerr << "newFileAvailable: Error " << e.what()
144                   << "found while notifying new dump to dump manager "
145                   << "with objPath=" << notifyObjPath
146                   << " and intf=" << dumpInterface << "\n";
147         return PLDM_ERROR;
148     }
149 
150     return PLDM_SUCCESS;
151 }
152 
153 std::string DumpHandler::getOffloadUri(uint32_t fileHandle)
154 {
155     auto path = findDumpObjPath(fileHandle);
156     if (path.empty())
157     {
158         return {};
159     }
160 
161     std::string socketInterface{};
162 
163     try
164     {
165         socketInterface =
166             pldm::utils::DBusHandler().getDbusProperty<std::string>(
167                 path.c_str(), "OffloadUri", dumpEntry);
168     }
169     catch (const std::exception& e)
170     {
171         std::cerr << "getOffloadUri: Error " << e.what()
172                   << "found while fetching the dump offload URI "
173                   << "with objPath=" << path.c_str()
174                   << " and intf=" << socketInterface << "\n";
175     }
176 
177     return socketInterface;
178 }
179 
180 int DumpHandler::writeFromMemory(uint32_t, uint32_t length, uint64_t address,
181                                  oem_platform::Handler* /*oemPlatformHandler*/)
182 {
183     if (DumpHandler::fd == -1)
184     {
185         auto socketInterface = getOffloadUri(fileHandle);
186         int sock = setupUnixSocket(socketInterface);
187         if (sock < 0)
188         {
189             sock = -errno;
190             close(DumpHandler::fd);
191             std::cerr
192                 << "DumpHandler::writeFromMemory: setupUnixSocket() failed"
193                 << std::endl;
194             std::remove(socketInterface.c_str());
195             return PLDM_ERROR;
196         }
197 
198         DumpHandler::fd = sock;
199     }
200     return transferFileDataToSocket(DumpHandler::fd, length, address);
201 }
202 
203 int DumpHandler::write(const char* buffer, uint32_t, uint32_t& length,
204                        oem_platform::Handler* /*oemPlatformHandler*/)
205 {
206     int rc = writeToUnixSocket(DumpHandler::fd, buffer, length);
207     if (rc < 0)
208     {
209         rc = -errno;
210         close(DumpHandler::fd);
211         auto socketInterface = getOffloadUri(fileHandle);
212         std::remove(socketInterface.c_str());
213         std::cerr << "DumpHandler::write: writeToUnixSocket() failed"
214                   << std::endl;
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             std::cerr << "Failue in resource dump file ack" << std::endl;
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                 std::cerr << "fileAck: Error " << e.what()
243                           << "found while setting the dump progress status as "
244                           << "Failed with objPath=" << path.c_str()
245                           << " and intf=Common.Progress"
246                           << "\n";
247             }
248         }
249 
250         if (fs::exists(resDumpDirPath))
251         {
252             fs::remove_all(resDumpDirPath);
253         }
254         return PLDM_SUCCESS;
255     }
256 
257     if (!path.empty())
258     {
259         if (fileStatus == PLDM_ERROR_FILE_DISCARDED)
260         {
261             uint32_t val = 0xFFFFFFFF;
262             PropertyValue value = static_cast<uint32_t>(val);
263             auto dumpIntf = resDumpEntry;
264 
265             if (dumpType == PLDM_FILE_TYPE_DUMP)
266             {
267                 dumpIntf = systemDumpEntry;
268             }
269 
270             DBusMapping dbusMapping{path.c_str(), dumpIntf, "SourceDumpId",
271                                     "uint32_t"};
272             try
273             {
274                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
275             }
276             catch (const std::exception& e)
277             {
278                 std::cerr << "fileAck: Failed to make a d-bus call to DUMP "
279                              "manager to reset source dump id of "
280                           << path.c_str() << ", with ERROR=" << e.what()
281                           << "\n";
282                 pldm::utils::reportError(
283                     "xyz.openbmc_project.bmc.PLDM.fileAck.SourceDumpIdResetFail");
284                 return PLDM_ERROR;
285             }
286 
287             auto& bus = pldm::utils::DBusHandler::getBus();
288             try
289             {
290                 auto method = bus.new_method_call(
291                     "xyz.openbmc_project.Dump.Manager", path.c_str(),
292                     "xyz.openbmc_project.Object.Delete", "Delete");
293                 bus.call(method);
294             }
295             catch (const std::exception& e)
296             {
297                 std::cerr
298                     << "fileAck: Failed to make a d-bus method to delete the dump entry "
299                     << path.c_str() << ", with ERROR=" << e.what() << "\n";
300                 pldm::utils::reportError(
301                     "xyz.openbmc_project.bmc.PLDM.fileAck.DumpEntryDeleteFail");
302                 return PLDM_ERROR;
303             }
304             return PLDM_SUCCESS;
305         }
306 
307         if (dumpType == PLDM_FILE_TYPE_DUMP ||
308             dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP)
309         {
310             PropertyValue value{true};
311             DBusMapping dbusMapping{path, dumpEntry, "Offloaded", "bool"};
312             try
313             {
314                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
315             }
316             catch (const std::exception& e)
317             {
318                 std::cerr
319                     << "fileAck: Failed to make a d-bus method to set the dump "
320                     << "offloaded property to true with path=" << path.c_str()
321                     << "and with ERROR=" << e.what() << "\n";
322             }
323 
324             auto socketInterface = getOffloadUri(fileHandle);
325             std::remove(socketInterface.c_str());
326             if (DumpHandler::fd >= 0)
327             {
328                 close(DumpHandler::fd);
329                 DumpHandler::fd = -1;
330             }
331         }
332         return PLDM_SUCCESS;
333     }
334 
335     return PLDM_ERROR;
336 }
337 
338 int DumpHandler::readIntoMemory(uint32_t offset, uint32_t& length,
339                                 uint64_t address,
340                                 oem_platform::Handler* /*oemPlatformHandler*/)
341 {
342     if (dumpType != PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS)
343     {
344         return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
345     }
346     return transferFileData(resDumpDirPath, true, offset, length, address);
347 }
348 
349 int DumpHandler::read(uint32_t offset, uint32_t& length, Response& response,
350                       oem_platform::Handler* /*oemPlatformHandler*/)
351 {
352     if (dumpType != PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS)
353     {
354         return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
355     }
356     return readFile(resDumpDirPath, offset, length, response);
357 }
358 
359 } // namespace responder
360 } // namespace pldm
361