1 #include "file_io.hpp"
2 
3 #include <fcntl.h>
4 #include <sys/mman.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 
9 #include <cstring>
10 #include <fstream>
11 #include <phosphor-logging/log.hpp>
12 
13 #include "libpldm/base.h"
14 
15 namespace pldm
16 {
17 
18 namespace responder
19 {
20 
21 namespace fs = std::filesystem;
22 using namespace phosphor::logging;
23 
24 namespace dma
25 {
26 
27 /** @struct AspeedXdmaOp
28  *
29  * Structure representing XDMA operation
30  */
31 struct AspeedXdmaOp
32 {
33     uint64_t hostAddr; //!< the DMA address on the host side, configured by
34                        //!< PCI subsystem.
35     uint32_t len;      //!< the size of the transfer in bytes, it should be a
36                        //!< multiple of 16 bytes
37     uint32_t upstream; //!< boolean indicating the direction of the DMA
38                        //!< operation, true means a transfer from BMC to host.
39 };
40 
41 constexpr auto xdmaDev = "/dev/xdma";
42 
43 int DMA::transferDataHost(const fs::path& path, uint32_t offset,
44                           uint32_t length, uint64_t address, bool upstream)
45 {
46     static const size_t pageSize = getpagesize();
47     uint32_t numPages = length / pageSize;
48     uint32_t pageAlignedLength = numPages * pageSize;
49 
50     if (length > pageAlignedLength)
51     {
52         pageAlignedLength += pageSize;
53     }
54 
55     auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
56         munmap(vgaMem, pageAlignedLength);
57     };
58 
59     int fd = -1;
60     int rc = 0;
61     fd = open(xdmaDev, O_RDWR);
62     if (fd < 0)
63     {
64         rc = -errno;
65         log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
66         return rc;
67     }
68 
69     utils::CustomFD xdmaFd(fd);
70 
71     void* vgaMem;
72     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
73                   MAP_SHARED, xdmaFd(), 0);
74     if (MAP_FAILED == vgaMem)
75     {
76         rc = -errno;
77         log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
78         return rc;
79     }
80 
81     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
82 
83     if (upstream)
84     {
85         std::ifstream stream(path.string());
86 
87         stream.seekg(offset);
88         stream.read(static_cast<char*>(vgaMemPtr.get()), length);
89 
90         if (static_cast<uint32_t>(stream.gcount()) != length)
91         {
92             log<level::ERR>("mismatch between number of characters to read and "
93                             "the length read",
94                             entry("LENGTH=%d", length),
95                             entry("COUNT=%d", stream.gcount()));
96             return -1;
97         }
98     }
99 
100     AspeedXdmaOp xdmaOp;
101     xdmaOp.upstream = upstream ? 1 : 0;
102     xdmaOp.hostAddr = address;
103     xdmaOp.len = length;
104 
105     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
106     if (rc < 0)
107     {
108         rc = -errno;
109         log<level::ERR>("Failed to execute the DMA operation",
110                         entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
111                         entry("ADDRESS=%lld", address),
112                         entry("LENGTH=%d", length));
113         return rc;
114     }
115 
116     if (!upstream)
117     {
118         std::ofstream stream(path.string());
119 
120         stream.seekp(offset);
121         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
122     }
123 
124     return 0;
125 }
126 
127 } // namespace dma
128 
129 Response readFileIntoMemory(const uint8_t* request, size_t payloadLength)
130 {
131     uint32_t fileHandle = 0;
132     uint32_t offset = 0;
133     uint32_t length = 0;
134     uint64_t address = 0;
135     fs::path path("");
136 
137     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
138     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
139 
140     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
141     {
142         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
143                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
144         return response;
145     }
146 
147     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
148                               &length, &address);
149 
150     if (!fs::exists(path))
151     {
152         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
153         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
154                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
155         return response;
156     }
157 
158     auto fileSize = fs::file_size(path);
159     if (offset >= fileSize)
160     {
161         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
162                         entry("FILE_SIZE=%d", fileSize));
163         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
164                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
165         return response;
166     }
167 
168     if (offset + length > fileSize)
169     {
170         length = fileSize - offset;
171     }
172 
173     if (length % dma::minSize)
174     {
175         log<level::ERR>("Read length is not a multiple of DMA minSize",
176                         entry("LENGTH=%d", length));
177         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
178                                    PLDM_INVALID_READ_LENGTH, 0, responsePtr);
179         return response;
180     }
181 
182     using namespace dma;
183     DMA intf;
184     return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, path, offset,
185                             length, address, true);
186 }
187 
188 Response writeFileFromMemory(const uint8_t* request, size_t payloadLength)
189 {
190     uint32_t fileHandle = 0;
191     uint32_t offset = 0;
192     uint32_t length = 0;
193     uint64_t address = 0;
194     fs::path path("");
195 
196     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
197     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
198 
199     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
200     {
201         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
202                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
203         return response;
204     }
205 
206     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
207                               &length, &address);
208 
209     if (length % dma::minSize)
210     {
211         log<level::ERR>("Write length is not a multiple of DMA minSize",
212                         entry("LENGTH=%d", length));
213         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
214                                    PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
215         return response;
216     }
217 
218     if (!fs::exists(path))
219     {
220         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
221         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
222                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
223         return response;
224     }
225 
226     auto fileSize = fs::file_size(path);
227     if (offset >= fileSize)
228     {
229         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
230                         entry("FILE_SIZE=%d", fileSize));
231         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
232                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
233         return response;
234     }
235 
236     using namespace dma;
237     DMA intf;
238     return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, path, offset,
239                             length, address, false);
240 }
241 
242 } // namespace responder
243 } // namespace pldm
244