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