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