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