blob: 6b893edd1a6f1e828fa44191947fc66b9759581f [file] [log] [blame]
Tom Joseph0c6d22c2019-06-26 09:58:41 +05301#include "config.h"
2
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +05303#include "file_io.hpp"
4
Tom Joseph0c6d22c2019-06-26 09:58:41 +05305#include "file_table.hpp"
Jinu Joy Thomasf666db12019-05-29 05:22:31 -05006#include "libpldmresponder/utils.hpp"
7#include "registration.hpp"
8
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +05309#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
21namespace pldm
22{
23
24namespace responder
25{
26
Jinu Joy Thomasf666db12019-05-29 05:22:31 -050027namespace oem_ibm
28{
29
30void registerHandlers()
31{
Tom Joseph0c6d22c2019-06-26 09:58:41 +053032 registerHandler(PLDM_OEM, PLDM_GET_FILE_TABLE, std::move(getFileTable));
Jinu Joy Thomasf666db12019-05-29 05:22:31 -050033 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
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +053041namespace fs = std::filesystem;
42using namespace phosphor::logging;
43
44namespace dma
45{
46
47/** @struct AspeedXdmaOp
48 *
49 * Structure representing XDMA operation
50 */
51struct 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
61constexpr auto xdmaDev = "/dev/xdma";
62
63int 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 {
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530105 std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530106 stream.seekg(offset);
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530107
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);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530116
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 {
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530145 std::ofstream stream(path.string(),
146 std::ios::in | std::ios::out | std::ios::binary);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530147
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500157Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength)
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530158{
159 uint32_t fileHandle = 0;
160 uint32_t offset = 0;
161 uint32_t length = 0;
162 uint64_t address = 0;
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530163
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500174 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
175 &offset, &length, &address);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530176
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530177 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))
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530195 {
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
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530202 auto fileSize = fs::file_size(value.fsPath);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530203 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;
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530228 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
229 offset, length, address, true);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530230}
231
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500232Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530233{
234 uint32_t fileHandle = 0;
235 uint32_t offset = 0;
236 uint32_t length = 0;
237 uint64_t address = 0;
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530238
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
Jinu Joy Thomasf666db12019-05-29 05:22:31 -0500249 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
250 &offset, &length, &address);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530251
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
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530261 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))
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530279 {
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
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530286 auto fileSize = fs::file_size(value.fsPath);
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530287 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;
Tom Joseph0c6d22c2019-06-26 09:58:41 +0530298 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
299 offset, length, address, false);
300}
301
302Response 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;
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +0530351}
352
353} // namespace responder
354} // namespace pldm