1 #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 15 namespace pldm 16 { 17 18 namespace responder 19 { 20 21 namespace fs = std::filesystem; 22 using namespace phosphor::logging; 23 24 namespace dma 25 { 26 27 /** @struct AspeedXdmaOp 28 * 29 * Structure representing XDMA operation 30 */ 31 struct 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 41 constexpr auto xdmaDev = "/dev/xdma"; 42 43 int 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 129 Response 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 188 Response 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 244