1 #include "file_io.hpp"
2 
3 #include "file_io_by_type.hpp"
4 #include "file_table.hpp"
5 #include "utils.hpp"
6 #include "xyz/openbmc_project/Common/error.hpp"
7 
8 #include <fcntl.h>
9 #include <libpldm/base.h>
10 #include <sys/mman.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include <phosphor-logging/lg2.hpp>
16 
17 #include <cstring>
18 #include <fstream>
19 #include <memory>
20 
21 PHOSPHOR_LOG2_USING;
22 
23 namespace pldm
24 {
25 using namespace pldm::responder::utils;
26 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
27 
28 namespace responder
29 {
30 namespace fs = std::filesystem;
31 
32 namespace dma
33 {
34 /** @struct AspeedXdmaOp
35  *
36  * Structure representing XDMA operation
37  */
38 struct AspeedXdmaOp
39 {
40     uint64_t hostAddr; //!< the DMA address on the host side, configured by
41                        //!< PCI subsystem.
42     uint32_t len;      //!< the size of the transfer in bytes, it should be a
43                        //!< multiple of 16 bytes
44     uint32_t upstream; //!< boolean indicating the direction of the DMA
45                        //!< operation, true means a transfer from BMC to host.
46 };
47 
48 constexpr auto xdmaDev = "/dev/aspeed-xdma";
49 
50 int DMA::transferHostDataToSocket(int fd, uint32_t length, uint64_t address)
51 {
52     static const size_t pageSize = getpagesize();
53     uint32_t numPages = length / pageSize;
54     uint32_t pageAlignedLength = numPages * pageSize;
55 
56     if (length > pageAlignedLength)
57     {
58         pageAlignedLength += pageSize;
59     }
60 
61     auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
62         munmap(vgaMem, pageAlignedLength);
63     };
64 
65     int dmaFd = -1;
66     int rc = 0;
67     dmaFd = open(xdmaDev, O_RDWR);
68     if (dmaFd < 0)
69     {
70         rc = -errno;
71         error(
72             "Failed to open the XDMA device for transferring remote terminus data to socket with response code '{RC}'",
73             "RC", rc);
74         return rc;
75     }
76 
77     pldm::utils::CustomFD xdmaFd(dmaFd);
78 
79     void* vgaMem;
80     vgaMem =
81         mmap(nullptr, pageAlignedLength, PROT_READ, MAP_SHARED, xdmaFd(), 0);
82     if (MAP_FAILED == vgaMem)
83     {
84         rc = -errno;
85         error(
86             "Failed to mmap the XDMA device for transferring remote terminus data to socket with response code '{RC}'",
87             "RC", rc);
88         return rc;
89     }
90 
91     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
92 
93     AspeedXdmaOp xdmaOp;
94     xdmaOp.upstream = 0;
95     xdmaOp.hostAddr = address;
96     xdmaOp.len = length;
97 
98     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
99     if (rc < 0)
100     {
101         rc = -errno;
102         error(
103             "Failed to execute the DMA operation for transferring remote terminus data to socket at address '{ADDRESS}' and length '{LENGTH}' with response code '{RC}'",
104             "RC", rc, "ADDRESS", address, "LENGTH", length);
105         return rc;
106     }
107 
108     rc = writeToUnixSocket(fd, static_cast<const char*>(vgaMemPtr.get()),
109                            length);
110     if (rc < 0)
111     {
112         rc = -errno;
113         close(fd);
114         error(
115             "Failed to write to Unix socket, closing socket for transferring remote terminus data to socket with response code '{RC}'",
116             "RC", rc);
117         return rc;
118     }
119     return 0;
120 }
121 
122 int DMA::transferDataHost(int fd, uint32_t offset, uint32_t length,
123                           uint64_t address, bool upstream)
124 {
125     static const size_t pageSize = getpagesize();
126     uint32_t numPages = length / pageSize;
127     uint32_t pageAlignedLength = numPages * pageSize;
128 
129     if (length > pageAlignedLength)
130     {
131         pageAlignedLength += pageSize;
132     }
133 
134     int rc = 0;
135     auto mmapCleanup = [pageAlignedLength, &rc](void* vgaMem) {
136         if (rc != -EINTR)
137         {
138             munmap(vgaMem, pageAlignedLength);
139         }
140         else
141         {
142             error(
143                 "Received interrupt during DMA transfer for data between BMC and remote terminus. Skipping Unmap.");
144         }
145     };
146 
147     int dmaFd = -1;
148     dmaFd = open(xdmaDev, O_RDWR);
149     if (dmaFd < 0)
150     {
151         rc = -errno;
152         error(
153             "Failed to open the XDMA device for data transfer between BMC and remote terminus with response code '{RC}'",
154             "RC", rc);
155         return rc;
156     }
157 
158     pldm::utils::CustomFD xdmaFd(dmaFd);
159 
160     void* vgaMem;
161     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
162                   MAP_SHARED, xdmaFd(), 0);
163     if (MAP_FAILED == vgaMem)
164     {
165         rc = -errno;
166         error(
167             "Failed to mmap the XDMA device for data transfer between BMC and remote terminus with response code '{RC}'",
168             "RC", rc);
169         return rc;
170     }
171 
172     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
173 
174     if (upstream)
175     {
176         rc = lseek(fd, offset, SEEK_SET);
177         if (rc == -1)
178         {
179             error(
180                 "Failed to transfer data between BMC and remote terminus due to lseek failure with upstream '{UPSTREAM}' at offset '{OFFSET}', error number - {ERROR_NUM}",
181                 "ERROR_NUM", errno, "UPSTREAM", upstream, "OFFSET", offset);
182             return rc;
183         }
184 
185         // Writing to the VGA memory should be aligned at page boundary,
186         // otherwise write data into a buffer aligned at page boundary and
187         // then write to the VGA memory.
188         std::vector<char> buffer{};
189         buffer.resize(pageAlignedLength);
190         rc = read(fd, buffer.data(), length);
191         if (rc == -1)
192         {
193             error(
194                 "Failed to transfer data between BMC and remote terminus with file read on upstream '{UPSTREAM}' of length '{LENGTH}' at offset '{OFFSET}' failed, error number - {ERROR_NUM}",
195                 "ERROR_NUM", errno, "UPSTREAM", upstream, "LENGTH", length,
196                 "OFFSET", offset);
197             return rc;
198         }
199         if (rc != static_cast<int>(length))
200         {
201             error(
202                 "Failed to transfer data between BMC and remote terminus mismatched for number of characters to read on upstream '{UPSTREAM}' and the length '{LENGTH}' read  and count '{RC}'",
203                 "UPSTREAM", upstream, "LENGTH", length, "RC", rc);
204             return -1;
205         }
206         memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
207                pageAlignedLength);
208     }
209 
210     AspeedXdmaOp xdmaOp;
211     xdmaOp.upstream = upstream ? 1 : 0;
212     xdmaOp.hostAddr = address;
213     xdmaOp.len = length;
214 
215     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
216     if (rc < 0)
217     {
218         rc = -errno;
219         error(
220             "Failed to execute the DMA operation on data between BMC and remote terminus for upstream '{UPSTREAM}' of length '{LENGTH}' at address '{ADDRESS}', response code '{RC}'",
221             "RC", rc, "UPSTREAM", upstream, "ADDRESS", address, "LENGTH",
222             length);
223         return rc;
224     }
225 
226     if (!upstream)
227     {
228         rc = lseek(fd, offset, SEEK_SET);
229         if (rc == -1)
230         {
231             error(
232                 "Failed to transfer data between BMC and remote terminus due to lseek failure '{UPSTREAM}' at offset '{OFFSET}' failed, error number - {ERROR_NUM}",
233                 "ERROR_NUM", errno, "UPSTREAM", upstream, "OFFSET", offset);
234             return rc;
235         }
236         rc = write(fd, static_cast<const char*>(vgaMemPtr.get()), length);
237         if (rc == -1)
238         {
239             error(
240                 "Failed to transfer data between BMC and remote terminus where file write upstream '{UPSTREAM}' of length '{LENGTH}' at offset '{OFFSET}' failed, error number - {ERROR_NUM}",
241                 "ERROR_NUM", errno, "UPSTREAM", upstream, "LENGTH", length,
242                 "OFFSET", offset);
243             return rc;
244         }
245     }
246 
247     return 0;
248 }
249 
250 } // namespace dma
251 
252 namespace oem_ibm
253 {
254 void encodeRWResponseHandler(uint8_t instance_id, uint8_t command,
255                              uint8_t completion_code, uint32_t length,
256                              struct pldm_msg* msg)
257 {
258     int rc = encode_rw_file_memory_resp(instance_id, command, completion_code,
259                                         length, msg);
260     if (rc != PLDM_SUCCESS)
261     {
262         error(
263             "Failed to encode response for command {COMMAND}, response code '{RC}'",
264             "COMMAND", command, "RC", rc);
265     }
266 }
267 
268 void encodeReadResponseHandler(uint8_t instance_id, uint8_t completion_code,
269                                uint32_t length, struct pldm_msg* msg)
270 {
271     int rc = encode_read_file_resp(instance_id, completion_code, length, msg);
272     if (rc != PLDM_SUCCESS)
273     {
274         error("Failed to encode read file response, response code '{RC}'", "RC",
275               rc);
276     }
277 }
278 
279 void encodeWriteResponseHandler(uint8_t instance_id, uint8_t completion_code,
280                                 uint32_t length, struct pldm_msg* msg)
281 {
282     int rc = encode_write_file_resp(instance_id, completion_code, length, msg);
283     if (rc != PLDM_SUCCESS)
284     {
285         error("Failed to encode write file response, response code '{RC}'",
286               "RC", rc);
287     }
288 }
289 
290 void encodeGetFileResponseHandler(
291     uint8_t instance_id, uint8_t completion_code, uint32_t next_transfer_handle,
292     uint8_t transfer_flag, const uint8_t* table_data, size_t table_size,
293     struct pldm_msg* msg)
294 {
295     int rc = encode_get_file_table_resp(instance_id, completion_code,
296                                         next_transfer_handle, transfer_flag,
297                                         table_data, table_size, msg);
298     if (rc != PLDM_SUCCESS)
299     {
300         error("Failed to encode get file table response, response code '{RC}'",
301               "RC", rc);
302     }
303 }
304 
305 void encodeRWTypeMemoryResponseHandler(uint8_t instance_id, uint8_t command,
306                                        uint8_t completion_code, uint32_t length,
307                                        struct pldm_msg* msg)
308 {
309     int rc = encode_rw_file_by_type_memory_resp(instance_id, command,
310                                                 completion_code, length, msg);
311     if (rc != PLDM_SUCCESS)
312     {
313         error(
314             "Failed to encode read/write file by type memory response, response code '{RC}'",
315             "RC", rc);
316     }
317 }
318 
319 void encodeRWTypeResponseHandler(uint8_t instance_id, uint8_t command,
320                                  uint8_t completion_code, uint32_t length,
321                                  struct pldm_msg* msg)
322 {
323     int rc = encode_rw_file_by_type_resp(instance_id, command, completion_code,
324                                          length, msg);
325     if (rc != PLDM_SUCCESS)
326     {
327         error(
328             "Failed to encode response for command {COMMAND}, response code '{RC}'",
329             "COMMAND", command, "RC", rc);
330     }
331 }
332 
333 void encodeFileAckResponseHandler(uint8_t instance_id, uint8_t completion_code,
334                                   struct pldm_msg* msg)
335 {
336     int rc = encode_file_ack_resp(instance_id, completion_code, msg);
337     if (rc != PLDM_SUCCESS)
338     {
339         error("Failed to encode file ack response, response code '{RC}'", "RC",
340               rc);
341     }
342 }
343 
344 Response Handler::readFileIntoMemory(const pldm_msg* request,
345                                      size_t payloadLength)
346 {
347     uint32_t fileHandle = 0;
348     uint32_t offset = 0;
349     uint32_t length = 0;
350     uint64_t address = 0;
351 
352     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
353     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
354     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
355     {
356         error(
357             "Failed to read file into memory as payload length '{LENGTH}' not equal to '{REQ_LENGTH}'",
358             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_RW_FILE_MEM_REQ_BYTES);
359         encodeRWResponseHandler(request->hdr.instance_id,
360                                 PLDM_READ_FILE_INTO_MEMORY,
361                                 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
362         return response;
363     }
364 
365     int responseCode = decode_rw_file_memory_req(
366         request, payloadLength, &fileHandle, &offset, &length, &address);
367     if (responseCode != PLDM_SUCCESS)
368     {
369         error(
370             "Failed to decode read file into memory request, response code '{RC}'",
371             "RC", responseCode);
372     }
373 
374     using namespace pldm::filetable;
375     auto& table = buildFileTable(FILE_TABLE_JSON);
376     FileEntry value{};
377 
378     try
379     {
380         value = table.at(fileHandle);
381     }
382     catch (const std::exception& e)
383     {
384         error(
385             "File handle '{HANDLE}' does not exist in the file table, error - {ERROR}",
386             "HANDLE", fileHandle, "ERROR", e);
387         encodeRWResponseHandler(request->hdr.instance_id,
388                                 PLDM_READ_FILE_INTO_MEMORY,
389                                 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
390         return response;
391     }
392 
393     if (!fs::exists(value.fsPath))
394     {
395         error("File '{PATH}' and handle '{FILE_HANDLE}' with does not exist",
396               "PATH", value.fsPath, "FILE_HANDLE", fileHandle);
397         encodeRWResponseHandler(request->hdr.instance_id,
398                                 PLDM_READ_FILE_INTO_MEMORY,
399                                 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
400         return response;
401     }
402 
403     auto fileSize = fs::file_size(value.fsPath);
404     if (!fileSize)
405     {
406         error(
407             "Failed to PLDM_READ_FILE_INTO_MEMORY from file {PATH} with size '{SIZE}'",
408             "PATH", value.fsPath, "SIZE", fileSize);
409         encodeRWResponseHandler(request->hdr.instance_id,
410                                 PLDM_READ_FILE_INTO_MEMORY,
411                                 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
412         return response;
413     }
414     if (offset >= fileSize)
415     {
416         error(
417             "Offset '{OFFSET}' exceeds file size '{SIZE}' and file handle '{FILE_HANDLE}'",
418             "OFFSET", offset, "SIZE", fileSize, "FILE_HANDLE", fileHandle);
419         encodeRWResponseHandler(request->hdr.instance_id,
420                                 PLDM_READ_FILE_INTO_MEMORY,
421                                 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
422         return response;
423     }
424 
425     if (offset + length > fileSize)
426     {
427         length = fileSize - offset;
428     }
429 
430     if (!length || length % dma::minSize)
431     {
432         error("Packet length '{LENGTH}' is non multiple of minimum DMA size",
433               "LENGTH", length);
434         encodeRWResponseHandler(request->hdr.instance_id,
435                                 PLDM_READ_FILE_INTO_MEMORY,
436                                 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
437         return response;
438     }
439 
440     using namespace dma;
441     DMA intf;
442     return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
443                             offset, length, address, true,
444                             request->hdr.instance_id);
445 }
446 
447 Response Handler::writeFileFromMemory(const pldm_msg* request,
448                                       size_t payloadLength)
449 {
450     uint32_t fileHandle = 0;
451     uint32_t offset = 0;
452     uint32_t length = 0;
453     uint64_t address = 0;
454 
455     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
456     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
457 
458     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
459     {
460         error(
461             "Failed to write file from memory as payload length '{LENGTH}' not equal to '{REQ_LENGTH}'",
462             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_RW_FILE_MEM_REQ_BYTES);
463         encodeRWResponseHandler(request->hdr.instance_id,
464                                 PLDM_WRITE_FILE_FROM_MEMORY,
465                                 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
466         return response;
467     }
468 
469     int responseCode = decode_rw_file_memory_req(
470         request, payloadLength, &fileHandle, &offset, &length, &address);
471     if (responseCode != PLDM_SUCCESS)
472     {
473         error(
474             "Failed to decode write file from memory request, response code '{RC}'",
475             "RC", responseCode);
476     }
477 
478     if (!length || length % dma::minSize)
479     {
480         error("Packet length '{LENGTH}' is non multiple of minimum DMA size",
481               "LENGTH", length);
482         encodeRWResponseHandler(request->hdr.instance_id,
483                                 PLDM_WRITE_FILE_FROM_MEMORY,
484                                 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
485         return response;
486     }
487 
488     using namespace pldm::filetable;
489     auto& table = buildFileTable(FILE_TABLE_JSON);
490     FileEntry value{};
491 
492     try
493     {
494         value = table.at(fileHandle);
495     }
496     catch (const std::exception& e)
497     {
498         error(
499             "File handle '{HANDLE}' does not exist in the file table, error - {ERROR}",
500             "HANDLE", fileHandle, "ERROR", e);
501         encodeRWResponseHandler(request->hdr.instance_id,
502                                 PLDM_WRITE_FILE_FROM_MEMORY,
503                                 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
504         return response;
505     }
506 
507     if (!fs::exists(value.fsPath))
508     {
509         error("File '{PATH}' does not exist for file handle '{FILE_HANDLE}'",
510               "PATH", value.fsPath, "FILE_HANDLE", fileHandle);
511         encodeRWResponseHandler(request->hdr.instance_id,
512                                 PLDM_WRITE_FILE_FROM_MEMORY,
513                                 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
514         return response;
515     }
516 
517     auto fileSize = fs::file_size(value.fsPath);
518     if (!fileSize)
519     {
520         info(
521             "File '{PATH}' has size '{SIZE}' for command PLDM_WRITE_FILE_FROM_MEMORY",
522             "PATH", value.fsPath, "SIZE", fileSize);
523     }
524     if (offset >= fileSize)
525     {
526         error(
527             "Offset '{OFFSET}' exceeds file size {SIZE} for file '{PATH} and handle {FILE_HANDLE}",
528             "OFFSET", offset, "SIZE", fileSize, "PATH", value.fsPath,
529             "FILE_HANDLE", fileHandle);
530         encodeRWResponseHandler(request->hdr.instance_id,
531                                 PLDM_WRITE_FILE_FROM_MEMORY,
532                                 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
533         return response;
534     }
535 
536     using namespace dma;
537     DMA intf;
538     return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
539                             offset, length, address, false,
540                             request->hdr.instance_id);
541 }
542 
543 Response Handler::getFileTable(const pldm_msg* request, size_t payloadLength)
544 {
545     uint32_t transferHandle = 0;
546     uint8_t transferFlag = 0;
547     uint8_t tableType = 0;
548 
549     Response response(
550         sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
551     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
552 
553     if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
554     {
555         error(
556             "Failed to get file table as payload length '{LENGTH}' not equal to required length '{REQ_LENGTH}'",
557             "LENGTH", payloadLength, "REQ_LENGTH",
558             PLDM_GET_FILE_TABLE_REQ_BYTES);
559         encodeGetFileResponseHandler(request->hdr.instance_id,
560                                      PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr,
561                                      0, responsePtr);
562         return response;
563     }
564 
565     auto rc = decode_get_file_table_req(request, payloadLength, &transferHandle,
566                                         &transferFlag, &tableType);
567     if (rc)
568     {
569         error("Failed to decode get file table request, response code '{RC}'",
570               "RC", rc);
571         encodeGetFileResponseHandler(request->hdr.instance_id, rc, 0, 0,
572                                      nullptr, 0, responsePtr);
573         return response;
574     }
575 
576     if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
577     {
578         error(
579             "Failed to match table type '{TYPE}' with expected table type '{REQ_TYPE}'",
580             "TYPE", tableType, "REQ_TYPE", PLDM_FILE_ATTRIBUTE_TABLE);
581         encodeGetFileResponseHandler(request->hdr.instance_id,
582                                      PLDM_INVALID_FILE_TABLE_TYPE, 0, 0,
583                                      nullptr, 0, responsePtr);
584         return response;
585     }
586 
587     using namespace pldm::filetable;
588     auto table = buildFileTable(FILE_TABLE_JSON);
589     auto attrTable = table();
590     response.resize(response.size() + attrTable.size());
591     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
592 
593     if (attrTable.empty())
594     {
595         error("PLDM file attribute table is empty");
596         encodeGetFileResponseHandler(request->hdr.instance_id,
597                                      PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, nullptr,
598                                      0, responsePtr);
599         return response;
600     }
601 
602     encodeGetFileResponseHandler(request->hdr.instance_id, PLDM_SUCCESS, 0,
603                                  PLDM_START_AND_END, attrTable.data(),
604                                  attrTable.size(), responsePtr);
605     return response;
606 }
607 
608 Response Handler::readFile(const pldm_msg* request, size_t payloadLength)
609 {
610     uint32_t fileHandle = 0;
611     uint32_t offset = 0;
612     uint32_t length = 0;
613 
614     Response response(sizeof(pldm_msg_hdr) + PLDM_READ_FILE_RESP_BYTES);
615     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
616 
617     if (payloadLength != PLDM_READ_FILE_REQ_BYTES)
618     {
619         error(
620             "Failed to read file as payload length '{LENGTH}' not equal to '{REQ_LENGTH}'",
621             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_READ_FILE_REQ_BYTES);
622         encodeReadResponseHandler(request->hdr.instance_id,
623                                   PLDM_ERROR_INVALID_LENGTH, length,
624                                   responsePtr);
625         return response;
626     }
627 
628     auto rc = decode_read_file_req(request, payloadLength, &fileHandle, &offset,
629                                    &length);
630 
631     if (rc)
632     {
633         error("Failed to decode read file request, response code '{RC}'", "RC",
634               rc);
635         encodeReadResponseHandler(request->hdr.instance_id, rc, 0, responsePtr);
636         return response;
637     }
638 
639     using namespace pldm::filetable;
640     auto& table = buildFileTable(FILE_TABLE_JSON);
641     FileEntry value{};
642 
643     try
644     {
645         value = table.at(fileHandle);
646     }
647     catch (const std::exception& e)
648     {
649         error(
650             "File handle '{HANDLE}' does not exist in the file table, error - {ERROR}",
651             "HANDLE", fileHandle, "ERROR", e);
652 
653         encodeReadResponseHandler(request->hdr.instance_id,
654                                   PLDM_INVALID_FILE_HANDLE, length,
655                                   responsePtr);
656         return response;
657     }
658 
659     if (!fs::exists(value.fsPath))
660     {
661         error("File '{PATH}' and handle {FILE_HANDLE} does not exist", "PATH",
662               value.fsPath, "FILE_HANDLE", fileHandle);
663         encodeReadResponseHandler(request->hdr.instance_id,
664                                   PLDM_INVALID_FILE_HANDLE, length,
665                                   responsePtr);
666         return response;
667     }
668 
669     auto fileSize = fs::file_size(value.fsPath);
670     if (!fileSize)
671     {
672         error("Failed to read file {PATH} with size '{SIZE}'", "PATH",
673               value.fsPath, "SIZE", fileSize);
674         encodeRWResponseHandler(request->hdr.instance_id,
675                                 PLDM_READ_FILE_INTO_MEMORY,
676                                 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
677         return response;
678     }
679 
680     if (offset >= fileSize)
681     {
682         error(
683             "Offset '{OFFSET}' exceeds file size '{SIZE}' for file '{PATH}' and file handle '{HANDLE}'",
684             "OFFSET", offset, "SIZE", fileSize, "PATH", value.fsPath, "HANDLE",
685             fileHandle);
686         encodeReadResponseHandler(request->hdr.instance_id,
687                                   PLDM_DATA_OUT_OF_RANGE, length, responsePtr);
688         return response;
689     }
690 
691     if (offset + length > fileSize)
692     {
693         length = fileSize - offset;
694     }
695 
696     response.resize(response.size() + length);
697     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
698     auto fileDataPos = reinterpret_cast<char*>(responsePtr);
699     fileDataPos += sizeof(pldm_msg_hdr) + sizeof(uint8_t) + sizeof(length);
700 
701     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
702     stream.seekg(offset);
703     stream.read(fileDataPos, length);
704 
705     encodeReadResponseHandler(request->hdr.instance_id, PLDM_SUCCESS, length,
706                               responsePtr);
707 
708     return response;
709 }
710 
711 Response Handler::writeFile(const pldm_msg* request, size_t payloadLength)
712 {
713     uint32_t fileHandle = 0;
714     uint32_t offset = 0;
715     uint32_t length = 0;
716     size_t fileDataOffset = 0;
717 
718     Response response(sizeof(pldm_msg_hdr) + PLDM_WRITE_FILE_RESP_BYTES);
719     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
720 
721     if (payloadLength < PLDM_WRITE_FILE_REQ_BYTES)
722     {
723         error(
724             "Failed to write file as payload length '{LENGTH}' less than '{REQ_LENGTH}'",
725             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_WRITE_FILE_REQ_BYTES);
726         encodeWriteResponseHandler(request->hdr.instance_id,
727                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
728         return response;
729     }
730 
731     auto rc = decode_write_file_req(request, payloadLength, &fileHandle,
732                                     &offset, &length, &fileDataOffset);
733 
734     if (rc)
735     {
736         error("Failed to decode write file request, response code '{RC}'", "RC",
737               rc);
738         encodeWriteResponseHandler(request->hdr.instance_id, rc, 0,
739                                    responsePtr);
740         return response;
741     }
742 
743     using namespace pldm::filetable;
744     auto& table = buildFileTable(FILE_TABLE_JSON);
745     FileEntry value{};
746 
747     try
748     {
749         value = table.at(fileHandle);
750     }
751     catch (const std::exception& e)
752     {
753         error(
754             "File handle '{HANDLE}' does not exist in the file table, error - {ERROR}",
755             "HANDLE", fileHandle, "ERROR", e);
756         encodeWriteResponseHandler(request->hdr.instance_id,
757                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
758         return response;
759     }
760 
761     if (!fs::exists(value.fsPath))
762     {
763         error("File '{PATH}' and handle {FILE_HANDLE} does not exist", "PATH",
764               value.fsPath, "FILE_HANDLE", fileHandle);
765         encodeWriteResponseHandler(request->hdr.instance_id,
766                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
767         return response;
768     }
769 
770     auto fileSize = fs::file_size(value.fsPath);
771 
772     if (!fileSize)
773     {
774         info("File {PATH} has size '{SIZE}' for write file command", "PATH",
775              value.fsPath, "SIZE", fileSize);
776     }
777 
778     if (offset >= fileSize)
779     {
780         error(
781             "Offset '{OFFSET}' exceeds file size '{SIZE}' for file '{PATH}' and handle {FILE_HANDLE}",
782             "OFFSET", offset, "SIZE", fileSize, "PATH", value.fsPath,
783             "FILE_HANDLE", fileHandle);
784         encodeWriteResponseHandler(request->hdr.instance_id,
785                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
786         return response;
787     }
788 
789     auto fileDataPos =
790         reinterpret_cast<const char*>(request->payload) + fileDataOffset;
791 
792     std::ofstream stream(value.fsPath,
793                          std::ios::in | std::ios::out | std::ios::binary);
794     stream.seekp(offset);
795     stream.write(fileDataPos, length);
796 
797     encodeWriteResponseHandler(request->hdr.instance_id, PLDM_SUCCESS, length,
798                                responsePtr);
799 
800     return response;
801 }
802 
803 Response rwFileByTypeIntoMemory(uint8_t cmd, const pldm_msg* request,
804                                 size_t payloadLength,
805                                 oem_platform::Handler* oemPlatformHandler)
806 {
807     Response response(
808         sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_MEM_RESP_BYTES, 0);
809     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
810 
811     if (payloadLength != PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES)
812     {
813         error(
814             "Failed to read file into memory as payload length '{LENGTH}' not equal to '{REQ_LENGTH}'",
815             "LENGTH", payloadLength, "REQ_LENGTH",
816             PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
817         encodeRWTypeMemoryResponseHandler(request->hdr.instance_id, cmd,
818                                           PLDM_ERROR_INVALID_LENGTH, 0,
819                                           responsePtr);
820         return response;
821     }
822 
823     uint16_t fileType{};
824     uint32_t fileHandle{};
825     uint32_t offset{};
826     uint32_t length{};
827     uint64_t address{};
828     auto rc = decode_rw_file_by_type_memory_req(
829         request, payloadLength, &fileType, &fileHandle, &offset, &length,
830         &address);
831     if (rc != PLDM_SUCCESS)
832     {
833         error(
834             "Failed to decode read/write file by type memory request, response code '{RC}'",
835             "RC", rc);
836         encodeRWTypeMemoryResponseHandler(request->hdr.instance_id, cmd, rc, 0,
837                                           responsePtr);
838         return response;
839     }
840     if (!length || length % dma::minSize)
841     {
842         error(
843             "Packet length '{LENGTH}' is non multiple of minimum DMA size for command {CMD}",
844             "LENGTH", length, "CMD", cmd);
845         encodeRWTypeMemoryResponseHandler(request->hdr.instance_id, cmd,
846                                           PLDM_ERROR_INVALID_LENGTH, 0,
847                                           responsePtr);
848         return response;
849     }
850 
851     std::unique_ptr<FileHandler> handler{};
852     try
853     {
854         handler = getHandlerByType(fileType, fileHandle);
855     }
856     catch (const InternalFailure& e)
857     {
858         error("Unknown file type '{TYPE}', error - {ERROR} ", "TYPE", fileType,
859               "ERROR", e);
860         encodeRWTypeMemoryResponseHandler(request->hdr.instance_id, cmd,
861                                           PLDM_INVALID_FILE_TYPE, 0,
862                                           responsePtr);
863         return response;
864     }
865 
866     rc = cmd == PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY
867              ? handler->writeFromMemory(offset, length, address,
868                                         oemPlatformHandler)
869              : handler->readIntoMemory(offset, length, address,
870                                        oemPlatformHandler);
871     encodeRWTypeMemoryResponseHandler(request->hdr.instance_id, cmd, rc, length,
872                                       responsePtr);
873     return response;
874 }
875 
876 Response Handler::writeFileByTypeFromMemory(const pldm_msg* request,
877                                             size_t payloadLength)
878 {
879     return rwFileByTypeIntoMemory(PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY, request,
880                                   payloadLength, oemPlatformHandler);
881 }
882 
883 Response Handler::readFileByTypeIntoMemory(const pldm_msg* request,
884                                            size_t payloadLength)
885 {
886     return rwFileByTypeIntoMemory(PLDM_READ_FILE_BY_TYPE_INTO_MEMORY, request,
887                                   payloadLength, oemPlatformHandler);
888 }
889 
890 Response Handler::writeFileByType(const pldm_msg* request, size_t payloadLength)
891 {
892     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_RESP_BYTES);
893     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
894 
895     if (payloadLength < PLDM_RW_FILE_BY_TYPE_REQ_BYTES)
896     {
897         error(
898             "Failed to write file by type as payload length '{LENGTH}' less than '{REQ_LENGTH}'",
899             "LENGTH", payloadLength, "REQ_LENGTH",
900             PLDM_RW_FILE_BY_TYPE_REQ_BYTES);
901         encodeRWTypeResponseHandler(request->hdr.instance_id,
902                                     PLDM_WRITE_FILE_BY_TYPE,
903                                     PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
904         return response;
905     }
906     uint16_t fileType{};
907     uint32_t fileHandle{};
908     uint32_t offset{};
909     uint32_t length{};
910 
911     auto rc = decode_rw_file_by_type_req(request, payloadLength, &fileType,
912                                          &fileHandle, &offset, &length);
913     if (rc != PLDM_SUCCESS)
914     {
915         error("Failed decoded write file by type request, response code '{RC}'",
916               "RC", rc);
917         encodeRWTypeResponseHandler(request->hdr.instance_id,
918                                     PLDM_WRITE_FILE_BY_TYPE, rc, 0,
919                                     responsePtr);
920         return response;
921     }
922 
923     std::unique_ptr<FileHandler> handler{};
924     try
925     {
926         handler = getHandlerByType(fileType, fileHandle);
927     }
928     catch (const InternalFailure& e)
929     {
930         error("Unknown file type '{TYPE}', error - {ERROR}", "TYPE", fileType,
931               "ERROR", e);
932         encodeRWTypeResponseHandler(request->hdr.instance_id,
933                                     PLDM_WRITE_FILE_BY_TYPE,
934                                     PLDM_INVALID_FILE_TYPE, 0, responsePtr);
935         return response;
936     }
937 
938     rc = handler->write(reinterpret_cast<const char*>(
939                             request->payload + PLDM_RW_FILE_BY_TYPE_REQ_BYTES),
940                         offset, length, oemPlatformHandler);
941     encodeRWTypeResponseHandler(request->hdr.instance_id,
942                                 PLDM_WRITE_FILE_BY_TYPE, rc, length,
943                                 responsePtr);
944     return response;
945 }
946 
947 Response Handler::readFileByType(const pldm_msg* request, size_t payloadLength)
948 {
949     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_RESP_BYTES);
950     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
951 
952     if (payloadLength != PLDM_RW_FILE_BY_TYPE_REQ_BYTES)
953     {
954         error(
955             "Failed to read file by type as payload length '{LENGTH}' less than '{REQ_LENGTH}'",
956             "LENGTH", payloadLength, "REQ_LENGTH",
957             PLDM_RW_FILE_BY_TYPE_REQ_BYTES);
958         encodeRWTypeResponseHandler(request->hdr.instance_id,
959                                     PLDM_READ_FILE_BY_TYPE,
960                                     PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
961         return response;
962     }
963     uint16_t fileType{};
964     uint32_t fileHandle{};
965     uint32_t offset{};
966     uint32_t length{};
967 
968     auto rc = decode_rw_file_by_type_req(request, payloadLength, &fileType,
969                                          &fileHandle, &offset, &length);
970     if (rc != PLDM_SUCCESS)
971     {
972         error(
973             "Failed to decode read file by type request, response code '{RC}'",
974             "RC", rc);
975         encodeRWTypeResponseHandler(request->hdr.instance_id,
976                                     PLDM_READ_FILE_BY_TYPE, rc, 0, responsePtr);
977         return response;
978     }
979 
980     std::unique_ptr<FileHandler> handler{};
981     try
982     {
983         handler = getHandlerByType(fileType, fileHandle);
984     }
985     catch (const InternalFailure& e)
986     {
987         error("Unknown file type '{TYPE}', error - {ERROR}", "TYPE", fileType,
988               "ERROR", e);
989         encodeRWTypeResponseHandler(request->hdr.instance_id,
990                                     PLDM_READ_FILE_BY_TYPE,
991                                     PLDM_INVALID_FILE_TYPE, 0, responsePtr);
992         return response;
993     }
994 
995     rc = handler->read(offset, length, response, oemPlatformHandler);
996     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
997     encodeRWTypeResponseHandler(request->hdr.instance_id,
998                                 PLDM_READ_FILE_BY_TYPE, rc, length,
999                                 responsePtr);
1000     return response;
1001 }
1002 
1003 Response Handler::fileAck(const pldm_msg* request, size_t payloadLength)
1004 {
1005     Response response(sizeof(pldm_msg_hdr) + PLDM_FILE_ACK_RESP_BYTES);
1006     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
1007 
1008     if (payloadLength != PLDM_FILE_ACK_REQ_BYTES)
1009     {
1010         error(
1011             "Failed to do file ack as payload length '{LENGTH}' is less than '{REQ_LENGTH}'",
1012             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_FILE_ACK_REQ_BYTES);
1013         encodeFileAckResponseHandler(request->hdr.instance_id,
1014                                      PLDM_ERROR_INVALID_LENGTH, responsePtr);
1015         return response;
1016     }
1017     uint16_t fileType{};
1018     uint32_t fileHandle{};
1019     uint8_t fileStatus{};
1020 
1021     auto rc = decode_file_ack_req(request, payloadLength, &fileType,
1022                                   &fileHandle, &fileStatus);
1023     if (rc != PLDM_SUCCESS)
1024     {
1025         encodeFileAckResponseHandler(request->hdr.instance_id, rc, responsePtr);
1026         return response;
1027     }
1028 
1029     std::unique_ptr<FileHandler> handler{};
1030     try
1031     {
1032         handler = getHandlerByType(fileType, fileHandle);
1033     }
1034 
1035     catch (const InternalFailure& e)
1036     {
1037         error("Unknown file type '{TYPE}', error - {ERROR}", "TYPE", fileType,
1038               "ERROR", e);
1039         encodeFileAckResponseHandler(request->hdr.instance_id,
1040                                      PLDM_INVALID_FILE_TYPE, responsePtr);
1041         return response;
1042     }
1043 
1044     rc = handler->fileAck(fileStatus);
1045     encodeFileAckResponseHandler(request->hdr.instance_id, rc, responsePtr);
1046     return response;
1047 }
1048 
1049 Response Handler::getAlertStatus(const pldm_msg* request, size_t payloadLength)
1050 {
1051     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_ALERT_STATUS_RESP_BYTES);
1052     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
1053     if (payloadLength != PLDM_GET_ALERT_STATUS_REQ_BYTES)
1054     {
1055         error(
1056             "Failed to get alert status as payload length '{LENGTH}' is less than '{REQ_LENGTH}'",
1057             "LENGTH", payloadLength, "REQ_LENGTH",
1058             PLDM_GET_ALERT_STATUS_REQ_BYTES);
1059         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
1060     }
1061 
1062     uint8_t versionId{};
1063 
1064     auto rc = decode_get_alert_status_req(request, payloadLength, &versionId);
1065     if (rc != PLDM_SUCCESS)
1066     {
1067         error("Failed to decode get alert status request, response code '{RC}'",
1068               "RC", rc);
1069         return CmdHandler::ccOnlyResponse(request, rc);
1070     }
1071 
1072     if (versionId != 0)
1073     {
1074         error(
1075             "Failed to get alert status due to unsupported version ID '{VERSION}'",
1076             "VERSION", versionId);
1077         return CmdHandler::ccOnlyResponse(request,
1078                                           PLDM_HOST_UNSUPPORTED_FORMAT_VERSION);
1079     }
1080 
1081     constexpr uint32_t rackEntry = 0xFF000030;
1082     constexpr uint32_t priCecNode = 0x00008030;
1083     rc = encode_get_alert_status_resp(request->hdr.instance_id, PLDM_SUCCESS,
1084                                       rackEntry, priCecNode, responsePtr,
1085                                       PLDM_GET_ALERT_STATUS_RESP_BYTES);
1086     if (rc != PLDM_SUCCESS)
1087     {
1088         error(
1089             "Failed to encode get alert status response, response code '{RC}'",
1090             "RC", rc);
1091         return CmdHandler::ccOnlyResponse(request, rc);
1092     }
1093 
1094     return response;
1095 }
1096 
1097 Response Handler::newFileAvailable(const pldm_msg* request,
1098                                    size_t payloadLength)
1099 {
1100     Response response(sizeof(pldm_msg_hdr) + PLDM_NEW_FILE_RESP_BYTES);
1101 
1102     if (payloadLength != PLDM_NEW_FILE_REQ_BYTES)
1103     {
1104         error(
1105             "Failed new file available as payload length '{LENGTH}' is less than '{REQ_LENGTH}'",
1106             "LENGTH", payloadLength, "REQ_LENGTH", PLDM_NEW_FILE_REQ_BYTES);
1107         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
1108     }
1109     uint16_t fileType{};
1110     uint32_t fileHandle{};
1111     uint64_t length{};
1112 
1113     auto rc = decode_new_file_req(request, payloadLength, &fileType,
1114                                   &fileHandle, &length);
1115 
1116     if (rc != PLDM_SUCCESS)
1117     {
1118         error("Failed to decode new file request, response code '{RC}'", "RC",
1119               rc);
1120         return CmdHandler::ccOnlyResponse(request, rc);
1121     }
1122 
1123     std::unique_ptr<FileHandler> handler{};
1124     try
1125     {
1126         handler = getHandlerByType(fileType, fileHandle);
1127     }
1128     catch (const InternalFailure& e)
1129     {
1130         error("Unknown file type '{TYPE}', error - {ERROR}", "TYPE", fileType,
1131               "ERROR", e);
1132         return CmdHandler::ccOnlyResponse(request, PLDM_INVALID_FILE_TYPE);
1133     }
1134 
1135     rc = handler->newFileAvailable(length);
1136     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
1137     int responseCode =
1138         encode_new_file_resp(request->hdr.instance_id, rc, responsePtr);
1139     if (responseCode != PLDM_SUCCESS)
1140     {
1141         error(
1142             "Failed to encode new file available response, response code '{RC}'",
1143             "RC", responseCode);
1144     }
1145     return response;
1146 }
1147 
1148 } // namespace oem_ibm
1149 } // namespace responder
1150 } // namespace pldm
1151