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