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