1 #pragma once
2 
3 #include "handler.hpp"
4 #include "utils.hpp"
5 
6 #include <fcntl.h>
7 #include <stdint.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 
12 #include <filesystem>
13 #include <iostream>
14 #include <vector>
15 
16 #include "libpldm/base.h"
17 #include "oem/ibm/libpldm/file_io.h"
18 
19 namespace pldm
20 {
21 namespace responder
22 {
23 namespace dma
24 {
25 
26 // The minimum data size of dma transfer in bytes
27 constexpr uint32_t minSize = 16;
28 
29 // 16MB - 4096B (16773120 bytes) is the maximum data size of DMA transfer
30 constexpr size_t maxSize = (16 * 1024 * 1024) - 4096;
31 
32 namespace fs = std::filesystem;
33 
34 /**
35  * @class DMA
36  *
37  * Expose API to initiate transfer of data by DMA
38  *
39  * This class only exposes the public API transferDataHost to transfer data
40  * between BMC and host using DMA. This allows for mocking the transferDataHost
41  * for unit testing purposes.
42  */
43 class DMA
44 {
45   public:
46     /** @brief API to transfer data between BMC and host using DMA
47      *
48      * @param[in] path     - pathname of the file to transfer data from or to
49      * @param[in] offset   - offset in the file
50      * @param[in] length   - length of the data to transfer
51      * @param[in] address  - DMA address on the host
52      * @param[in] upstream - indicates direction of the transfer; true indicates
53      *                       transfer to the host
54      *
55      * @return returns 0 on success, negative errno on failure
56      */
57     int transferDataHost(int fd, uint32_t offset, uint32_t length,
58                          uint64_t address, bool upstream);
59 };
60 
61 /** @brief Transfer the data between BMC and host using DMA.
62  *
63  *  There is a max size for each DMA operation, transferAll API abstracts this
64  *  and the requested length is broken down into multiple DMA operations if the
65  *  length exceed max size.
66  *
67  * @tparam[in] T - DMA interface type
68  * @param[in] intf - interface passed to invoke DMA transfer
69  * @param[in] command  - PLDM command
70  * @param[in] path     - pathname of the file to transfer data from or to
71  * @param[in] offset   - offset in the file
72  * @param[in] length   - length of the data to transfer
73  * @param[in] address  - DMA address on the host
74  * @param[in] upstream - indicates direction of the transfer; true indicates
75  *                       transfer to the host
76  * @param[in] instanceId - Message's instance id
77  * @return PLDM response message
78  */
79 
80 template <class DMAInterface>
81 Response transferAll(DMAInterface* intf, uint8_t command, fs::path& path,
82                      uint32_t offset, uint32_t length, uint64_t address,
83                      bool upstream, uint8_t instanceId)
84 {
85     uint32_t origLength = length;
86     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
87     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
88 
89     int flags{};
90     if (upstream)
91     {
92         flags = O_RDONLY;
93     }
94     else if (fs::exists(path))
95     {
96         flags = O_RDWR;
97     }
98     else
99     {
100         flags = O_WRONLY;
101     }
102     int file = open(path.string().c_str(), flags);
103     if (file == -1)
104     {
105         std::cerr << "File does not exist, path = " << path.string() << "\n";
106         encode_rw_file_memory_resp(instanceId, command, PLDM_ERROR, 0,
107                                    responsePtr);
108         return response;
109     }
110     pldm::utils::CustomFD fd(file);
111 
112     while (length > dma::maxSize)
113     {
114         auto rc = intf->transferDataHost(fd(), offset, dma::maxSize, address,
115                                          upstream);
116         if (rc < 0)
117         {
118             encode_rw_file_memory_resp(instanceId, command, PLDM_ERROR, 0,
119                                        responsePtr);
120             return response;
121         }
122 
123         offset += dma::maxSize;
124         length -= dma::maxSize;
125         address += dma::maxSize;
126     }
127 
128     auto rc = intf->transferDataHost(fd(), offset, length, address, upstream);
129     if (rc < 0)
130     {
131         encode_rw_file_memory_resp(instanceId, command, PLDM_ERROR, 0,
132                                    responsePtr);
133         return response;
134     }
135 
136     encode_rw_file_memory_resp(instanceId, command, PLDM_SUCCESS, origLength,
137                                responsePtr);
138     return response;
139 }
140 
141 } // namespace dma
142 
143 namespace oem_ibm
144 {
145 
146 class Handler : public CmdHandler
147 {
148   public:
149     Handler()
150     {
151         handlers.emplace(PLDM_READ_FILE_INTO_MEMORY,
152                          [this](const pldm_msg* request, size_t payloadLength) {
153                              return this->readFileIntoMemory(request,
154                                                              payloadLength);
155                          });
156         handlers.emplace(PLDM_WRITE_FILE_FROM_MEMORY,
157                          [this](const pldm_msg* request, size_t payloadLength) {
158                              return this->writeFileFromMemory(request,
159                                                               payloadLength);
160                          });
161         handlers.emplace(PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
162                          [this](const pldm_msg* request, size_t payloadLength) {
163                              return this->writeFileByTypeFromMemory(
164                                  request, payloadLength);
165                          });
166         handlers.emplace(PLDM_READ_FILE_BY_TYPE_INTO_MEMORY,
167                          [this](const pldm_msg* request, size_t payloadLength) {
168                              return this->readFileByTypeIntoMemory(
169                                  request, payloadLength);
170                          });
171         handlers.emplace(PLDM_READ_FILE_BY_TYPE, [this](const pldm_msg* request,
172                                                         size_t payloadLength) {
173             return this->readFileByType(request, payloadLength);
174         });
175         handlers.emplace(PLDM_GET_FILE_TABLE,
176                          [this](const pldm_msg* request, size_t payloadLength) {
177                              return this->getFileTable(request, payloadLength);
178                          });
179         handlers.emplace(PLDM_READ_FILE,
180                          [this](const pldm_msg* request, size_t payloadLength) {
181                              return this->readFile(request, payloadLength);
182                          });
183         handlers.emplace(PLDM_WRITE_FILE,
184                          [this](const pldm_msg* request, size_t payloadLength) {
185                              return this->writeFile(request, payloadLength);
186                          });
187         handlers.emplace(PLDM_FILE_ACK,
188                          [this](const pldm_msg* request, size_t payloadLength) {
189                              return this->fileAck(request, payloadLength);
190                          });
191     }
192 
193     /** @brief Handler for readFileIntoMemory command
194      *
195      *  @param[in] request - pointer to PLDM request payload
196      *  @param[in] payloadLength - length of the message
197      *
198      *  @return PLDM response message
199      */
200     Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength);
201 
202     /** @brief Handler for writeFileIntoMemory command
203      *
204      *  @param[in] request - pointer to PLDM request payload
205      *  @param[in] payloadLength - length of the message
206      *
207      *  @return PLDM response message
208      */
209     Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength);
210 
211     /** @brief Handler for writeFileByTypeFromMemory command
212      *
213      *  @param[in] request - pointer to PLDM request payload
214      *  @param[in] payloadLength - length of the message
215      *
216      *  @return PLDM response message
217      */
218 
219     Response writeFileByTypeFromMemory(const pldm_msg* request,
220                                        size_t payloadLength);
221 
222     /** @brief Handler for readFileByTypeIntoMemory command
223      *
224      *  @param[in] request - pointer to PLDM request payload
225      *  @param[in] payloadLength - length of the message
226      *
227      *  @return PLDM response message
228      */
229     Response readFileByTypeIntoMemory(const pldm_msg* request,
230                                       size_t payloadLength);
231 
232     /** @brief Handler for readFileByType command
233      *
234      *  @param[in] request - pointer to PLDM request payload
235      *  @param[in] payloadLength - length of the message
236      *
237      *  @return PLDM response message
238      */
239     Response readFileByType(const pldm_msg* request, size_t payloadLength);
240 
241     /** @brief Handler for GetFileTable command
242      *
243      *  @param[in] request - pointer to PLDM request payload
244      *  @param[in] payloadLength - length of the message payload
245      *
246      *  @return PLDM response message
247      */
248     Response getFileTable(const pldm_msg* request, size_t payloadLength);
249 
250     /** @brief Handler for readFile command
251      *
252      *  @param[in] request - PLDM request msg
253      *  @param[in] payloadLength - length of the message payload
254      *
255      *  @return PLDM response message
256      */
257     Response readFile(const pldm_msg* request, size_t payloadLength);
258 
259     /** @brief Handler for writeFile command
260      *
261      *  @param[in] request - PLDM request msg
262      *  @param[in] payloadLength - length of the message payload
263      *
264      *  @return PLDM response message
265      */
266     Response writeFile(const pldm_msg* request, size_t payloadLength);
267 
268     Response fileAck(const pldm_msg* request, size_t payloadLength);
269 };
270 
271 } // namespace oem_ibm
272 } // namespace responder
273 } // namespace pldm
274