blob: 9cf093caa95cd5021c6a9de3244a43b426519942 [file] [log] [blame]
Jinu Joy Thomas7f57f442019-06-13 20:38:49 +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
24namespace dma
25{
26
27/** @struct AspeedXdmaOp
28 *
29 * Structure representing XDMA operation
30 */
31struct AspeedXdmaOp
32{
33 uint64_t hostAddr; //!< the DMA address on the host side, configured by
34 //!< PCI subsystem.
35 uint32_t len; //!< the size of the transfer in bytes, it should be a
36 //!< multiple of 16 bytes
37 uint32_t upstream; //!< boolean indicating the direction of the DMA
38 //!< operation, true means a transfer from BMC to host.
39};
40
41constexpr auto xdmaDev = "/dev/xdma";
42
43int DMA::transferDataHost(const fs::path& path, uint32_t offset,
44 uint32_t length, uint64_t address, bool upstream)
45{
46 static const size_t pageSize = getpagesize();
47 uint32_t numPages = length / pageSize;
48 uint32_t pageAlignedLength = numPages * pageSize;
49
50 if (length > pageAlignedLength)
51 {
52 pageAlignedLength += pageSize;
53 }
54
55 auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
56 munmap(vgaMem, pageAlignedLength);
57 };
58
59 int fd = -1;
60 int rc = 0;
61 fd = open(xdmaDev, O_RDWR);
62 if (fd < 0)
63 {
64 rc = -errno;
65 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc));
66 return rc;
67 }
68
69 utils::CustomFD xdmaFd(fd);
70
71 void* vgaMem;
72 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
73 MAP_SHARED, xdmaFd(), 0);
74 if (MAP_FAILED == vgaMem)
75 {
76 rc = -errno;
77 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc));
78 return rc;
79 }
80
81 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
82
83 if (upstream)
84 {
85 std::ifstream stream(path.string());
86
87 stream.seekg(offset);
88 stream.read(static_cast<char*>(vgaMemPtr.get()), length);
89
90 if (static_cast<uint32_t>(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 }
98 }
99
100 AspeedXdmaOp xdmaOp;
101 xdmaOp.upstream = upstream ? 1 : 0;
102 xdmaOp.hostAddr = address;
103 xdmaOp.len = length;
104
105 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
106 if (rc < 0)
107 {
108 rc = -errno;
109 log<level::ERR>("Failed to execute the DMA operation",
110 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream),
111 entry("ADDRESS=%lld", address),
112 entry("LENGTH=%d", length));
113 return rc;
114 }
115
116 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
129Response readFileIntoMemory(const uint8_t* request, size_t payloadLength)
130{
131 uint32_t fileHandle = 0;
132 uint32_t offset = 0;
133 uint32_t length = 0;
134 uint64_t address = 0;
135 fs::path path("");
136
137 Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
138 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
139
140 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
141 {
142 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
143 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
144 return response;
145 }
146
147 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
148 &length, &address);
149
150 if (!fs::exists(path))
151 {
152 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
153 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
154 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
155 return response;
156 }
157
158 auto fileSize = fs::file_size(path);
159 if (offset >= fileSize)
160 {
161 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
162 entry("FILE_SIZE=%d", fileSize));
163 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
164 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
165 return response;
166 }
167
168 if (offset + length > fileSize)
169 {
170 length = fileSize - offset;
171 }
172
173 if (length % dma::minSize)
174 {
175 log<level::ERR>("Read length is not a multiple of DMA minSize",
176 entry("LENGTH=%d", length));
177 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
178 PLDM_INVALID_READ_LENGTH, 0, responsePtr);
179 return response;
180 }
181
182 using namespace dma;
183 DMA intf;
184 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, path, offset,
185 length, address, true);
186}
187
188Response writeFileFromMemory(const uint8_t* request, size_t payloadLength)
189{
190 uint32_t fileHandle = 0;
191 uint32_t offset = 0;
192 uint32_t length = 0;
193 uint64_t address = 0;
194 fs::path path("");
195
196 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
197 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
198
199 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES)
200 {
201 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
202 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
203 return response;
204 }
205
206 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
207 &length, &address);
208
209 if (length % dma::minSize)
210 {
211 log<level::ERR>("Write length is not a multiple of DMA minSize",
212 entry("LENGTH=%d", length));
213 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
214 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
215 return response;
216 }
217
218 if (!fs::exists(path))
219 {
220 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
221 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
222 PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
223 return response;
224 }
225
226 auto fileSize = fs::file_size(path);
227 if (offset >= fileSize)
228 {
229 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
230 entry("FILE_SIZE=%d", fileSize));
231 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
232 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr);
233 return response;
234 }
235
236 using namespace dma;
237 DMA intf;
238 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, path, offset,
239 length, address, false);
240}
241
242} // namespace responder
243} // namespace pldm