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(request->hdr.instance_id, 170 PLDM_READ_FILE_INTO_MEMORY, 171 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 172 return response; 173 } 174 175 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 176 &offset, &length, &address); 177 178 using namespace pldm::filetable; 179 auto& table = buildFileTable(FILE_TABLE_JSON); 180 FileEntry value{}; 181 182 try 183 { 184 value = table.at(fileHandle); 185 } 186 catch (std::exception& e) 187 { 188 log<level::ERR>("File handle does not exist in the file table", 189 entry("HANDLE=%d", fileHandle)); 190 encode_rw_file_memory_resp(request->hdr.instance_id, 191 PLDM_READ_FILE_INTO_MEMORY, 192 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 193 return response; 194 } 195 196 if (!fs::exists(value.fsPath)) 197 { 198 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 199 encode_rw_file_memory_resp(request->hdr.instance_id, 200 PLDM_READ_FILE_INTO_MEMORY, 201 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 202 return response; 203 } 204 205 auto fileSize = fs::file_size(value.fsPath); 206 if (offset >= fileSize) 207 { 208 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 209 entry("FILE_SIZE=%d", fileSize)); 210 encode_rw_file_memory_resp(request->hdr.instance_id, 211 PLDM_READ_FILE_INTO_MEMORY, 212 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 213 return response; 214 } 215 216 if (offset + length > fileSize) 217 { 218 length = fileSize - offset; 219 } 220 221 if (length % dma::minSize) 222 { 223 log<level::ERR>("Read length is not a multiple of DMA minSize", 224 entry("LENGTH=%d", length)); 225 encode_rw_file_memory_resp(request->hdr.instance_id, 226 PLDM_READ_FILE_INTO_MEMORY, 227 PLDM_INVALID_READ_LENGTH, 0, responsePtr); 228 return response; 229 } 230 231 using namespace dma; 232 DMA intf; 233 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath, 234 offset, length, address, true, 235 request->hdr.instance_id); 236 } 237 238 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength) 239 { 240 uint32_t fileHandle = 0; 241 uint32_t offset = 0; 242 uint32_t length = 0; 243 uint64_t address = 0; 244 245 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0); 246 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 247 248 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 249 { 250 encode_rw_file_memory_resp(request->hdr.instance_id, 251 PLDM_WRITE_FILE_FROM_MEMORY, 252 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 253 return response; 254 } 255 256 decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle, 257 &offset, &length, &address); 258 259 if (length % dma::minSize) 260 { 261 log<level::ERR>("Write length is not a multiple of DMA minSize", 262 entry("LENGTH=%d", length)); 263 encode_rw_file_memory_resp(request->hdr.instance_id, 264 PLDM_WRITE_FILE_FROM_MEMORY, 265 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr); 266 return response; 267 } 268 269 using namespace pldm::filetable; 270 auto& table = buildFileTable(FILE_TABLE_JSON); 271 FileEntry value{}; 272 273 try 274 { 275 value = table.at(fileHandle); 276 } 277 catch (std::exception& e) 278 { 279 log<level::ERR>("File handle does not exist in the file table", 280 entry("HANDLE=%d", fileHandle)); 281 encode_rw_file_memory_resp(request->hdr.instance_id, 282 PLDM_WRITE_FILE_FROM_MEMORY, 283 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 284 return response; 285 } 286 287 if (!fs::exists(value.fsPath)) 288 { 289 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 290 encode_rw_file_memory_resp(request->hdr.instance_id, 291 PLDM_WRITE_FILE_FROM_MEMORY, 292 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 293 return response; 294 } 295 296 auto fileSize = fs::file_size(value.fsPath); 297 if (offset >= fileSize) 298 { 299 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 300 entry("FILE_SIZE=%d", fileSize)); 301 encode_rw_file_memory_resp(request->hdr.instance_id, 302 PLDM_WRITE_FILE_FROM_MEMORY, 303 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 304 return response; 305 } 306 307 using namespace dma; 308 DMA intf; 309 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath, 310 offset, length, address, false, 311 request->hdr.instance_id); 312 } 313 314 Response getFileTable(const pldm_msg* request, size_t payloadLength) 315 { 316 uint32_t transferHandle = 0; 317 uint8_t transferFlag = 0; 318 uint8_t tableType = 0; 319 320 Response response(sizeof(pldm_msg_hdr) + 321 PLDM_GET_FILE_TABLE_MIN_RESP_BYTES); 322 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 323 324 if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES) 325 { 326 encode_get_file_table_resp(request->hdr.instance_id, 327 PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr, 0, 328 responsePtr); 329 return response; 330 } 331 332 auto rc = 333 decode_get_file_table_req(request->payload, payloadLength, 334 &transferHandle, &transferFlag, &tableType); 335 if (rc) 336 { 337 encode_get_file_table_resp(request->hdr.instance_id, rc, 0, 0, nullptr, 338 0, responsePtr); 339 return response; 340 } 341 342 if (tableType != PLDM_FILE_ATTRIBUTE_TABLE) 343 { 344 encode_get_file_table_resp(request->hdr.instance_id, 345 PLDM_INVALID_FILE_TABLE_TYPE, 0, 0, nullptr, 346 0, responsePtr); 347 return response; 348 } 349 350 using namespace pldm::filetable; 351 auto table = buildFileTable(FILE_TABLE_JSON); 352 auto attrTable = table(); 353 response.resize(response.size() + attrTable.size()); 354 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 355 356 if (attrTable.empty()) 357 { 358 encode_get_file_table_resp(request->hdr.instance_id, 359 PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, nullptr, 360 0, responsePtr); 361 return response; 362 } 363 364 encode_get_file_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 0, 365 PLDM_START_AND_END, attrTable.data(), 366 attrTable.size(), responsePtr); 367 return response; 368 } 369 370 } // namespace responder 371 } // namespace pldm 372