1 #pragma once
2 
3 #include <stdint.h>
4 #include <unistd.h>
5 
6 #include <filesystem>
7 
8 #include "libpldm/base.h"
9 #include "libpldm/file_io.h"
10 
11 namespace pldm
12 {
13 
14 namespace responder
15 {
16 
17 using Response = std::vector<uint8_t>;
18 
19 namespace utils
20 {
21 
22 /** @struct CustomFD
23  *
24  *  RAII wrapper for file descriptor.
25  */
26 struct CustomFD
27 {
28     CustomFD(const CustomFD&) = delete;
29     CustomFD& operator=(const CustomFD&) = delete;
30     CustomFD(CustomFD&&) = delete;
31     CustomFD& operator=(CustomFD&&) = delete;
32 
33     CustomFD(int fd) : fd(fd)
34     {
35     }
36 
37     ~CustomFD()
38     {
39         if (fd >= 0)
40         {
41             close(fd);
42         }
43     }
44 
45     int operator()() const
46     {
47         return fd;
48     }
49 
50   private:
51     int fd = -1;
52 };
53 
54 } // namespace utils
55 
56 namespace dma
57 {
58 
59 // The minimum data size of dma transfer in bytes
60 constexpr uint32_t minSize = 16;
61 
62 // 16MB - 4096B (16773120 bytes) is the maximum data size of DMA transfer
63 constexpr size_t maxSize = (16 * 1024 * 1024) - 4096;
64 
65 namespace fs = std::filesystem;
66 
67 /**
68  * @class DMA
69  *
70  * Expose API to initiate transfer of data by DMA
71  *
72  * This class only exposes the public API transferDataHost to transfer data
73  * between BMC and host using DMA. This allows for mocking the transferDataHost
74  * for unit testing purposes.
75  */
76 class DMA
77 {
78   public:
79     /** @brief API to transfer data between BMC and host using DMA
80      *
81      * @param[in] path     - pathname of the file to transfer data from or to
82      * @param[in] offset   - offset in the file
83      * @param[in] length   - length of the data to transfer
84      * @param[in] address  - DMA address on the host
85      * @param[in] upstream - indicates direction of the transfer; true indicates
86      *                       transfer to the host
87      *
88      * @return returns 0 on success, negative errno on failure
89      */
90     int transferDataHost(const fs::path& path, uint32_t offset, uint32_t length,
91                          uint64_t address, bool upstream);
92 };
93 
94 /** @brief Transfer the data between BMC and host using DMA.
95  *
96  *  There is a max size for each DMA operation, transferAll API abstracts this
97  *  and the requested length is broken down into multiple DMA operations if the
98  *  length exceed max size.
99  *
100  * @tparam[in] T - DMA interface type
101  * @param[in] intf - interface passed to invoke DMA transfer
102  * @param[in] command  - PLDM command
103  * @param[in] path     - pathname of the file to transfer data from or to
104  * @param[in] offset   - offset in the file
105  * @param[in] length   - length of the data to transfer
106  * @param[in] address  - DMA address on the host
107  * @param[in] upstream - indicates direction of the transfer; true indicates
108  *                       transfer to the host
109  * @return PLDM response message
110  */
111 
112 template <class DMAInterface>
113 Response transferAll(DMAInterface* intf, uint8_t command, fs::path& path,
114                      uint32_t offset, uint32_t length, uint64_t address,
115                      bool upstream)
116 {
117     uint32_t origLength = length;
118     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
119     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
120 
121     while (length > dma::maxSize)
122     {
123         auto rc = intf->transferDataHost(path, offset, dma::maxSize, address,
124                                          upstream);
125         if (rc < 0)
126         {
127             encode_rw_file_memory_resp(0, command, PLDM_ERROR, 0, responsePtr);
128             return response;
129         }
130 
131         offset += dma::maxSize;
132         length -= dma::maxSize;
133         address += dma::maxSize;
134     }
135 
136     auto rc = intf->transferDataHost(path, offset, length, address, upstream);
137     if (rc < 0)
138     {
139         encode_rw_file_memory_resp(0, command, PLDM_ERROR, 0, responsePtr);
140         return response;
141     }
142 
143     encode_rw_file_memory_resp(0, command, PLDM_SUCCESS, origLength,
144                                responsePtr);
145     return response;
146 }
147 
148 } // namespace dma
149 
150 /** @brief Handler for readFileIntoMemory command
151  *
152  *  @param[in] request - pointer to PLDM request payload
153  *  @param[in] payloadLength - length of the message payload
154  *
155  *  @return PLDM response message
156  */
157 Response readFileIntoMemory(const uint8_t* request, size_t payloadLength);
158 
159 /** @brief Handler for writeFileIntoMemory command
160  *
161  *  @param[in] request - pointer to PLDM request payload
162  *  @param[in] payloadLength - length of the message payload
163  *
164  *  @return PLDM response message
165  */
166 Response writeFileFromMemory(const uint8_t* request, size_t payloadLength);
167 } // namespace responder
168 } // namespace pldm
169