1 #include "config.h"
2 
3 #include "file_io.hpp"
4 
5 #include "file_table.hpp"
6 #include "libpldmresponder/utils.hpp"
7 #include "registration.hpp"
8 
9 #include <fcntl.h>
10 #include <sys/mman.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include <cstring>
16 #include <fstream>
17 #include <phosphor-logging/log.hpp>
18 
19 #include "libpldm/base.h"
20 
21 namespace pldm
22 {
23 
24 namespace responder
25 {
26 
27 namespace oem_ibm
28 {
29 
30 void registerHandlers()
31 {
32     registerHandler(PLDM_OEM, PLDM_GET_FILE_TABLE, std::move(getFileTable));
33     registerHandler(PLDM_OEM, PLDM_READ_FILE_INTO_MEMORY,
34                     std::move(readFileIntoMemory));
35     registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY,
36                     std::move(writeFileFromMemory));
37 }
38 
39 } // namespace oem_ibm
40 
41 namespace fs = std::filesystem;
42 using namespace phosphor::logging;
43 
44 namespace dma
45 {
46 
47 /** @struct AspeedXdmaOp
48  *
49  * Structure representing XDMA operation
50  */
51 struct AspeedXdmaOp
52 {
53     uint64_t hostAddr; //!< the DMA address on the host side, configured by
54                        //!< PCI subsystem.
55     uint32_t len;      //!< the size of the transfer in bytes, it should be a
56                        //!< multiple of 16 bytes
57     uint32_t upstream; //!< boolean indicating the direction of the DMA
58                        //!< operation, true means a transfer from BMC to host.
59 };
60 
61 constexpr auto xdmaDev = "/dev/xdma";
62 
63 int DMA::transferDataHost(const fs::path& path, uint32_t offset,
64                           uint32_t length, uint64_t address, bool upstream)
65 {
66     static const size_t pageSize = getpagesize();
67     uint32_t numPages = length / pageSize;
68     uint32_t pageAlignedLength = numPages * pageSize;
69 
70     if (length > pageAlignedLength)
71     {
72         pageAlignedLength += pageSize;
73     }
74 
75     auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
76         munmap(vgaMem, pageAlignedLength);
77     };
78 
79     int fd = -1;
80     int rc = 0;
81     fd = open(xdmaDev, O_RDWR);
82     if (fd < 0)
83     {
84         rc = -errno;
85         log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
86         return rc;
87     }
88 
89     utils::CustomFD xdmaFd(fd);
90 
91     void* vgaMem;
92     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
93                   MAP_SHARED, xdmaFd(), 0);
94     if (MAP_FAILED == vgaMem)
95     {
96         rc = -errno;
97         log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
98         return rc;
99     }
100 
101     std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
102 
103     if (upstream)
104     {
105         std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
106         stream.seekg(offset);
107 
108         // Writing to the VGA memory should be aligned at page boundary,
109         // otherwise write data into a buffer aligned at page boundary and
110         // then write to the VGA memory.
111         std::vector<char> buffer{};
112         buffer.resize(pageAlignedLength);
113         stream.read(buffer.data(), length);
114         memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
115                pageAlignedLength);
116 
117         if (static_cast<uint32_t>(stream.gcount()) != length)
118         {
119             log<level::ERR>("mismatch between number of characters to read and "
120                             "the length read",
121                             entry("LENGTH=%d", length),
122                             entry("COUNT=%d", stream.gcount()));
123             return -1;
124         }
125     }
126 
127     AspeedXdmaOp xdmaOp;
128     xdmaOp.upstream = upstream ? 1 : 0;
129     xdmaOp.hostAddr = address;
130     xdmaOp.len = length;
131 
132     rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
133     if (rc < 0)
134     {
135         rc = -errno;
136         log<level::ERR>("Failed to execute the DMA operation",
137                         entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
138                         entry("ADDRESS=%lld", address),
139                         entry("LENGTH=%d", length));
140         return rc;
141     }
142 
143     if (!upstream)
144     {
145         std::ofstream stream(path.string(),
146                              std::ios::in | std::ios::out | std::ios::binary);
147 
148         stream.seekp(offset);
149         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
150     }
151 
152     return 0;
153 }
154 
155 } // namespace dma
156 
157 Response readFileIntoMemory(const pldm_msg* request, 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(0, PLDM_READ_FILE_INTO_MEMORY,
170                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
171         return response;
172     }
173 
174     decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
175                               &offset, &length, &address);
176 
177     using namespace pldm::filetable;
178     auto& table = buildFileTable(FILE_TABLE_JSON);
179     FileEntry value{};
180 
181     try
182     {
183         value = table.at(fileHandle);
184     }
185     catch (std::exception& e)
186     {
187         log<level::ERR>("File handle does not exist in the file table",
188                         entry("HANDLE=%d", fileHandle));
189         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
190                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
191         return response;
192     }
193 
194     if (!fs::exists(value.fsPath))
195     {
196         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
197         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
198                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
199         return response;
200     }
201 
202     auto fileSize = fs::file_size(value.fsPath);
203     if (offset >= fileSize)
204     {
205         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
206                         entry("FILE_SIZE=%d", fileSize));
207         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
208                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
209         return response;
210     }
211 
212     if (offset + length > fileSize)
213     {
214         length = fileSize - offset;
215     }
216 
217     if (length % dma::minSize)
218     {
219         log<level::ERR>("Read length is not a multiple of DMA minSize",
220                         entry("LENGTH=%d", length));
221         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
222                                    PLDM_INVALID_READ_LENGTH, 0, responsePtr);
223         return response;
224     }
225 
226     using namespace dma;
227     DMA intf;
228     return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
229                             offset, length, address, true);
230 }
231 
232 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
233 {
234     uint32_t fileHandle = 0;
235     uint32_t offset = 0;
236     uint32_t length = 0;
237     uint64_t address = 0;
238 
239     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
240     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
241 
242     if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
243     {
244         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
245                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
246         return response;
247     }
248 
249     decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
250                               &offset, &length, &address);
251 
252     if (length % dma::minSize)
253     {
254         log<level::ERR>("Write length is not a multiple of DMA minSize",
255                         entry("LENGTH=%d", length));
256         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
257                                    PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
258         return response;
259     }
260 
261     using namespace pldm::filetable;
262     auto& table = buildFileTable(FILE_TABLE_JSON);
263     FileEntry value{};
264 
265     try
266     {
267         value = table.at(fileHandle);
268     }
269     catch (std::exception& e)
270     {
271         log<level::ERR>("File handle does not exist in the file table",
272                         entry("HANDLE=%d", fileHandle));
273         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
274                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
275         return response;
276     }
277 
278     if (!fs::exists(value.fsPath))
279     {
280         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
281         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
282                                    PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
283         return response;
284     }
285 
286     auto fileSize = fs::file_size(value.fsPath);
287     if (offset >= fileSize)
288     {
289         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
290                         entry("FILE_SIZE=%d", fileSize));
291         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
292                                    PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
293         return response;
294     }
295 
296     using namespace dma;
297     DMA intf;
298     return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
299                             offset, length, address, false);
300 }
301 
302 Response getFileTable(const pldm_msg* request, size_t payloadLength)
303 {
304     uint32_t transferHandle = 0;
305     uint8_t transferFlag = 0;
306     uint8_t tableType = 0;
307 
308     Response response(sizeof(pldm_msg_hdr) +
309                       PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
310     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
311 
312     if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
313     {
314         encode_get_file_table_resp(0, PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr,
315                                    0, responsePtr);
316         return response;
317     }
318 
319     auto rc =
320         decode_get_file_table_req(request->payload, payloadLength,
321                                   &transferHandle, &transferFlag, &tableType);
322     if (rc)
323     {
324         encode_get_file_table_resp(0, rc, 0, 0, nullptr, 0, responsePtr);
325         return response;
326     }
327 
328     if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
329     {
330         encode_get_file_table_resp(0, PLDM_INVALID_FILE_TABLE_TYPE, 0, 0,
331                                    nullptr, 0, responsePtr);
332         return response;
333     }
334 
335     using namespace pldm::filetable;
336     auto table = buildFileTable(FILE_TABLE_JSON);
337     auto attrTable = table();
338     response.resize(response.size() + attrTable.size());
339     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
340 
341     if (attrTable.empty())
342     {
343         encode_get_file_table_resp(0, PLDM_FILE_TABLE_UNAVAILABLE, 0, 0,
344                                    nullptr, 0, responsePtr);
345         return response;
346     }
347 
348     encode_get_file_table_resp(0, PLDM_SUCCESS, 0, PLDM_START_AND_END,
349                                attrTable.data(), attrTable.size(), responsePtr);
350     return response;
351 }
352 
353 } // namespace responder
354 } // namespace pldm
355