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