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     registerHandler(PLDM_OEM, PLDM_READ_FILE, std::move(readFile));
38     registerHandler(PLDM_OEM, PLDM_WRITE_FILE, std::move(writeFile));
39 }
40 
41 } // namespace oem_ibm
42 
43 namespace fs = std::filesystem;
44 using namespace phosphor::logging;
45 
46 namespace dma
47 {
48 
49 /** @struct AspeedXdmaOp
50  *
51  * Structure representing XDMA operation
52  */
53 struct AspeedXdmaOp
54 {
55     uint64_t hostAddr; //!< the DMA address on the host side, configured by
56                        //!< PCI subsystem.
57     uint32_t len;      //!< the size of the transfer in bytes, it should be a
58                        //!< multiple of 16 bytes
59     uint32_t upstream; //!< boolean indicating the direction of the DMA
60                        //!< operation, true means a transfer from BMC to host.
61 };
62 
63 constexpr auto xdmaDev = "/dev/xdma";
64 
65 int DMA::transferDataHost(const fs::path& path, uint32_t offset,
66                           uint32_t length, uint64_t address, bool upstream)
67 {
68     static const size_t pageSize = getpagesize();
69     uint32_t numPages = length / pageSize;
70     uint32_t pageAlignedLength = numPages * pageSize;
71 
72     if (length > pageAlignedLength)
73     {
74         pageAlignedLength += pageSize;
75     }
76 
77     auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
78         munmap(vgaMem, pageAlignedLength);
79     };
80 
81     int fd = -1;
82     int rc = 0;
83     fd = open(xdmaDev, O_RDWR);
84     if (fd < 0)
85     {
86         rc = -errno;
87         log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
88         return rc;
89     }
90 
91     utils::CustomFD xdmaFd(fd);
92 
93     void* vgaMem;
94     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
95                   MAP_SHARED, xdmaFd(), 0);
96     if (MAP_FAILED == vgaMem)
97     {
98         rc = -errno;
99         log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
100         return rc;
101     }
102 
103     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
104 
105     if (upstream)
106     {
107         std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
108         stream.seekg(offset);
109 
110         // Writing to the VGA memory should be aligned at page boundary,
111         // otherwise write data into a buffer aligned at page boundary and
112         // then write to the VGA memory.
113         std::vector<char> buffer{};
114         buffer.resize(pageAlignedLength);
115         stream.read(buffer.data(), length);
116         memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
117                pageAlignedLength);
118 
119         if (static_cast<uint32_t>(stream.gcount()) != length)
120         {
121             log<level::ERR>("mismatch between number of characters to read and "
122                             "the length read",
123                             entry("LENGTH=%d", length),
124                             entry("COUNT=%d", stream.gcount()));
125             return -1;
126         }
127     }
128 
129     AspeedXdmaOp xdmaOp;
130     xdmaOp.upstream = upstream ? 1 : 0;
131     xdmaOp.hostAddr = address;
132     xdmaOp.len = length;
133 
134     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
135     if (rc < 0)
136     {
137         rc = -errno;
138         log<level::ERR>("Failed to execute the DMA operation",
139                         entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
140                         entry("ADDRESS=%lld", address),
141                         entry("LENGTH=%d", length));
142         return rc;
143     }
144 
145     if (!upstream)
146     {
147         std::ofstream stream(path.string(),
148                              std::ios::in | std::ios::out | std::ios::binary);
149 
150         stream.seekp(offset);
151         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
152     }
153 
154     return 0;
155 }
156 
157 } // namespace dma
158 
159 Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength)
160 {
161     uint32_t fileHandle = 0;
162     uint32_t offset = 0;
163     uint32_t length = 0;
164     uint64_t address = 0;
165 
166     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
167     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
168 
169     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
170     {
171         encode_rw_file_memory_resp(request->hdr.instance_id,
172                                    PLDM_READ_FILE_INTO_MEMORY,
173                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
174         return response;
175     }
176 
177     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
178                               &length, &address);
179 
180     using namespace pldm::filetable;
181     auto& table = buildFileTable(FILE_TABLE_JSON);
182     FileEntry value{};
183 
184     try
185     {
186         value = table.at(fileHandle);
187     }
188     catch (std::exception& e)
189     {
190         log<level::ERR>("File handle does not exist in the file table",
191                         entry("HANDLE=%d", fileHandle));
192         encode_rw_file_memory_resp(request->hdr.instance_id,
193                                    PLDM_READ_FILE_INTO_MEMORY,
194                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
195         return response;
196     }
197 
198     if (!fs::exists(value.fsPath))
199     {
200         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
201         encode_rw_file_memory_resp(request->hdr.instance_id,
202                                    PLDM_READ_FILE_INTO_MEMORY,
203                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
204         return response;
205     }
206 
207     auto fileSize = fs::file_size(value.fsPath);
208     if (offset >= fileSize)
209     {
210         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
211                         entry("FILE_SIZE=%d", fileSize));
212         encode_rw_file_memory_resp(request->hdr.instance_id,
213                                    PLDM_READ_FILE_INTO_MEMORY,
214                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
215         return response;
216     }
217 
218     if (offset + length > fileSize)
219     {
220         length = fileSize - offset;
221     }
222 
223     if (length % dma::minSize)
224     {
225         log<level::ERR>("Read length is not a multiple of DMA minSize",
226                         entry("LENGTH=%d", length));
227         encode_rw_file_memory_resp(request->hdr.instance_id,
228                                    PLDM_READ_FILE_INTO_MEMORY,
229                                    PLDM_INVALID_READ_LENGTH, 0, responsePtr);
230         return response;
231     }
232 
233     using namespace dma;
234     DMA intf;
235     return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
236                             offset, length, address, true,
237                             request->hdr.instance_id);
238 }
239 
240 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
241 {
242     uint32_t fileHandle = 0;
243     uint32_t offset = 0;
244     uint32_t length = 0;
245     uint64_t address = 0;
246 
247     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
248     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
249 
250     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
251     {
252         encode_rw_file_memory_resp(request->hdr.instance_id,
253                                    PLDM_WRITE_FILE_FROM_MEMORY,
254                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
255         return response;
256     }
257 
258     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
259                               &length, &address);
260 
261     if (length % dma::minSize)
262     {
263         log<level::ERR>("Write length is not a multiple of DMA minSize",
264                         entry("LENGTH=%d", length));
265         encode_rw_file_memory_resp(request->hdr.instance_id,
266                                    PLDM_WRITE_FILE_FROM_MEMORY,
267                                    PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
268         return response;
269     }
270 
271     using namespace pldm::filetable;
272     auto& table = buildFileTable(FILE_TABLE_JSON);
273     FileEntry value{};
274 
275     try
276     {
277         value = table.at(fileHandle);
278     }
279     catch (std::exception& e)
280     {
281         log<level::ERR>("File handle does not exist in the file table",
282                         entry("HANDLE=%d", fileHandle));
283         encode_rw_file_memory_resp(request->hdr.instance_id,
284                                    PLDM_WRITE_FILE_FROM_MEMORY,
285                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
286         return response;
287     }
288 
289     if (!fs::exists(value.fsPath))
290     {
291         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
292         encode_rw_file_memory_resp(request->hdr.instance_id,
293                                    PLDM_WRITE_FILE_FROM_MEMORY,
294                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
295         return response;
296     }
297 
298     auto fileSize = fs::file_size(value.fsPath);
299     if (offset >= fileSize)
300     {
301         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
302                         entry("FILE_SIZE=%d", fileSize));
303         encode_rw_file_memory_resp(request->hdr.instance_id,
304                                    PLDM_WRITE_FILE_FROM_MEMORY,
305                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
306         return response;
307     }
308 
309     using namespace dma;
310     DMA intf;
311     return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
312                             offset, length, address, false,
313                             request->hdr.instance_id);
314 }
315 
316 Response getFileTable(const pldm_msg* request, size_t payloadLength)
317 {
318     uint32_t transferHandle = 0;
319     uint8_t transferFlag = 0;
320     uint8_t tableType = 0;
321 
322     Response response(sizeof(pldm_msg_hdr) +
323                       PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
324     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
325 
326     if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
327     {
328         encode_get_file_table_resp(request->hdr.instance_id,
329                                    PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr, 0,
330                                    responsePtr);
331         return response;
332     }
333 
334     auto rc = decode_get_file_table_req(request, payloadLength, &transferHandle,
335                                         &transferFlag, &tableType);
336     if (rc)
337     {
338         encode_get_file_table_resp(request->hdr.instance_id, rc, 0, 0, nullptr,
339                                    0, responsePtr);
340         return response;
341     }
342 
343     if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
344     {
345         encode_get_file_table_resp(request->hdr.instance_id,
346                                    PLDM_INVALID_FILE_TABLE_TYPE, 0, 0, nullptr,
347                                    0, responsePtr);
348         return response;
349     }
350 
351     using namespace pldm::filetable;
352     auto table = buildFileTable(FILE_TABLE_JSON);
353     auto attrTable = table();
354     response.resize(response.size() + attrTable.size());
355     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
356 
357     if (attrTable.empty())
358     {
359         encode_get_file_table_resp(request->hdr.instance_id,
360                                    PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, nullptr,
361                                    0, responsePtr);
362         return response;
363     }
364 
365     encode_get_file_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 0,
366                                PLDM_START_AND_END, attrTable.data(),
367                                attrTable.size(), responsePtr);
368     return response;
369 }
370 
371 Response readFile(const pldm_msg* request, size_t payloadLength)
372 {
373     uint32_t fileHandle = 0;
374     uint32_t offset = 0;
375     uint32_t length = 0;
376 
377     Response response(sizeof(pldm_msg_hdr) + PLDM_READ_FILE_RESP_BYTES);
378     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
379 
380     if (payloadLength != PLDM_READ_FILE_REQ_BYTES)
381     {
382         encode_read_file_resp(request->hdr.instance_id,
383                               PLDM_ERROR_INVALID_LENGTH, length, responsePtr);
384         return response;
385     }
386 
387     auto rc = decode_read_file_req(request, payloadLength, &fileHandle, &offset,
388                                    &length);
389 
390     if (rc)
391     {
392         encode_read_file_resp(request->hdr.instance_id, rc, 0, responsePtr);
393         return response;
394     }
395 
396     using namespace pldm::filetable;
397     auto& table = buildFileTable(FILE_TABLE_JSON);
398     FileEntry value{};
399 
400     try
401     {
402         value = table.at(fileHandle);
403     }
404     catch (std::exception& e)
405     {
406         log<level::ERR>("File handle does not exist in the file table",
407                         entry("HANDLE=%d", fileHandle));
408         encode_read_file_resp(request->hdr.instance_id,
409                               PLDM_INVALID_FILE_HANDLE, length, responsePtr);
410         return response;
411     }
412 
413     if (!fs::exists(value.fsPath))
414     {
415         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
416         encode_read_file_resp(request->hdr.instance_id,
417                               PLDM_INVALID_FILE_HANDLE, length, responsePtr);
418         return response;
419     }
420 
421     auto fileSize = fs::file_size(value.fsPath);
422     if (offset >= fileSize)
423     {
424         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
425                         entry("FILE_SIZE=%d", fileSize));
426         encode_read_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE,
427                               length, responsePtr);
428         return response;
429     }
430 
431     if (offset + length > fileSize)
432     {
433         length = fileSize - offset;
434     }
435 
436     response.resize(response.size() + length);
437     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
438     auto fileDataPos = reinterpret_cast<char*>(responsePtr);
439     fileDataPos += sizeof(pldm_msg_hdr) + sizeof(uint8_t) + sizeof(length);
440 
441     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
442     stream.seekg(offset);
443     stream.read(fileDataPos, length);
444 
445     encode_read_file_resp(request->hdr.instance_id, PLDM_SUCCESS, length,
446                           responsePtr);
447 
448     return response;
449 }
450 
451 Response writeFile(const pldm_msg* request, size_t payloadLength)
452 {
453     uint32_t fileHandle = 0;
454     uint32_t offset = 0;
455     uint32_t length = 0;
456     size_t fileDataOffset = 0;
457 
458     Response response(sizeof(pldm_msg_hdr) + PLDM_WRITE_FILE_RESP_BYTES);
459     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
460 
461     if (payloadLength < PLDM_WRITE_FILE_REQ_BYTES)
462     {
463         encode_write_file_resp(request->hdr.instance_id,
464                                PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
465         return response;
466     }
467 
468     auto rc = decode_write_file_req(request, payloadLength, &fileHandle,
469                                     &offset, &length, &fileDataOffset);
470 
471     if (rc)
472     {
473         encode_write_file_resp(request->hdr.instance_id, rc, 0, responsePtr);
474         return response;
475     }
476 
477     using namespace pldm::filetable;
478     auto& table = buildFileTable(FILE_TABLE_JSON);
479     FileEntry value{};
480 
481     try
482     {
483         value = table.at(fileHandle);
484     }
485     catch (std::exception& e)
486     {
487         log<level::ERR>("File handle does not exist in the file table",
488                         entry("HANDLE=%d", fileHandle));
489         encode_write_file_resp(request->hdr.instance_id,
490                                PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
491         return response;
492     }
493 
494     if (!fs::exists(value.fsPath))
495     {
496         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
497         encode_write_file_resp(request->hdr.instance_id,
498                                PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
499         return response;
500     }
501 
502     auto fileSize = fs::file_size(value.fsPath);
503     if (offset >= fileSize)
504     {
505         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
506                         entry("FILE_SIZE=%d", fileSize));
507         encode_write_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE,
508                                0, responsePtr);
509         return response;
510     }
511 
512     auto fileDataPos =
513         reinterpret_cast<const char*>(request->payload) + fileDataOffset;
514 
515     std::ofstream stream(value.fsPath,
516                          std::ios::in | std::ios::out | std::ios::binary);
517     stream.seekp(offset);
518     stream.write(fileDataPos, length);
519 
520     encode_write_file_resp(request->hdr.instance_id, PLDM_SUCCESS, length,
521                            responsePtr);
522 
523     return response;
524 }
525 
526 } // namespace responder
527 } // namespace pldm
528