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 registerHandler(PLDM_OEM, PLDM_READ_FILE, std::move(readFile)); 38 registerHandler(PLDM_OEM, PLDM_WRITE_FILE, std::move(writeFile)); 39 } 40 41 } // namespace oem_ibm 42 43 namespace fs = std::filesystem; 44 using namespace phosphor::logging; 45 46 namespace dma 47 { 48 49 /** @struct AspeedXdmaOp 50 * 51 * Structure representing XDMA operation 52 */ 53 struct AspeedXdmaOp 54 { 55 uint64_t hostAddr; //!< the DMA address on the host side, configured by 56 //!< PCI subsystem. 57 uint32_t len; //!< the size of the transfer in bytes, it should be a 58 //!< multiple of 16 bytes 59 uint32_t upstream; //!< boolean indicating the direction of the DMA 60 //!< operation, true means a transfer from BMC to host. 61 }; 62 63 constexpr auto xdmaDev = "/dev/xdma"; 64 65 int DMA::transferDataHost(const fs::path& path, uint32_t offset, 66 uint32_t length, uint64_t address, bool upstream) 67 { 68 static const size_t pageSize = getpagesize(); 69 uint32_t numPages = length / pageSize; 70 uint32_t pageAlignedLength = numPages * pageSize; 71 72 if (length > pageAlignedLength) 73 { 74 pageAlignedLength += pageSize; 75 } 76 77 auto mmapCleanup = [pageAlignedLength](void* vgaMem) { 78 munmap(vgaMem, pageAlignedLength); 79 }; 80 81 int fd = -1; 82 int rc = 0; 83 fd = open(xdmaDev, O_RDWR); 84 if (fd < 0) 85 { 86 rc = -errno; 87 log<level::ERR>("Failed to open the XDMA device", entry("RC=%d", rc)); 88 return rc; 89 } 90 91 utils::CustomFD xdmaFd(fd); 92 93 void* vgaMem; 94 vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ, 95 MAP_SHARED, xdmaFd(), 0); 96 if (MAP_FAILED == vgaMem) 97 { 98 rc = -errno; 99 log<level::ERR>("Failed to mmap the XDMA device", entry("RC=%d", rc)); 100 return rc; 101 } 102 103 std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup); 104 105 if (upstream) 106 { 107 std::ifstream stream(path.string(), std::ios::in | std::ios::binary); 108 stream.seekg(offset); 109 110 // Writing to the VGA memory should be aligned at page boundary, 111 // otherwise write data into a buffer aligned at page boundary and 112 // then write to the VGA memory. 113 std::vector<char> buffer{}; 114 buffer.resize(pageAlignedLength); 115 stream.read(buffer.data(), length); 116 memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(), 117 pageAlignedLength); 118 119 if (static_cast<uint32_t>(stream.gcount()) != length) 120 { 121 log<level::ERR>("mismatch between number of characters to read and " 122 "the length read", 123 entry("LENGTH=%d", length), 124 entry("COUNT=%d", stream.gcount())); 125 return -1; 126 } 127 } 128 129 AspeedXdmaOp xdmaOp; 130 xdmaOp.upstream = upstream ? 1 : 0; 131 xdmaOp.hostAddr = address; 132 xdmaOp.len = length; 133 134 rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp)); 135 if (rc < 0) 136 { 137 rc = -errno; 138 log<level::ERR>("Failed to execute the DMA operation", 139 entry("RC=%d", rc), entry("UPSTREAM=%d", upstream), 140 entry("ADDRESS=%lld", address), 141 entry("LENGTH=%d", length)); 142 return rc; 143 } 144 145 if (!upstream) 146 { 147 std::ofstream stream(path.string(), 148 std::ios::in | std::ios::out | std::ios::binary); 149 150 stream.seekp(offset); 151 stream.write(static_cast<const char*>(vgaMemPtr.get()), length); 152 } 153 154 return 0; 155 } 156 157 } // namespace dma 158 159 Response readFileIntoMemory(const pldm_msg* request, size_t payloadLength) 160 { 161 uint32_t fileHandle = 0; 162 uint32_t offset = 0; 163 uint32_t length = 0; 164 uint64_t address = 0; 165 166 Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0); 167 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 168 169 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 170 { 171 encode_rw_file_memory_resp(request->hdr.instance_id, 172 PLDM_READ_FILE_INTO_MEMORY, 173 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 174 return response; 175 } 176 177 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset, 178 &length, &address); 179 180 using namespace pldm::filetable; 181 auto& table = buildFileTable(FILE_TABLE_JSON); 182 FileEntry value{}; 183 184 try 185 { 186 value = table.at(fileHandle); 187 } 188 catch (std::exception& e) 189 { 190 log<level::ERR>("File handle does not exist in the file table", 191 entry("HANDLE=%d", fileHandle)); 192 encode_rw_file_memory_resp(request->hdr.instance_id, 193 PLDM_READ_FILE_INTO_MEMORY, 194 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 195 return response; 196 } 197 198 if (!fs::exists(value.fsPath)) 199 { 200 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 201 encode_rw_file_memory_resp(request->hdr.instance_id, 202 PLDM_READ_FILE_INTO_MEMORY, 203 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 204 return response; 205 } 206 207 auto fileSize = fs::file_size(value.fsPath); 208 if (offset >= fileSize) 209 { 210 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 211 entry("FILE_SIZE=%d", fileSize)); 212 encode_rw_file_memory_resp(request->hdr.instance_id, 213 PLDM_READ_FILE_INTO_MEMORY, 214 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 215 return response; 216 } 217 218 if (offset + length > fileSize) 219 { 220 length = fileSize - offset; 221 } 222 223 if (length % dma::minSize) 224 { 225 log<level::ERR>("Read length is not a multiple of DMA minSize", 226 entry("LENGTH=%d", length)); 227 encode_rw_file_memory_resp(request->hdr.instance_id, 228 PLDM_READ_FILE_INTO_MEMORY, 229 PLDM_INVALID_READ_LENGTH, 0, responsePtr); 230 return response; 231 } 232 233 using namespace dma; 234 DMA intf; 235 return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath, 236 offset, length, address, true, 237 request->hdr.instance_id); 238 } 239 240 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength) 241 { 242 uint32_t fileHandle = 0; 243 uint32_t offset = 0; 244 uint32_t length = 0; 245 uint64_t address = 0; 246 247 Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0); 248 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 249 250 if (payloadLength != PLDM_RW_FILE_MEM_REQ_BYTES) 251 { 252 encode_rw_file_memory_resp(request->hdr.instance_id, 253 PLDM_WRITE_FILE_FROM_MEMORY, 254 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 255 return response; 256 } 257 258 decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset, 259 &length, &address); 260 261 if (length % dma::minSize) 262 { 263 log<level::ERR>("Write length is not a multiple of DMA minSize", 264 entry("LENGTH=%d", length)); 265 encode_rw_file_memory_resp(request->hdr.instance_id, 266 PLDM_WRITE_FILE_FROM_MEMORY, 267 PLDM_INVALID_WRITE_LENGTH, 0, responsePtr); 268 return response; 269 } 270 271 using namespace pldm::filetable; 272 auto& table = buildFileTable(FILE_TABLE_JSON); 273 FileEntry value{}; 274 275 try 276 { 277 value = table.at(fileHandle); 278 } 279 catch (std::exception& e) 280 { 281 log<level::ERR>("File handle does not exist in the file table", 282 entry("HANDLE=%d", fileHandle)); 283 encode_rw_file_memory_resp(request->hdr.instance_id, 284 PLDM_WRITE_FILE_FROM_MEMORY, 285 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 286 return response; 287 } 288 289 if (!fs::exists(value.fsPath)) 290 { 291 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 292 encode_rw_file_memory_resp(request->hdr.instance_id, 293 PLDM_WRITE_FILE_FROM_MEMORY, 294 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 295 return response; 296 } 297 298 auto fileSize = fs::file_size(value.fsPath); 299 if (offset >= fileSize) 300 { 301 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 302 entry("FILE_SIZE=%d", fileSize)); 303 encode_rw_file_memory_resp(request->hdr.instance_id, 304 PLDM_WRITE_FILE_FROM_MEMORY, 305 PLDM_DATA_OUT_OF_RANGE, 0, responsePtr); 306 return response; 307 } 308 309 using namespace dma; 310 DMA intf; 311 return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath, 312 offset, length, address, false, 313 request->hdr.instance_id); 314 } 315 316 Response getFileTable(const pldm_msg* request, size_t payloadLength) 317 { 318 uint32_t transferHandle = 0; 319 uint8_t transferFlag = 0; 320 uint8_t tableType = 0; 321 322 Response response(sizeof(pldm_msg_hdr) + 323 PLDM_GET_FILE_TABLE_MIN_RESP_BYTES); 324 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 325 326 if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES) 327 { 328 encode_get_file_table_resp(request->hdr.instance_id, 329 PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr, 0, 330 responsePtr); 331 return response; 332 } 333 334 auto rc = decode_get_file_table_req(request, payloadLength, &transferHandle, 335 &transferFlag, &tableType); 336 if (rc) 337 { 338 encode_get_file_table_resp(request->hdr.instance_id, rc, 0, 0, nullptr, 339 0, responsePtr); 340 return response; 341 } 342 343 if (tableType != PLDM_FILE_ATTRIBUTE_TABLE) 344 { 345 encode_get_file_table_resp(request->hdr.instance_id, 346 PLDM_INVALID_FILE_TABLE_TYPE, 0, 0, nullptr, 347 0, responsePtr); 348 return response; 349 } 350 351 using namespace pldm::filetable; 352 auto table = buildFileTable(FILE_TABLE_JSON); 353 auto attrTable = table(); 354 response.resize(response.size() + attrTable.size()); 355 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 356 357 if (attrTable.empty()) 358 { 359 encode_get_file_table_resp(request->hdr.instance_id, 360 PLDM_FILE_TABLE_UNAVAILABLE, 0, 0, nullptr, 361 0, responsePtr); 362 return response; 363 } 364 365 encode_get_file_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 0, 366 PLDM_START_AND_END, attrTable.data(), 367 attrTable.size(), responsePtr); 368 return response; 369 } 370 371 Response readFile(const pldm_msg* request, size_t payloadLength) 372 { 373 uint32_t fileHandle = 0; 374 uint32_t offset = 0; 375 uint32_t length = 0; 376 377 Response response(sizeof(pldm_msg_hdr) + PLDM_READ_FILE_RESP_BYTES); 378 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 379 380 if (payloadLength != PLDM_READ_FILE_REQ_BYTES) 381 { 382 encode_read_file_resp(request->hdr.instance_id, 383 PLDM_ERROR_INVALID_LENGTH, length, responsePtr); 384 return response; 385 } 386 387 auto rc = decode_read_file_req(request, payloadLength, &fileHandle, &offset, 388 &length); 389 390 if (rc) 391 { 392 encode_read_file_resp(request->hdr.instance_id, rc, 0, responsePtr); 393 return response; 394 } 395 396 using namespace pldm::filetable; 397 auto& table = buildFileTable(FILE_TABLE_JSON); 398 FileEntry value{}; 399 400 try 401 { 402 value = table.at(fileHandle); 403 } 404 catch (std::exception& e) 405 { 406 log<level::ERR>("File handle does not exist in the file table", 407 entry("HANDLE=%d", fileHandle)); 408 encode_read_file_resp(request->hdr.instance_id, 409 PLDM_INVALID_FILE_HANDLE, length, responsePtr); 410 return response; 411 } 412 413 if (!fs::exists(value.fsPath)) 414 { 415 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 416 encode_read_file_resp(request->hdr.instance_id, 417 PLDM_INVALID_FILE_HANDLE, length, responsePtr); 418 return response; 419 } 420 421 auto fileSize = fs::file_size(value.fsPath); 422 if (offset >= fileSize) 423 { 424 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 425 entry("FILE_SIZE=%d", fileSize)); 426 encode_read_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE, 427 length, responsePtr); 428 return response; 429 } 430 431 if (offset + length > fileSize) 432 { 433 length = fileSize - offset; 434 } 435 436 response.resize(response.size() + length); 437 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 438 auto fileDataPos = reinterpret_cast<char*>(responsePtr); 439 fileDataPos += sizeof(pldm_msg_hdr) + sizeof(uint8_t) + sizeof(length); 440 441 std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary); 442 stream.seekg(offset); 443 stream.read(fileDataPos, length); 444 445 encode_read_file_resp(request->hdr.instance_id, PLDM_SUCCESS, length, 446 responsePtr); 447 448 return response; 449 } 450 451 Response writeFile(const pldm_msg* request, size_t payloadLength) 452 { 453 uint32_t fileHandle = 0; 454 uint32_t offset = 0; 455 uint32_t length = 0; 456 size_t fileDataOffset = 0; 457 458 Response response(sizeof(pldm_msg_hdr) + PLDM_WRITE_FILE_RESP_BYTES); 459 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 460 461 if (payloadLength < PLDM_WRITE_FILE_REQ_BYTES) 462 { 463 encode_write_file_resp(request->hdr.instance_id, 464 PLDM_ERROR_INVALID_LENGTH, 0, responsePtr); 465 return response; 466 } 467 468 auto rc = decode_write_file_req(request, payloadLength, &fileHandle, 469 &offset, &length, &fileDataOffset); 470 471 if (rc) 472 { 473 encode_write_file_resp(request->hdr.instance_id, rc, 0, responsePtr); 474 return response; 475 } 476 477 using namespace pldm::filetable; 478 auto& table = buildFileTable(FILE_TABLE_JSON); 479 FileEntry value{}; 480 481 try 482 { 483 value = table.at(fileHandle); 484 } 485 catch (std::exception& e) 486 { 487 log<level::ERR>("File handle does not exist in the file table", 488 entry("HANDLE=%d", fileHandle)); 489 encode_write_file_resp(request->hdr.instance_id, 490 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 491 return response; 492 } 493 494 if (!fs::exists(value.fsPath)) 495 { 496 log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle)); 497 encode_write_file_resp(request->hdr.instance_id, 498 PLDM_INVALID_FILE_HANDLE, 0, responsePtr); 499 return response; 500 } 501 502 auto fileSize = fs::file_size(value.fsPath); 503 if (offset >= fileSize) 504 { 505 log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset), 506 entry("FILE_SIZE=%d", fileSize)); 507 encode_write_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE, 508 0, responsePtr); 509 return response; 510 } 511 512 auto fileDataPos = 513 reinterpret_cast<const char*>(request->payload) + fileDataOffset; 514 515 std::ofstream stream(value.fsPath, 516 std::ios::in | std::ios::out | std::ios::binary); 517 stream.seekp(offset); 518 stream.write(fileDataPos, length); 519 520 encode_write_file_resp(request->hdr.instance_id, PLDM_SUCCESS, length, 521 responsePtr); 522 523 return response; 524 } 525 526 } // namespace responder 527 } // namespace pldm 528