1 #include "config.h" 2 3 #include "file_io.hpp" 4 5 #include "file_table.hpp" 6 #include "libpldmresponder/utils.hpp" 7 #include "registration.hpp" 8 9 #include <fcntl.h> 10 #include <sys/mman.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <unistd.h> 14 15 #include <cstring> 16 #include <fstream> 17 #include <phosphor-logging/log.hpp> 18 19 #include "libpldm/base.h" 20 21 namespace pldm 22 { 23 24 namespace responder 25 { 26 27 namespace oem_ibm 28 { 29 30 void registerHandlers() 31 { 32 registerHandler(PLDM_OEM, PLDM_GET_FILE_TABLE, std::move(getFileTable)); 33 registerHandler(PLDM_OEM, PLDM_READ_FILE_INTO_MEMORY, 34 std::move(readFileIntoMemory)); 35 registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY, 36 std::move(writeFileFromMemory)); 37 } 38 39 } // namespace oem_ibm 40 41 namespace fs = std::filesystem; 42 using namespace phosphor::logging; 43 44 namespace dma 45 { 46 47 /** @struct AspeedXdmaOp 48 * 49 * Structure representing XDMA operation 50 */ 51 struct AspeedXdmaOp 52 { 53 uint64_t hostAddr; //!< the DMA address on the host side, configured by 54 //!< PCI subsystem. 55 uint32_t len; //!< the size of the transfer in bytes, it should be a 56 //!< multiple of 16 bytes 57 uint32_t upstream; //!< boolean indicating the direction of the DMA 58 //!< operation, true means a transfer from BMC to host. 59 }; 60 61 constexpr auto xdmaDev = "/dev/xdma"; 62 63 int DMA::transferDataHost(const fs::path& path, uint32_t offset, 64 uint32_t length, uint64_t address, bool upstream) 65 { 66 static const size_t pageSize = getpagesize(); 67 uint32_t numPages = length / pageSize; 68 uint32_t pageAlignedLength = numPages * pageSize; 69 70 if (length > pageAlignedLength) 71 { 72 pageAlignedLength += pageSize; 73 } 74 75 auto mmapCleanup = [pageAlignedLength](void* vgaMem) { 76 munmap(vgaMem, pageAlignedLength); 77 }; 78 79 int fd = -1; 80 int rc = 0; 81 fd = open(xdmaDev, O_RDWR); 82 if (fd < 0) 83 { 84 rc = -errno; 85 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc)); 86 return rc; 87 } 88 89 utils::CustomFD xdmaFd(fd); 90 91 void* vgaMem; 92 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ, 93 MAP_SHARED, xdmaFd(), 0); 94 if (MAP_FAILED == vgaMem) 95 { 96 rc = -errno; 97 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc)); 98 return rc; 99 } 100 101 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup); 102 103 if (upstream) 104 { 105 std::ifstream stream(path.string(), std::ios::in | std::ios::binary); 106 stream.seekg(offset); 107 108 // Writing to the VGA memory should be aligned at page boundary, 109 // otherwise write data into a buffer aligned at page boundary and 110 // then write to the VGA memory. 111 std::vector<char> buffer{}; 112 buffer.resize(pageAlignedLength); 113 stream.read(buffer.data(), length); 114 memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(), 115 pageAlignedLength); 116 117 if (static_cast<uint32_t>(stream.gcount()) != length) 118 { 119 log<level::ERR>("mismatch between number of characters to read and " 120 "the length read", 121 entry("LENGTH=%d", length), 122 entry("COUNT=%d", stream.gcount())); 123 return -1; 124 } 125 } 126 127 AspeedXdmaOp xdmaOp; 128 xdmaOp.upstream = upstream ? 1 : 0; 129 xdmaOp.hostAddr = address; 130 xdmaOp.len = length; 131 132 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp)); 133 if (rc < 0) 134 { 135 rc = -errno; 136 log<level::ERR>("Failed to execute the DMA operation", 137 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream), 138 entry("ADDRESS=%lld", address), 139 entry("LENGTH=%d", length)); 140 return rc; 141 } 142 143 if (!upstream) 144 { 145 std::ofstream stream(path.string(), 146 std::ios::in | std::ios::out | std::ios::binary); 147 148 stream.seekp(offset); 149 stream.write(static_cast<const char*>(vgaMemPtr.get()), length); 150 } 151 152 return 0; 153 } 154 155 } // namespace dma 156 157 Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength) 158 { 159 uint32_t fileHandle = 0; 160 uint32_t offset = 0; 161 uint32_t length = 0; 162 uint64_t address = 0; 163 164 Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0); 165 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 166 167 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 168 { 169 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 170 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 171 return response; 172 } 173 174 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 175 &offset, &length, &address); 176 177 using namespace pldm::filetable; 178 auto& table = buildFileTable(FILE_TABLE_JSON); 179 FileEntry value{}; 180 181 try 182 { 183 value = table.at(fileHandle); 184 } 185 catch (std::exception& e) 186 { 187 log<level::ERR>("File handle does not exist in the file table", 188 entry("HANDLE=%d", fileHandle)); 189 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 190 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 191 return response; 192 } 193 194 if (!fs::exists(value.fsPath)) 195 { 196 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 197 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 198 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 199 return response; 200 } 201 202 auto fileSize = fs::file_size(value.fsPath); 203 if (offset >= fileSize) 204 { 205 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 206 entry("FILE_SIZE=%d", fileSize)); 207 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 208 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 209 return response; 210 } 211 212 if (offset + length > fileSize) 213 { 214 length = fileSize - offset; 215 } 216 217 if (length % dma::minSize) 218 { 219 log<level::ERR>("Read length is not a multiple of DMA minSize", 220 entry("LENGTH=%d", length)); 221 encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY, 222 PLDM_INVALID_READ_LENGTH, 0, responsePtr); 223 return response; 224 } 225 226 using namespace dma; 227 DMA intf; 228 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath, 229 offset, length, address, true); 230 } 231 232 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength) 233 { 234 uint32_t fileHandle = 0; 235 uint32_t offset = 0; 236 uint32_t length = 0; 237 uint64_t address = 0; 238 239 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0); 240 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 241 242 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 243 { 244 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 245 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 246 return response; 247 } 248 249 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 250 &offset, &length, &address); 251 252 if (length % dma::minSize) 253 { 254 log<level::ERR>("Write length is not a multiple of DMA minSize", 255 entry("LENGTH=%d", length)); 256 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 257 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr); 258 return response; 259 } 260 261 using namespace pldm::filetable; 262 auto& table = buildFileTable(FILE_TABLE_JSON); 263 FileEntry value{}; 264 265 try 266 { 267 value = table.at(fileHandle); 268 } 269 catch (std::exception& e) 270 { 271 log<level::ERR>("File handle does not exist in the file table", 272 entry("HANDLE=%d", fileHandle)); 273 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 274 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 275 return response; 276 } 277 278 if (!fs::exists(value.fsPath)) 279 { 280 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 281 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 282 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 283 return response; 284 } 285 286 auto fileSize = fs::file_size(value.fsPath); 287 if (offset >= fileSize) 288 { 289 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 290 entry("FILE_SIZE=%d", fileSize)); 291 encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY, 292 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 293 return response; 294 } 295 296 using namespace dma; 297 DMA intf; 298 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath, 299 offset, length, address, false); 300 } 301 302 Response getFileTable(const pldm_msg* request, size_t payloadLength) 303 { 304 uint32_t transferHandle = 0; 305 uint8_t transferFlag = 0; 306 uint8_t tableType = 0; 307 308 Response response(sizeof(pldm_msg_hdr) + 309 PLDM_GET_FILE_TABLE_MIN_RESP_BYTES); 310 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 311 312 if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES) 313 { 314 encode_get_file_table_resp(0, PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr, 315 0, responsePtr); 316 return response; 317 } 318 319 auto rc = 320 decode_get_file_table_req(request->payload, payloadLength, 321 &transferHandle, &transferFlag, &tableType); 322 if (rc) 323 { 324 encode_get_file_table_resp(0, rc, 0, 0, nullptr, 0, responsePtr); 325 return response; 326 } 327 328 if (tableType != PLDM_FILE_ATTRIBUTE_TABLE) 329 { 330 encode_get_file_table_resp(0, PLDM_INVALID_FILE_TABLE_TYPE, 0, 0, 331 nullptr, 0, responsePtr); 332 return response; 333 } 334 335 using namespace pldm::filetable; 336 auto table = buildFileTable(FILE_TABLE_JSON); 337 auto attrTable = table(); 338 response.resize(response.size() + attrTable.size()); 339 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 340 341 if (attrTable.empty()) 342 { 343 encode_get_file_table_resp(0, PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, 344 nullptr, 0, responsePtr); 345 return response; 346 } 347 348 encode_get_file_table_resp(0, PLDM_SUCCESS, 0, PLDM_START_AND_END, 349 attrTable.data(), attrTable.size(), responsePtr); 350 return response; 351 } 352 353 } // namespace responder 354 } // namespace pldm 355