blob: ed043c623e08a6554d79c63210af77fe2fd30072 [file] [log] [blame]
Tom Josephf8329ac2019-04-11 22:13:22 +05301#include "file_io.hpp"
2
3#include <fcntl.h>
4#include <sys/mman.h>
5#include <sys/stat.h>
6#include <sys/types.h>
7#include <unistd.h>
8
9#include <cstring>
10#include <fstream>
11#include <phosphor-logging/log.hpp>
12
13#include "libpldm/base.h"
14
15namespace pldm
16{
17
18namespace responder
19{
20
21namespace fs = std::filesystem;
22using namespace phosphor::logging;
23
Eddie James3b02e272019-04-22 20:13:55 +000024namespace dma
Tom Josephf8329ac2019-04-11 22:13:22 +053025{
Eddie James3b02e272019-04-22 20:13:55 +000026
27/** @struct AspeedXdmaOp
28 *
29 * Structure representing XDMA operation
30 */
31struct AspeedXdmaOp
32{
33 uint8_t upstream; //!< boolean indicating the direction of the DMA
34 //!< operation, true means a transfer from BMC to host.
35 uint64_t hostAddr; //!< the DMA address on the host side, configured by
36 //!< PCI subsystem.
37 uint32_t len; //!< the size of the transfer in bytes, it should be a
38 //!< multiple of 16 bytes
39} __attribute__((packed));
40
41constexpr auto xdmaDev = "/dev/xdma";
42
43int transferDataHost(const fs::path& path, uint32_t offset, uint32_t length,
44 uint64_t address, bool upstream)
45{
Tom Josephf8329ac2019-04-11 22:13:22 +053046 static const size_t pageSize = getpagesize();
47 uint32_t numPages = length / pageSize;
Eddie James3b02e272019-04-22 20:13:55 +000048 uint32_t pageAlignedLength = numPages * pageSize;
49
50 if (length > pageAlignedLength)
Tom Josephf8329ac2019-04-11 22:13:22 +053051 {
Eddie James3b02e272019-04-22 20:13:55 +000052 pageAlignedLength += pageSize;
Tom Josephf8329ac2019-04-11 22:13:22 +053053 }
54
Eddie James3b02e272019-04-22 20:13:55 +000055 auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
56 munmap(vgaMem, pageAlignedLength);
Tom Josephf8329ac2019-04-11 22:13:22 +053057 };
58
59 int fd = -1;
60 int rc = 0;
Eddie James3b02e272019-04-22 20:13:55 +000061 fd = open(xdmaDev, O_RDWR);
Tom Josephf8329ac2019-04-11 22:13:22 +053062 if (fd < 0)
63 {
Eddie James3b02e272019-04-22 20:13:55 +000064 rc = -errno;
65 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
Tom Josephf8329ac2019-04-11 22:13:22 +053066 return rc;
67 }
Tom Josephf8329ac2019-04-11 22:13:22 +053068
Eddie James3b02e272019-04-22 20:13:55 +000069 utils::CustomFD xdmaFd(fd);
Tom Josephf8329ac2019-04-11 22:13:22 +053070
Eddie James3b02e272019-04-22 20:13:55 +000071 void* vgaMem;
72 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
73 MAP_SHARED, xdmaFd(), 0);
Tom Josephf8329ac2019-04-11 22:13:22 +053074 if (MAP_FAILED == vgaMem)
75 {
76 rc = -errno;
Eddie James3b02e272019-04-22 20:13:55 +000077 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
Tom Josephf8329ac2019-04-11 22:13:22 +053078 return rc;
79 }
Eddie James3b02e272019-04-22 20:13:55 +000080
Tom Josephf8329ac2019-04-11 22:13:22 +053081 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
82
Eddie James3b02e272019-04-22 20:13:55 +000083 if (upstream)
Tom Josephf8329ac2019-04-11 22:13:22 +053084 {
Eddie James3b02e272019-04-22 20:13:55 +000085 std::ifstream stream(path.string());
86
87 stream.seekg(offset);
88 stream.read(static_cast<char*>(vgaMemPtr.get()), length);
89
90 if (stream.gcount() != length)
91 {
92 log<level::ERR>("mismatch between number of characters to read and "
93 "the length read",
94 entry("LENGTH=%d", length),
95 entry("COUNT=%d", stream.gcount()));
96 return -1;
97 }
Tom Josephf8329ac2019-04-11 22:13:22 +053098 }
99
Eddie James3b02e272019-04-22 20:13:55 +0000100 AspeedXdmaOp xdmaOp;
101 xdmaOp.upstream = upstream ? 1 : 0;
Tom Josephf8329ac2019-04-11 22:13:22 +0530102 xdmaOp.hostAddr = address;
103 xdmaOp.len = length;
104
Eddie James3b02e272019-04-22 20:13:55 +0000105 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
Tom Josephf8329ac2019-04-11 22:13:22 +0530106 if (rc < 0)
107 {
108 rc = -errno;
Eddie James3b02e272019-04-22 20:13:55 +0000109 log<level::ERR>("Failed to execute the DMA operation",
110 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
Tom Josephf8329ac2019-04-11 22:13:22 +0530111 entry("ADDRESS=%lld", address),
112 entry("LENGTH=%d", length));
113 return rc;
114 }
115
Eddie James3b02e272019-04-22 20:13:55 +0000116 if (!upstream)
117 {
118 std::ofstream stream(path.string());
119
120 stream.seekp(offset);
121 stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
122 }
123
124 return 0;
125}
126
127} // namespace dma
128
129void transferAll(uint8_t command, fs::path& path, uint32_t offset,
130 uint32_t length, uint64_t address, bool upstream,
131 pldm_msg* response)
132{
133 uint32_t origLength = length;
134
135 while (length > dma::maxSize)
136 {
137 auto rc = dma::transferDataHost(path, offset, dma::maxSize, address,
138 upstream);
139 if (rc < 0)
140 {
141 encode_rw_file_memory_resp(0, command, PLDM_ERROR, 0, response);
142 return;
143 }
144
145 offset += dma::maxSize;
146 length -= dma::maxSize;
147 address += dma::maxSize;
148 }
149
150 auto rc = dma::transferDataHost(path, offset, length, address, upstream);
151 if (rc < 0)
152 {
153 encode_rw_file_memory_resp(0, command, PLDM_ERROR, 0, response);
154 return;
155 }
156
157 encode_rw_file_memory_resp(0, command, PLDM_SUCCESS, origLength, response);
158 return;
Tom Josephf8329ac2019-04-11 22:13:22 +0530159}
160
161void readFileIntoMemory(const uint8_t* request, size_t payloadLength,
162 pldm_msg* response)
163{
164 uint32_t fileHandle = 0;
165 uint32_t offset = 0;
166 uint32_t length = 0;
167 uint64_t address = 0;
Eddie James3b02e272019-04-22 20:13:55 +0000168 fs::path path("");
Tom Josephf8329ac2019-04-11 22:13:22 +0530169
Eddie James3b02e272019-04-22 20:13:55 +0000170 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
Tom Josephf8329ac2019-04-11 22:13:22 +0530171 {
Eddie James3b02e272019-04-22 20:13:55 +0000172 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
173 PLDM_ERROR_INVALID_LENGTH, 0, response);
Tom Josephf8329ac2019-04-11 22:13:22 +0530174 return;
175 }
176
Eddie James3b02e272019-04-22 20:13:55 +0000177 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
178 &length, &address);
Tom Josephf8329ac2019-04-11 22:13:22 +0530179
Tom Josephf8329ac2019-04-11 22:13:22 +0530180 if (!fs::exists(path))
181 {
182 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
Eddie James3b02e272019-04-22 20:13:55 +0000183 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
184 PLDM_INVALID_FILE_HANDLE, 0, response);
Tom Josephf8329ac2019-04-11 22:13:22 +0530185 return;
186 }
187
188 auto fileSize = fs::file_size(path);
Tom Josephf8329ac2019-04-11 22:13:22 +0530189 if (offset >= fileSize)
190 {
191 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
192 entry("FILE_SIZE=%d", fileSize));
Eddie James3b02e272019-04-22 20:13:55 +0000193 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
194 PLDM_DATA_OUT_OF_RANGE, 0, response);
Tom Josephf8329ac2019-04-11 22:13:22 +0530195 return;
196 }
197
198 if (offset + length > fileSize)
199 {
200 length = fileSize - offset;
201 }
202
203 if (length % dma::minSize)
204 {
Eddie James3b02e272019-04-22 20:13:55 +0000205 log<level::ERR>("Read length is not a multiple of DMA minSize",
Tom Josephf8329ac2019-04-11 22:13:22 +0530206 entry("LENGTH=%d", length));
Eddie James3b02e272019-04-22 20:13:55 +0000207 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
208 PLDM_INVALID_READ_LENGTH, 0, response);
Tom Josephf8329ac2019-04-11 22:13:22 +0530209 return;
210 }
211
Eddie James3b02e272019-04-22 20:13:55 +0000212 transferAll(PLDM_READ_FILE_INTO_MEMORY, path, offset, length, address, true,
213 response);
214}
Tom Josephf8329ac2019-04-11 22:13:22 +0530215
Eddie James3b02e272019-04-22 20:13:55 +0000216void writeFileFromMemory(const uint8_t* request, size_t payloadLength,
217 pldm_msg* response)
218{
219 uint32_t fileHandle = 0;
220 uint32_t offset = 0;
221 uint32_t length = 0;
222 uint64_t address = 0;
223 fs::path path("");
224
225 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,
228 PLDM_ERROR_INVALID_LENGTH, 0, response);
229 return;
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,
240 PLDM_INVALID_WRITE_LENGTH, 0, response);
241 return;
242 }
243
244 if (!fs::exists(path))
245 {
246 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
247 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
248 PLDM_INVALID_FILE_HANDLE, 0, response);
249 return;
250 }
251
252 auto fileSize = fs::file_size(path);
253 if (offset >= fileSize)
254 {
255 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
256 entry("FILE_SIZE=%d", fileSize));
257 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
258 PLDM_DATA_OUT_OF_RANGE, 0, response);
259 return;
260 }
261
262 transferAll(PLDM_WRITE_FILE_FROM_MEMORY, path, offset, length, address,
263 false, response);
Tom Josephf8329ac2019-04-11 22:13:22 +0530264}
265
266} // namespace responder
267} // namespace pldm