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