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