blob: a513f59c38d969198d2b074bf61f2112350ad491 [file] [log] [blame]
Tom Joseph07220402019-05-15 16:30:41 +05301#include "config.h"
2
Tom Josephf8329ac2019-04-11 22:13:22 +05303#include "file_io.hpp"
4
Tom Joseph07220402019-05-15 16:30:41 +05305#include "file_table.hpp"
6
Tom Josephf8329ac2019-04-11 22:13:22 +05307#include <fcntl.h>
8#include <sys/mman.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11#include <unistd.h>
12
13#include <cstring>
14#include <fstream>
15#include <phosphor-logging/log.hpp>
16
17#include "libpldm/base.h"
18
19namespace pldm
20{
21
22namespace responder
23{
24
25namespace fs = std::filesystem;
26using namespace phosphor::logging;
27
Eddie James3b02e272019-04-22 20:13:55 +000028namespace dma
Tom Josephf8329ac2019-04-11 22:13:22 +053029{
Eddie James3b02e272019-04-22 20:13:55 +000030
31/** @struct AspeedXdmaOp
32 *
33 * Structure representing XDMA operation
34 */
35struct AspeedXdmaOp
36{
Eddie James3b02e272019-04-22 20:13:55 +000037 uint64_t hostAddr; //!< the DMA address on the host side, configured by
38 //!< PCI subsystem.
39 uint32_t len; //!< the size of the transfer in bytes, it should be a
40 //!< multiple of 16 bytes
Eddie Jamesc6da21e2019-05-29 18:29:58 +000041 uint32_t upstream; //!< boolean indicating the direction of the DMA
42 //!< operation, true means a transfer from BMC to host.
43};
Eddie James3b02e272019-04-22 20:13:55 +000044
45constexpr auto xdmaDev = "/dev/xdma";
46
Tom Joseph55306762019-05-02 09:11:26 +053047int DMA::transferDataHost(const fs::path& path, uint32_t offset,
48 uint32_t length, uint64_t address, bool upstream)
Eddie James3b02e272019-04-22 20:13:55 +000049{
Tom Josephf8329ac2019-04-11 22:13:22 +053050 static const size_t pageSize = getpagesize();
51 uint32_t numPages = length / pageSize;
Eddie James3b02e272019-04-22 20:13:55 +000052 uint32_t pageAlignedLength = numPages * pageSize;
53
54 if (length > pageAlignedLength)
Tom Josephf8329ac2019-04-11 22:13:22 +053055 {
Eddie James3b02e272019-04-22 20:13:55 +000056 pageAlignedLength += pageSize;
Tom Josephf8329ac2019-04-11 22:13:22 +053057 }
58
Eddie James3b02e272019-04-22 20:13:55 +000059 auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
60 munmap(vgaMem, pageAlignedLength);
Tom Josephf8329ac2019-04-11 22:13:22 +053061 };
62
63 int fd = -1;
64 int rc = 0;
Eddie James3b02e272019-04-22 20:13:55 +000065 fd = open(xdmaDev, O_RDWR);
Tom Josephf8329ac2019-04-11 22:13:22 +053066 if (fd < 0)
67 {
Eddie James3b02e272019-04-22 20:13:55 +000068 rc = -errno;
69 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
Tom Josephf8329ac2019-04-11 22:13:22 +053070 return rc;
71 }
Tom Josephf8329ac2019-04-11 22:13:22 +053072
Eddie James3b02e272019-04-22 20:13:55 +000073 utils::CustomFD xdmaFd(fd);
Tom Josephf8329ac2019-04-11 22:13:22 +053074
Eddie James3b02e272019-04-22 20:13:55 +000075 void* vgaMem;
76 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
77 MAP_SHARED, xdmaFd(), 0);
Tom Josephf8329ac2019-04-11 22:13:22 +053078 if (MAP_FAILED == vgaMem)
79 {
80 rc = -errno;
Eddie James3b02e272019-04-22 20:13:55 +000081 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
Tom Josephf8329ac2019-04-11 22:13:22 +053082 return rc;
83 }
Eddie James3b02e272019-04-22 20:13:55 +000084
Tom Josephf8329ac2019-04-11 22:13:22 +053085 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
86
Eddie James3b02e272019-04-22 20:13:55 +000087 if (upstream)
Tom Josephf8329ac2019-04-11 22:13:22 +053088 {
Eddie James3b02e272019-04-22 20:13:55 +000089 std::ifstream stream(path.string());
Eddie James3b02e272019-04-22 20:13:55 +000090 stream.seekg(offset);
Tom Joseph9b979252019-06-19 09:59:18 +053091
92 // Writing to the VGA memory should be aligned at page boundary,
93 // otherwise write data into a buffer aligned at page boundary and
94 // then write to the VGA memory.
95 std::vector<char> buffer{};
96 buffer.resize(pageAlignedLength);
97 stream.read(buffer.data(), length);
98 memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
99 pageAlignedLength);
Eddie James3b02e272019-04-22 20:13:55 +0000100
Tom Joseph7a8a7242019-05-29 15:26:38 +0530101 if (static_cast<uint32_t>(stream.gcount()) != length)
Eddie James3b02e272019-04-22 20:13:55 +0000102 {
103 log<level::ERR>("mismatch between number of characters to read and "
104 "the length read",
105 entry("LENGTH=%d", length),
106 entry("COUNT=%d", stream.gcount()));
107 return -1;
108 }
Tom Josephf8329ac2019-04-11 22:13:22 +0530109 }
110
Eddie James3b02e272019-04-22 20:13:55 +0000111 AspeedXdmaOp xdmaOp;
112 xdmaOp.upstream = upstream ? 1 : 0;
Tom Josephf8329ac2019-04-11 22:13:22 +0530113 xdmaOp.hostAddr = address;
114 xdmaOp.len = length;
115
Eddie James3b02e272019-04-22 20:13:55 +0000116 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
Tom Josephf8329ac2019-04-11 22:13:22 +0530117 if (rc < 0)
118 {
119 rc = -errno;
Eddie James3b02e272019-04-22 20:13:55 +0000120 log<level::ERR>("Failed to execute the DMA operation",
121 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
Tom Josephf8329ac2019-04-11 22:13:22 +0530122 entry("ADDRESS=%lld", address),
123 entry("LENGTH=%d", length));
124 return rc;
125 }
126
Eddie James3b02e272019-04-22 20:13:55 +0000127 if (!upstream)
128 {
129 std::ofstream stream(path.string());
130
131 stream.seekp(offset);
132 stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
133 }
134
135 return 0;
136}
137
138} // namespace dma
139
Tom Joseph4e48b442019-06-04 09:23:44 +0530140Response readFileIntoMemory(const uint8_t* request, size_t payloadLength)
Tom Josephf8329ac2019-04-11 22:13:22 +0530141{
142 uint32_t fileHandle = 0;
143 uint32_t offset = 0;
144 uint32_t length = 0;
145 uint64_t address = 0;
146
Tom Joseph4e48b442019-06-04 09:23:44 +0530147 Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
148 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
149
Eddie James3b02e272019-04-22 20:13:55 +0000150 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
Tom Josephf8329ac2019-04-11 22:13:22 +0530151 {
Eddie James3b02e272019-04-22 20:13:55 +0000152 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530153 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
154 return response;
Tom Josephf8329ac2019-04-11 22:13:22 +0530155 }
156
Eddie James3b02e272019-04-22 20:13:55 +0000157 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
158 &length, &address);
Tom Josephf8329ac2019-04-11 22:13:22 +0530159
Tom Joseph07220402019-05-15 16:30:41 +0530160 using namespace pldm::filetable;
161 auto& table = buildFileTable(FILE_TABLE_JSON);
162 FileEntry value{};
163
164 try
165 {
166 value = table.at(fileHandle);
167 }
168 catch (std::exception& e)
169 {
170 log<level::ERR>("File handle does not exist in the file table",
171 entry("HANDLE=%d", fileHandle));
172 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
173 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
174 return response;
175 }
176
177 if (!fs::exists(value.fsPath))
Tom Josephf8329ac2019-04-11 22:13:22 +0530178 {
179 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
Eddie James3b02e272019-04-22 20:13:55 +0000180 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530181 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
182 return response;
Tom Josephf8329ac2019-04-11 22:13:22 +0530183 }
184
Tom Joseph07220402019-05-15 16:30:41 +0530185 auto fileSize = fs::file_size(value.fsPath);
Tom Josephf8329ac2019-04-11 22:13:22 +0530186 if (offset >= fileSize)
187 {
188 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
189 entry("FILE_SIZE=%d", fileSize));
Eddie James3b02e272019-04-22 20:13:55 +0000190 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530191 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
192 return response;
Tom Josephf8329ac2019-04-11 22:13:22 +0530193 }
194
195 if (offset + length > fileSize)
196 {
197 length = fileSize - offset;
198 }
199
200 if (length % dma::minSize)
201 {
Eddie James3b02e272019-04-22 20:13:55 +0000202 log<level::ERR>("Read length is not a multiple of DMA minSize",
Tom Josephf8329ac2019-04-11 22:13:22 +0530203 entry("LENGTH=%d", length));
Eddie James3b02e272019-04-22 20:13:55 +0000204 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530205 PLDM_INVALID_READ_LENGTH, 0, responsePtr);
206 return response;
Tom Josephf8329ac2019-04-11 22:13:22 +0530207 }
208
Tom Joseph55306762019-05-02 09:11:26 +0530209 using namespace dma;
210 DMA intf;
Tom Joseph07220402019-05-15 16:30:41 +0530211 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
212 offset, length, address, true);
Eddie James3b02e272019-04-22 20:13:55 +0000213}
Tom Josephf8329ac2019-04-11 22:13:22 +0530214
Tom Joseph4e48b442019-06-04 09:23:44 +0530215Response writeFileFromMemory(const uint8_t* request, size_t payloadLength)
Eddie James3b02e272019-04-22 20:13:55 +0000216{
217 uint32_t fileHandle = 0;
218 uint32_t offset = 0;
219 uint32_t length = 0;
220 uint64_t address = 0;
Eddie James3b02e272019-04-22 20:13:55 +0000221
Tom Joseph4e48b442019-06-04 09:23:44 +0530222 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
223 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
224
Eddie James3b02e272019-04-22 20:13:55 +0000225 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
Tom Josephf8329ac2019-04-11 22:13:22 +0530226 {
Eddie James3b02e272019-04-22 20:13:55 +0000227 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530228 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
229 return response;
Tom Josephf8329ac2019-04-11 22:13:22 +0530230 }
Eddie James3b02e272019-04-22 20:13:55 +0000231
232 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
233 &length, &address);
234
235 if (length % dma::minSize)
236 {
237 log<level::ERR>("Write length is not a multiple of DMA minSize",
238 entry("LENGTH=%d", length));
239 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530240 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
241 return response;
Eddie James3b02e272019-04-22 20:13:55 +0000242 }
243
Tom Joseph07220402019-05-15 16:30:41 +0530244 using namespace pldm::filetable;
245 auto& table = buildFileTable(FILE_TABLE_JSON);
246 FileEntry value{};
247
248 try
249 {
250 value = table.at(fileHandle);
251 }
252 catch (std::exception& e)
253 {
254 log<level::ERR>("File handle does not exist in the file table",
255 entry("HANDLE=%d", fileHandle));
256 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
257 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
258 return response;
259 }
260
261 if (!fs::exists(value.fsPath))
Eddie James3b02e272019-04-22 20:13:55 +0000262 {
263 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
264 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530265 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
266 return response;
Eddie James3b02e272019-04-22 20:13:55 +0000267 }
268
Tom Joseph07220402019-05-15 16:30:41 +0530269 auto fileSize = fs::file_size(value.fsPath);
Eddie James3b02e272019-04-22 20:13:55 +0000270 if (offset >= fileSize)
271 {
272 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
273 entry("FILE_SIZE=%d", fileSize));
274 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
Tom Joseph4e48b442019-06-04 09:23:44 +0530275 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
276 return response;
Eddie James3b02e272019-04-22 20:13:55 +0000277 }
278
Tom Joseph55306762019-05-02 09:11:26 +0530279 using namespace dma;
280 DMA intf;
Tom Joseph07220402019-05-15 16:30:41 +0530281 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
282 offset, length, address, false);
Tom Josephf8329ac2019-04-11 22:13:22 +0530283}
284
Tom Joseph19856622019-06-07 10:18:21 +0530285Response getFileTable(const uint8_t* request, size_t payloadLength)
286{
287 uint32_t transferHandle = 0;
288 uint8_t transferFlag = 0;
289 uint8_t tableType = 0;
290
291 Response response(sizeof(pldm_msg_hdr) +
292 PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
293 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
294
295 if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
296 {
297 encode_get_file_table_resp(0, PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr,
298 0, responsePtr);
299 return response;
300 }
301
302 auto rc = decode_get_file_table_req(request, payloadLength, &transferHandle,
303 &transferFlag, &tableType);
304 if (rc)
305 {
306 encode_get_file_table_resp(0, rc, 0, 0, nullptr, 0, responsePtr);
307 return response;
308 }
309
310 if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
311 {
312 encode_get_file_table_resp(0, PLDM_INVALID_FILE_TABLE_TYPE, 0, 0,
313 nullptr, 0, responsePtr);
314 return response;
315 }
316
317 using namespace pldm::filetable;
318 auto table = buildFileTable(FILE_TABLE_JSON);
319 auto attrTable = table();
320 response.resize(response.size() + attrTable.size());
321 responsePtr = reinterpret_cast<pldm_msg*>(response.data());
322
323 if (attrTable.empty())
324 {
325 encode_get_file_table_resp(0, PLDM_FILE_TABLE_UNAVAILABLE, 0, 0,
326 nullptr, 0, responsePtr);
327 return response;
328 }
329
330 encode_get_file_table_resp(0, PLDM_SUCCESS, 0, PLDM_START_AND_END,
331 attrTable.data(), attrTable.size(), responsePtr);
332 return response;
333}
334
Tom Josephf8329ac2019-04-11 22:13:22 +0530335} // namespace responder
336} // namespace pldm