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