1 #include "file_io.hpp" 2 3 #include "libpldmresponder/utils.hpp" 4 #include "registration.hpp" 5 6 #include <fcntl.h> 7 #include <sys/mman.h> 8 #include <sys/stat.h> 9 #include <sys/types.h> 10 #include <unistd.h> 11 12 #include <cstring> 13 #include <fstream> 14 #include <phosphor-logging/log.hpp> 15 16 #include "libpldm/base.h" 17 18 namespace pldm 19 { 20 21 namespace responder 22 { 23 24 namespace oem_ibm 25 { 26 27 void registerHandlers() 28 { 29 registerHandler(PLDM_OEM, PLDM_READ_FILE_INTO_MEMORY, 30 std::move(readFileIntoMemory)); 31 registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY, 32 std::move(writeFileFromMemory)); 33 } 34 35 } // namespace oem_ibm 36 37 namespace fs = std::filesystem; 38 using namespace phosphor::logging; 39 40 namespace dma 41 { 42 43 /** @struct AspeedXdmaOp 44 * 45 * Structure representing XDMA operation 46 */ 47 struct AspeedXdmaOp 48 { 49 uint64_t hostAddr; //!< the DMA address on the host side, configured by 50 //!< PCI subsystem. 51 uint32_t len; //!< the size of the transfer in bytes, it should be a 52 //!< multiple of 16 bytes 53 uint32_t upstream; //!< boolean indicating the direction of the DMA 54 //!< operation, true means a transfer from BMC to host. 55 }; 56 57 constexpr auto xdmaDev = "/dev/xdma"; 58 59 int DMA::transferDataHost(const fs::path& path, uint32_t offset, 60 uint32_t length, uint64_t address, bool upstream) 61 { 62 static const size_t pageSize = getpagesize(); 63 uint32_t numPages = length / pageSize; 64 uint32_t pageAlignedLength = numPages * pageSize; 65 66 if (length > pageAlignedLength) 67 { 68 pageAlignedLength += pageSize; 69 } 70 71 auto mmapCleanup = [pageAlignedLength](void* vgaMem) { 72 munmap(vgaMem, pageAlignedLength); 73 }; 74 75 int fd = -1; 76 int rc = 0; 77 fd = open(xdmaDev, O_RDWR); 78 if (fd < 0) 79 { 80 rc = -errno; 81 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc)); 82 return rc; 83 } 84 85 utils::CustomFD xdmaFd(fd); 86 87 void* vgaMem; 88 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ, 89 MAP_SHARED, xdmaFd(), 0); 90 if (MAP_FAILED == vgaMem) 91 { 92 rc = -errno; 93 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc)); 94 return rc; 95 } 96 97 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup); 98 99 if (upstream) 100 { 101 std::ifstream stream(path.string()); 102 103 stream.seekg(offset); 104 stream.read(static_cast<char*>(vgaMemPtr.get()), length); 105 106 if (static_cast<uint32_t>(stream.gcount()) != length) 107 { 108 log<level::ERR>("mismatch between number of characters to read and " 109 "the length read", 110 entry("LENGTH=%d", length), 111 entry("COUNT=%d", stream.gcount())); 112 return -1; 113 } 114 } 115 116 AspeedXdmaOp xdmaOp; 117 xdmaOp.upstream = upstream ? 1 : 0; 118 xdmaOp.hostAddr = address; 119 xdmaOp.len = length; 120 121 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp)); 122 if (rc < 0) 123 { 124 rc = -errno; 125 log<level::ERR>("Failed to execute the DMA operation", 126 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream), 127 entry("ADDRESS=%lld", address), 128 entry("LENGTH=%d", length)); 129 return rc; 130 } 131 132 if (!upstream) 133 { 134 std::ofstream stream(path.string()); 135 136 stream.seekp(offset); 137 stream.write(static_cast<const char*>(vgaMemPtr.get()), length); 138 } 139 140 return 0; 141 } 142 143 } // namespace dma 144 145 Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength) 146 { 147 uint32_t fileHandle = 0; 148 uint32_t offset = 0; 149 uint32_t length = 0; 150 uint64_t address = 0; 151 fs::path path(""); 152 153 Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0); 154 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 155 156 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 157 { 158 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 159 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 160 return response; 161 } 162 163 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 164 &offset, &length, &address); 165 166 if (!fs::exists(path)) 167 { 168 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 169 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 170 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 171 return response; 172 } 173 174 auto fileSize = fs::file_size(path); 175 if (offset >= fileSize) 176 { 177 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 178 entry("FILE_SIZE=%d", fileSize)); 179 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 180 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 181 return response; 182 } 183 184 if (offset + length > fileSize) 185 { 186 length = fileSize - offset; 187 } 188 189 if (length % dma::minSize) 190 { 191 log<level::ERR>("Read length is not a multiple of DMA minSize", 192 entry("LENGTH=%d", length)); 193 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 194 PLDM_INVALID_READ_LENGTH, 0, responsePtr); 195 return response; 196 } 197 198 using namespace dma; 199 DMA intf; 200 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, path, offset, 201 length, address, true); 202 } 203 204 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength) 205 { 206 uint32_t fileHandle = 0; 207 uint32_t offset = 0; 208 uint32_t length = 0; 209 uint64_t address = 0; 210 fs::path path(""); 211 212 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0); 213 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 214 215 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 216 { 217 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 218 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 219 return response; 220 } 221 222 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 223 &offset, &length, &address); 224 225 if (length % dma::minSize) 226 { 227 log<level::ERR>("Write length is not a multiple of DMA minSize", 228 entry("LENGTH=%d", length)); 229 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 230 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr); 231 return response; 232 } 233 234 if (!fs::exists(path)) 235 { 236 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 237 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 238 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 239 return response; 240 } 241 242 auto fileSize = fs::file_size(path); 243 if (offset >= fileSize) 244 { 245 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 246 entry("FILE_SIZE=%d", fileSize)); 247 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 248 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 249 return response; 250 } 251 252 using namespace dma; 253 DMA intf; 254 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, path, offset, 255 length, address, false); 256 } 257 258 } // namespace responder 259 } // namespace pldm 260