1 #include "config.h"
2 
3 #include "file_io.hpp"
4 
5 #include "file_table.hpp"
6 #include "libpldmresponder/utils.hpp"
7 #include "registration.hpp"
8 
9 #include <fcntl.h>
10 #include <sys/mman.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include <cstring>
16 #include <fstream>
17 #include <phosphor-logging/log.hpp>
18 
19 #include "libpldm/base.h"
20 
21 namespace pldm
22 {
23 
24 namespace responder
25 {
26 
27 namespace oem_ibm
28 {
29 
30 void registerHandlers()
31 {
32     registerHandler(PLDM_OEM, PLDM_GET_FILE_TABLE, std::move(getFileTable));
33     registerHandler(PLDM_OEM, PLDM_READ_FILE_INTO_MEMORY,
34                     std::move(readFileIntoMemory));
35     registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY,
36                     std::move(writeFileFromMemory));
37 }
38 
39 } // namespace oem_ibm
40 
41 namespace fs = std::filesystem;
42 using namespace phosphor::logging;
43 
44 namespace dma
45 {
46 
47 /** @struct AspeedXdmaOp
48  *
49  * Structure representing XDMA operation
50  */
51 struct AspeedXdmaOp
52 {
53     uint64_t hostAddr; //!< the DMA address on the host side, configured by
54                        //!< PCI subsystem.
55     uint32_t len;      //!< the size of the transfer in bytes, it should be a
56                        //!< multiple of 16 bytes
57     uint32_t upstream; //!< boolean indicating the direction of the DMA
58                        //!< operation, true means a transfer from BMC to host.
59 };
60 
61 constexpr auto xdmaDev = "/dev/xdma";
62 
63 int DMA::transferDataHost(const fs::path& path, uint32_t offset,
64                           uint32_t length, uint64_t address, bool upstream)
65 {
66     static const size_t pageSize = getpagesize();
67     uint32_t numPages = length / pageSize;
68     uint32_t pageAlignedLength = numPages * pageSize;
69 
70     if (length > pageAlignedLength)
71     {
72         pageAlignedLength += pageSize;
73     }
74 
75     auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
76         munmap(vgaMem, pageAlignedLength);
77     };
78 
79     int fd = -1;
80     int rc = 0;
81     fd = open(xdmaDev, O_RDWR);
82     if (fd < 0)
83     {
84         rc = -errno;
85         log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
86         return rc;
87     }
88 
89     utils::CustomFD xdmaFd(fd);
90 
91     void* vgaMem;
92     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
93                   MAP_SHARED, xdmaFd(), 0);
94     if (MAP_FAILED == vgaMem)
95     {
96         rc = -errno;
97         log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
98         return rc;
99     }
100 
101     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
102 
103     if (upstream)
104     {
105         std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
106         stream.seekg(offset);
107 
108         // Writing to the VGA memory should be aligned at page boundary,
109         // otherwise write data into a buffer aligned at page boundary and
110         // then write to the VGA memory.
111         std::vector<char> buffer{};
112         buffer.resize(pageAlignedLength);
113         stream.read(buffer.data(), length);
114         memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
115                pageAlignedLength);
116 
117         if (static_cast<uint32_t>(stream.gcount()) != length)
118         {
119             log<level::ERR>("mismatch between number of characters to read and "
120                             "the length read",
121                             entry("LENGTH=%d", length),
122                             entry("COUNT=%d", stream.gcount()));
123             return -1;
124         }
125     }
126 
127     AspeedXdmaOp xdmaOp;
128     xdmaOp.upstream = upstream ? 1 : 0;
129     xdmaOp.hostAddr = address;
130     xdmaOp.len = length;
131 
132     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
133     if (rc < 0)
134     {
135         rc = -errno;
136         log<level::ERR>("Failed to execute the DMA operation",
137                         entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
138                         entry("ADDRESS=%lld", address),
139                         entry("LENGTH=%d", length));
140         return rc;
141     }
142 
143     if (!upstream)
144     {
145         std::ofstream stream(path.string(),
146                              std::ios::in | std::ios::out | std::ios::binary);
147 
148         stream.seekp(offset);
149         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
150     }
151 
152     return 0;
153 }
154 
155 } // namespace dma
156 
157 Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength)
158 {
159     uint32_t fileHandle = 0;
160     uint32_t offset = 0;
161     uint32_t length = 0;
162     uint64_t address = 0;
163 
164     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
165     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
166 
167     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
168     {
169         encode_rw_file_memory_resp(request->hdr.instance_id,
170                                    PLDM_READ_FILE_INTO_MEMORY,
171                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
172         return response;
173     }
174 
175     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
176                               &length, &address);
177 
178     using namespace pldm::filetable;
179     auto& table = buildFileTable(FILE_TABLE_JSON);
180     FileEntry value{};
181 
182     try
183     {
184         value = table.at(fileHandle);
185     }
186     catch (std::exception& e)
187     {
188         log<level::ERR>("File handle does not exist in the file table",
189                         entry("HANDLE=%d", fileHandle));
190         encode_rw_file_memory_resp(request->hdr.instance_id,
191                                    PLDM_READ_FILE_INTO_MEMORY,
192                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
193         return response;
194     }
195 
196     if (!fs::exists(value.fsPath))
197     {
198         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
199         encode_rw_file_memory_resp(request->hdr.instance_id,
200                                    PLDM_READ_FILE_INTO_MEMORY,
201                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
202         return response;
203     }
204 
205     auto fileSize = fs::file_size(value.fsPath);
206     if (offset >= fileSize)
207     {
208         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
209                         entry("FILE_SIZE=%d", fileSize));
210         encode_rw_file_memory_resp(request->hdr.instance_id,
211                                    PLDM_READ_FILE_INTO_MEMORY,
212                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
213         return response;
214     }
215 
216     if (offset + length > fileSize)
217     {
218         length = fileSize - offset;
219     }
220 
221     if (length % dma::minSize)
222     {
223         log<level::ERR>("Read length is not a multiple of DMA minSize",
224                         entry("LENGTH=%d", length));
225         encode_rw_file_memory_resp(request->hdr.instance_id,
226                                    PLDM_READ_FILE_INTO_MEMORY,
227                                    PLDM_INVALID_READ_LENGTH, 0, responsePtr);
228         return response;
229     }
230 
231     using namespace dma;
232     DMA intf;
233     return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
234                             offset, length, address, true,
235                             request->hdr.instance_id);
236 }
237 
238 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
239 {
240     uint32_t fileHandle = 0;
241     uint32_t offset = 0;
242     uint32_t length = 0;
243     uint64_t address = 0;
244 
245     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
246     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
247 
248     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
249     {
250         encode_rw_file_memory_resp(request->hdr.instance_id,
251                                    PLDM_WRITE_FILE_FROM_MEMORY,
252                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
253         return response;
254     }
255 
256     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
257                               &length, &address);
258 
259     if (length % dma::minSize)
260     {
261         log<level::ERR>("Write length is not a multiple of DMA minSize",
262                         entry("LENGTH=%d", length));
263         encode_rw_file_memory_resp(request->hdr.instance_id,
264                                    PLDM_WRITE_FILE_FROM_MEMORY,
265                                    PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
266         return response;
267     }
268 
269     using namespace pldm::filetable;
270     auto& table = buildFileTable(FILE_TABLE_JSON);
271     FileEntry value{};
272 
273     try
274     {
275         value = table.at(fileHandle);
276     }
277     catch (std::exception& e)
278     {
279         log<level::ERR>("File handle does not exist in the file table",
280                         entry("HANDLE=%d", fileHandle));
281         encode_rw_file_memory_resp(request->hdr.instance_id,
282                                    PLDM_WRITE_FILE_FROM_MEMORY,
283                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
284         return response;
285     }
286 
287     if (!fs::exists(value.fsPath))
288     {
289         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
290         encode_rw_file_memory_resp(request->hdr.instance_id,
291                                    PLDM_WRITE_FILE_FROM_MEMORY,
292                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
293         return response;
294     }
295 
296     auto fileSize = fs::file_size(value.fsPath);
297     if (offset >= fileSize)
298     {
299         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
300                         entry("FILE_SIZE=%d", fileSize));
301         encode_rw_file_memory_resp(request->hdr.instance_id,
302                                    PLDM_WRITE_FILE_FROM_MEMORY,
303                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
304         return response;
305     }
306 
307     using namespace dma;
308     DMA intf;
309     return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
310                             offset, length, address, false,
311                             request->hdr.instance_id);
312 }
313 
314 Response getFileTable(const pldm_msg* request, size_t payloadLength)
315 {
316     uint32_t transferHandle = 0;
317     uint8_t transferFlag = 0;
318     uint8_t tableType = 0;
319 
320     Response response(sizeof(pldm_msg_hdr) +
321                       PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
322     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
323 
324     if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
325     {
326         encode_get_file_table_resp(request->hdr.instance_id,
327                                    PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr, 0,
328                                    responsePtr);
329         return response;
330     }
331 
332     auto rc = decode_get_file_table_req(request, payloadLength, &transferHandle,
333                                         &transferFlag, &tableType);
334     if (rc)
335     {
336         encode_get_file_table_resp(request->hdr.instance_id, rc, 0, 0, nullptr,
337                                    0, responsePtr);
338         return response;
339     }
340 
341     if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
342     {
343         encode_get_file_table_resp(request->hdr.instance_id,
344                                    PLDM_INVALID_FILE_TABLE_TYPE, 0, 0, nullptr,
345                                    0, responsePtr);
346         return response;
347     }
348 
349     using namespace pldm::filetable;
350     auto table = buildFileTable(FILE_TABLE_JSON);
351     auto attrTable = table();
352     response.resize(response.size() + attrTable.size());
353     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
354 
355     if (attrTable.empty())
356     {
357         encode_get_file_table_resp(request->hdr.instance_id,
358                                    PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, nullptr,
359                                    0, responsePtr);
360         return response;
361     }
362 
363     encode_get_file_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 0,
364                                PLDM_START_AND_END, attrTable.data(),
365                                attrTable.size(), responsePtr);
366     return response;
367 }
368 
369 } // namespace responder
370 } // namespace pldm
371