blob: 2fec526d759d2a743701574b7ab4e0fb9ddefc2c [file] [log] [blame]
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +05301#include "file_io.hpp"
2
Jinu Joy Thomasf666db12019-05-29 05:22:31 -05003#include "libpldmresponder/utils.hpp"
4#include "registration.hpp"
5
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +05306#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
18namespace pldm
19{
20
21namespace responder
22{
23
Jinu Joy Thomasf666db12019-05-29 05:22:31 -050024namespace oem_ibm
25{
26
27void 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
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +053037namespace fs = std::filesystem;
38using namespace phosphor::logging;
39
40namespace dma
41{
42
43/** @struct AspeedXdmaOp
44 *
45 * Structure representing XDMA operation
46 */
47struct 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
57constexpr auto xdmaDev = "/dev/xdma";
58
59int 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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500145Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength)
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530146{
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500163 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
164 &offset, &length, &address);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530165
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500204Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530205{
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500222 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
223 &offset, &length, &address);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530224
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