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