1 #include "libpldmresponder/file_io.hpp" 2 #include "libpldmresponder/file_io_by_type.hpp" 3 #include "libpldmresponder/file_io_type_lid.hpp" 4 #include "libpldmresponder/file_io_type_pel.hpp" 5 #include "libpldmresponder/file_table.hpp" 6 #include "xyz/openbmc_project/Common/error.hpp" 7 8 #include <filesystem> 9 #include <fstream> 10 #include <nlohmann/json.hpp> 11 #include <phosphor-logging/elog-errors.hpp> 12 13 #include "libpldm/base.h" 14 #include "libpldm/file_io.h" 15 16 #include <gmock/gmock-matchers.h> 17 #include <gmock/gmock.h> 18 #include <gtest/gtest.h> 19 20 namespace fs = std::filesystem; 21 using Json = nlohmann::json; 22 using namespace pldm::filetable; 23 24 class TestFileTable : public testing::Test 25 { 26 public: 27 void SetUp() override 28 { 29 // Create a temporary directory to hold the config file and files to 30 // populate the file table. 31 char tmppldm[] = "/tmp/pldm_fileio_table.XXXXXX"; 32 dir = fs::path(mkdtemp(tmppldm)); 33 34 // Copy the sample image files to the directory 35 fs::copy("./files", dir); 36 37 imageFile = dir / "NVRAM-IMAGE"; 38 auto jsonObjects = Json::array(); 39 auto obj = Json::object(); 40 obj["path"] = imageFile.c_str(); 41 obj["file_traits"] = 1; 42 43 jsonObjects.push_back(obj); 44 obj.clear(); 45 cksumFile = dir / "NVRAM-IMAGE-CKSUM"; 46 obj["path"] = cksumFile.c_str(); 47 obj["file_traits"] = 4; 48 jsonObjects.push_back(obj); 49 50 fileTableConfig = dir / "configFile.json"; 51 std::ofstream file(fileTableConfig.c_str()); 52 file << std::setw(4) << jsonObjects << std::endl; 53 } 54 55 void TearDown() override 56 { 57 fs::remove_all(dir); 58 } 59 60 fs::path dir; 61 fs::path imageFile; 62 fs::path cksumFile; 63 fs::path fileTableConfig; 64 65 // <4 bytes - File handle - 0 (0x00 0x00 0x00 0x00)>, 66 // <2 bytes - Filename length - 11 (0x0b 0x00> 67 // <11 bytes - Filename - ASCII for NVRAM-IMAGE> 68 // <4 bytes - File size - 1024 (0x00 0x04 0x00 0x00)> 69 // <4 bytes - File traits - 1 (0x01 0x00 0x00 0x00)> 70 // <4 bytes - File handle - 1 (0x01 0x00 0x00 0x00)>, 71 // <2 bytes - Filename length - 17 (0x11 0x00> 72 // <17 bytes - Filename - ASCII for NVRAM-IMAGE-CKSUM> 73 // <4 bytes - File size - 16 (0x0f 0x00 0x00 0x00)> 74 // <4 bytes - File traits - 4 (0x04 0x00 0x00 0x00)> 75 // No pad bytes added since the length for both the file entries in the 76 // table is 56, which is a multiple of 4. 77 // <4 bytes - Checksum - 2088303182(0x4e 0xfa 0x78 0x7c)> 78 Table attrTable = { 79 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d, 0x2d, 80 0x49, 0x4d, 0x41, 0x47, 0x45, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 81 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d, 82 0x2d, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x2d, 0x43, 0x4b, 0x53, 0x55, 0x4d, 83 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4e, 0xfa, 0x78, 0x7c}; 84 }; 85 86 namespace pldm 87 { 88 89 namespace responder 90 { 91 92 namespace dma 93 { 94 95 class MockDMA 96 { 97 public: 98 MOCK_METHOD5(transferDataHost, 99 int(const fs::path& file, uint32_t offset, uint32_t length, 100 uint64_t address, bool upstream)); 101 }; 102 103 } // namespace dma 104 } // namespace responder 105 } // namespace pldm 106 using namespace pldm::responder; 107 using ::testing::_; 108 using ::testing::Return; 109 110 TEST(TransferDataHost, GoodPath) 111 { 112 using namespace pldm::responder::dma; 113 114 MockDMA dmaObj; 115 fs::path path(""); 116 117 // Minimum length of 16 and expect transferDataHost to be called once 118 // returns the default value of 0 (the return type of transferDataHost is 119 // int, the default value for int is 0) 120 uint32_t length = minSize; 121 EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1); 122 auto response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, 123 path, 0, length, 0, true, 0); 124 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 125 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 126 ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), 127 &length, sizeof(length))); 128 129 // maxsize of DMA 130 length = maxSize; 131 EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1); 132 response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path, 133 0, length, 0, true, 0); 134 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 135 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 136 ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), 137 &length, sizeof(length))); 138 139 // length greater than maxsize of DMA 140 length = maxSize + minSize; 141 EXPECT_CALL(dmaObj, transferDataHost(path, 0, maxSize, 0, true)).Times(1); 142 EXPECT_CALL(dmaObj, transferDataHost(path, maxSize, minSize, maxSize, true)) 143 .Times(1); 144 response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path, 145 0, length, 0, true, 0); 146 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 147 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 148 ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), 149 &length, sizeof(length))); 150 151 // length greater than 2*maxsize of DMA 152 length = 3 * maxSize; 153 EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, true)).Times(3); 154 response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path, 155 0, length, 0, true, 0); 156 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 157 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 158 ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), 159 &length, sizeof(length))); 160 161 // check for downstream(copy data from host to BMC) parameter 162 length = minSize; 163 EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, false)).Times(1); 164 response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path, 165 0, length, 0, false, 0); 166 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 167 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 168 ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), 169 &length, sizeof(length))); 170 } 171 172 TEST(TransferDataHost, BadPath) 173 { 174 using namespace pldm::responder::dma; 175 176 MockDMA dmaObj; 177 fs::path path(""); 178 179 // Minimum length of 16 and transferDataHost returning a negative errno 180 uint32_t length = minSize; 181 EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, _)).WillOnce(Return(-1)); 182 auto response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, 183 path, 0, length, 0, true, 0); 184 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 185 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR); 186 187 // length greater than maxsize of DMA and transferDataHost returning a 188 // negative errno 189 length = maxSize + minSize; 190 EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, _)).WillOnce(Return(-1)); 191 response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path, 192 0, length, 0, true, 0); 193 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 194 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR); 195 } 196 197 TEST(ReadFileIntoMemory, BadPath) 198 { 199 uint32_t fileHandle = 0; 200 uint32_t offset = 0; 201 uint32_t length = 10; 202 uint64_t address = 0; 203 204 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 205 requestMsg{}; 206 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 207 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 208 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 209 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 210 sizeof(length)); 211 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 212 sizeof(length), 213 &address, sizeof(address)); 214 215 // Pass invalid payload length 216 auto response = readFileIntoMemory(request, 0); 217 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 218 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); 219 } 220 221 TEST_F(TestFileTable, ReadFileInvalidFileHandle) 222 { 223 // Invalid file handle in the file table 224 uint32_t fileHandle = 2; 225 uint32_t offset = 0; 226 uint32_t length = 0; 227 uint64_t address = 0; 228 229 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 230 requestMsg{}; 231 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 232 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 233 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 234 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 235 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 236 sizeof(length)); 237 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 238 sizeof(length), 239 &address, sizeof(address)); 240 241 using namespace pldm::filetable; 242 // Initialise the file table with 2 valid file handles 0 & 1. 243 auto& table = buildFileTable(fileTableConfig.c_str()); 244 245 auto response = readFileIntoMemory(request, requestPayloadLength); 246 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 247 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE); 248 // Clear the file table contents. 249 table.clear(); 250 } 251 252 TEST_F(TestFileTable, ReadFileInvalidOffset) 253 { 254 uint32_t fileHandle = 0; 255 // The file size is 1024, so the offset is invalid 256 uint32_t offset = 1024; 257 uint32_t length = 0; 258 uint64_t address = 0; 259 260 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 261 requestMsg{}; 262 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 263 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 264 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 265 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 266 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 267 sizeof(length)); 268 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 269 sizeof(length), 270 &address, sizeof(address)); 271 272 using namespace pldm::filetable; 273 auto& table = buildFileTable(fileTableConfig.c_str()); 274 275 auto response = readFileIntoMemory(request, requestPayloadLength); 276 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 277 ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE); 278 // Clear the file table contents. 279 table.clear(); 280 } 281 282 TEST_F(TestFileTable, ReadFileInvalidLength) 283 { 284 uint32_t fileHandle = 0; 285 uint32_t offset = 100; 286 // Length should be a multiple of dma min size(16) 287 uint32_t length = 10; 288 uint64_t address = 0; 289 290 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 291 requestMsg{}; 292 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 293 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 294 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 295 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 296 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 297 sizeof(length)); 298 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 299 sizeof(length), 300 &address, sizeof(address)); 301 302 using namespace pldm::filetable; 303 auto& table = buildFileTable(fileTableConfig.c_str()); 304 305 auto response = readFileIntoMemory(request, requestPayloadLength); 306 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 307 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH); 308 // Clear the file table contents. 309 table.clear(); 310 } 311 312 TEST_F(TestFileTable, ReadFileInvalidEffectiveLength) 313 { 314 uint32_t fileHandle = 0; 315 // valid offset 316 uint32_t offset = 100; 317 // length + offset exceeds the size, so effective length is 318 // filesize(1024) - offset(100). The effective length is not a multiple of 319 // DMA min size(16) 320 uint32_t length = 1024; 321 uint64_t address = 0; 322 323 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 324 requestMsg{}; 325 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 326 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 327 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 328 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 329 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 330 sizeof(length)); 331 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 332 sizeof(length), 333 &address, sizeof(address)); 334 335 using namespace pldm::filetable; 336 auto& table = buildFileTable(fileTableConfig.c_str()); 337 338 auto response = readFileIntoMemory(request, requestPayloadLength); 339 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 340 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH); 341 // Clear the file table contents. 342 table.clear(); 343 } 344 345 TEST(WriteFileFromMemory, BadPath) 346 { 347 uint32_t fileHandle = 0; 348 uint32_t offset = 0; 349 uint32_t length = 10; 350 uint64_t address = 0; 351 352 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 353 requestMsg{}; 354 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 355 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 356 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 357 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 358 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 359 sizeof(length)); 360 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 361 sizeof(length), 362 &address, sizeof(address)); 363 364 // Pass invalid payload length 365 auto response = writeFileFromMemory(request, 0); 366 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 367 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); 368 369 // The length field is not a multiple of DMA minsize 370 response = writeFileFromMemory(request, requestPayloadLength); 371 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 372 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_WRITE_LENGTH); 373 } 374 375 TEST_F(TestFileTable, WriteFileInvalidFileHandle) 376 { 377 // Invalid file handle in the file table 378 uint32_t fileHandle = 2; 379 uint32_t offset = 0; 380 uint32_t length = 16; 381 uint64_t address = 0; 382 383 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 384 requestMsg{}; 385 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 386 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 387 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 388 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 389 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 390 sizeof(length)); 391 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 392 sizeof(length), 393 &address, sizeof(address)); 394 395 using namespace pldm::filetable; 396 // Initialise the file table with 2 valid file handles 0 & 1. 397 auto& table = buildFileTable(fileTableConfig.c_str()); 398 399 auto response = writeFileFromMemory(request, requestPayloadLength); 400 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 401 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE); 402 // Clear the file table contents. 403 table.clear(); 404 } 405 406 TEST_F(TestFileTable, WriteFileInvalidOffset) 407 { 408 uint32_t fileHandle = 0; 409 // The file size is 1024, so the offset is invalid 410 uint32_t offset = 1024; 411 uint32_t length = 16; 412 uint64_t address = 0; 413 414 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES> 415 requestMsg{}; 416 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 417 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 418 memcpy(request->payload, &fileHandle, sizeof(fileHandle)); 419 memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset)); 420 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length, 421 sizeof(length)); 422 memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) + 423 sizeof(length), 424 &address, sizeof(address)); 425 426 using namespace pldm::filetable; 427 // Initialise the file table with 2 valid file handles 0 & 1. 428 auto& table = buildFileTable(TestFileTable::fileTableConfig.c_str()); 429 430 auto response = writeFileFromMemory(request, requestPayloadLength); 431 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 432 ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE); 433 // Clear the file table contents. 434 table.clear(); 435 } 436 437 TEST(FileTable, ConfigNotExist) 438 { 439 FileTable tableObj(""); 440 EXPECT_EQ(tableObj.isEmpty(), true); 441 } 442 443 TEST_F(TestFileTable, ValidateFileEntry) 444 { 445 FileTable tableObj(fileTableConfig.c_str()); 446 447 // Test file handle 0, the file size is 1K bytes. 448 auto value = tableObj.at(0); 449 ASSERT_EQ(value.handle, 0); 450 ASSERT_EQ(strcmp(value.fsPath.c_str(), imageFile.c_str()), 0); 451 ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value.fsPath)), 1024); 452 ASSERT_EQ(value.traits.value, 1); 453 ASSERT_EQ(true, fs::exists(value.fsPath)); 454 455 // Test file handle 1, the file size is 16 bytes 456 auto value1 = tableObj.at(1); 457 ASSERT_EQ(value1.handle, 1); 458 ASSERT_EQ(strcmp(value1.fsPath.c_str(), cksumFile.c_str()), 0); 459 ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value1.fsPath)), 16); 460 ASSERT_EQ(value1.traits.value, 4); 461 ASSERT_EQ(true, fs::exists(value1.fsPath)); 462 463 // Test invalid file handle 464 ASSERT_THROW(tableObj.at(2), std::out_of_range); 465 } 466 467 TEST_F(TestFileTable, ValidateFileTable) 468 { 469 FileTable tableObj(fileTableConfig.c_str()); 470 471 // Validate file attribute table 472 auto table = tableObj(); 473 ASSERT_EQ(true, 474 std::equal(attrTable.begin(), attrTable.end(), table.begin())); 475 } 476 477 TEST_F(TestFileTable, GetFileTableCommand) 478 { 479 // Initialise the file table with a valid handle of 0 & 1 480 auto& table = buildFileTable(fileTableConfig.c_str()); 481 482 uint32_t transferHandle = 0; 483 uint8_t opFlag = 0; 484 uint8_t type = PLDM_FILE_ATTRIBUTE_TABLE; 485 uint32_t nextTransferHandle = 0; 486 uint8_t transferFlag = PLDM_START_AND_END; 487 488 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES> 489 requestMsg{}; 490 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 491 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 492 auto request = reinterpret_cast<pldm_get_file_table_req*>( 493 requestMsg.data() + sizeof(pldm_msg_hdr)); 494 request->transfer_handle = transferHandle; 495 request->operation_flag = opFlag; 496 request->table_type = type; 497 498 auto response = getFileTable(requestMsgPtr, requestPayloadLength); 499 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 500 ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS); 501 size_t offsetSize = sizeof(responsePtr->payload[0]); 502 ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &nextTransferHandle, 503 sizeof(nextTransferHandle))); 504 offsetSize += sizeof(nextTransferHandle); 505 ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &transferFlag, 506 sizeof(transferFlag))); 507 offsetSize += sizeof(transferFlag); 508 ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, attrTable.data(), 509 attrTable.size())); 510 table.clear(); 511 } 512 513 TEST_F(TestFileTable, GetFileTableCommandReqLengthMismatch) 514 { 515 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES> 516 requestMsg{}; 517 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 518 519 // Pass invalid command payload length 520 auto response = getFileTable(request, 0); 521 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 522 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); 523 } 524 525 TEST_F(TestFileTable, GetFileTableCommandOEMAttrTable) 526 { 527 uint32_t transferHandle = 0; 528 uint8_t opFlag = 0; 529 uint8_t type = PLDM_OEM_FILE_ATTRIBUTE_TABLE; 530 531 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES> 532 requestMsg{}; 533 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 534 size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr); 535 auto request = reinterpret_cast<pldm_get_file_table_req*>( 536 requestMsg.data() + sizeof(pldm_msg_hdr)); 537 request->transfer_handle = transferHandle; 538 request->operation_flag = opFlag; 539 request->table_type = type; 540 541 auto response = getFileTable(requestMsgPtr, requestPayloadLength); 542 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 543 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_TABLE_TYPE); 544 } 545 546 TEST_F(TestFileTable, ReadFileBadPath) 547 { 548 uint32_t fileHandle = 1; 549 uint32_t offset = 0; 550 uint32_t length = 0x4; 551 552 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES> 553 requestMsg{}; 554 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 555 auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr); 556 auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() + 557 sizeof(pldm_msg_hdr)); 558 559 request->file_handle = fileHandle; 560 request->offset = offset; 561 request->length = length; 562 563 using namespace pldm::filetable; 564 // Initialise the file table with 2 valid file handles 0 & 1. 565 auto& table = buildFileTable(fileTableConfig.c_str()); 566 567 // Invalid payload length 568 auto response = readFile(requestMsgPtr, 0); 569 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 570 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); 571 572 // Data out of range. File size is 1024, offset = 1024 is invalid. 573 request->offset = 1024; 574 575 response = readFile(requestMsgPtr, payload_length); 576 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 577 ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE); 578 579 // Invalid file handle 580 request->file_handle = 2; 581 582 response = readFile(requestMsgPtr, payload_length); 583 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 584 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE); 585 586 table.clear(); 587 } 588 589 TEST_F(TestFileTable, ReadFileGoodPath) 590 { 591 uint32_t fileHandle = 0; 592 uint32_t offset = 0; 593 uint32_t length = 0x4; 594 595 std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES> 596 requestMsg{}; 597 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 598 auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr); 599 auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() + 600 sizeof(pldm_msg_hdr)); 601 602 request->file_handle = fileHandle; 603 request->offset = offset; 604 request->length = length; 605 606 using namespace pldm::filetable; 607 // Initialise the file table with 2 valid file handles 0 & 1. 608 auto& table = buildFileTable(fileTableConfig.c_str()); 609 FileEntry value{}; 610 value = table.at(fileHandle); 611 612 std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary); 613 stream.seekg(offset); 614 std::vector<char> buffer(length); 615 stream.read(buffer.data(), length); 616 617 auto responseMsg = readFile(requestMsgPtr, payload_length); 618 auto response = reinterpret_cast<pldm_read_file_resp*>( 619 responseMsg.data() + sizeof(pldm_msg_hdr)); 620 ASSERT_EQ(response->completion_code, PLDM_SUCCESS); 621 ASSERT_EQ(response->length, length); 622 ASSERT_EQ(0, memcmp(response->file_data, buffer.data(), length)); 623 624 // Test condition offset + length > fileSize; 625 size_t fileSize = 1024; 626 request->offset = 1023; 627 request->length = 10; 628 629 stream.seekg(request->offset); 630 buffer.resize(fileSize - request->offset); 631 stream.read(buffer.data(), (fileSize - request->offset)); 632 633 responseMsg = readFile(requestMsgPtr, payload_length); 634 response = reinterpret_cast<pldm_read_file_resp*>(responseMsg.data() + 635 sizeof(pldm_msg_hdr)); 636 ASSERT_EQ(response->completion_code, PLDM_SUCCESS); 637 ASSERT_EQ(response->length, (fileSize - request->offset)); 638 ASSERT_EQ(0, memcmp(response->file_data, buffer.data(), 639 (fileSize - request->offset))); 640 641 table.clear(); 642 } 643 644 TEST_F(TestFileTable, WriteFileBadPath) 645 { 646 uint32_t fileHandle = 0; 647 uint32_t offset = 0; 648 uint32_t length = 0x10; 649 650 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 651 PLDM_WRITE_FILE_REQ_BYTES + length); 652 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 653 auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr); 654 auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() + 655 sizeof(pldm_msg_hdr)); 656 657 using namespace pldm::filetable; 658 // Initialise the file table with 2 valid file handles 0 & 1. 659 auto& table = buildFileTable(fileTableConfig.c_str()); 660 661 request->file_handle = fileHandle; 662 request->offset = offset; 663 request->length = length; 664 665 // Invalid payload length 666 auto response = writeFile(requestMsgPtr, 0); 667 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 668 ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); 669 670 // Data out of range. File size is 1024, offset = 1024 is invalid. 671 request->offset = 1024; 672 673 response = writeFile(requestMsgPtr, payload_length); 674 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 675 ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE); 676 677 // Invalid file handle 678 request->file_handle = 2; 679 680 response = writeFile(requestMsgPtr, payload_length); 681 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 682 ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE); 683 684 table.clear(); 685 } 686 687 TEST_F(TestFileTable, WriteFileGoodPath) 688 { 689 uint32_t fileHandle = 1; 690 uint32_t offset = 0; 691 std::array<uint8_t, 4> fileData = {0x41, 0x42, 0x43, 0x44}; 692 uint32_t length = fileData.size(); 693 694 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 695 PLDM_WRITE_FILE_REQ_BYTES + length); 696 auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data()); 697 auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr); 698 auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() + 699 sizeof(pldm_msg_hdr)); 700 701 using namespace pldm::filetable; 702 // Initialise the file table with 2 valid file handles 0 & 1. 703 auto& table = buildFileTable(fileTableConfig.c_str()); 704 FileEntry value{}; 705 value = table.at(fileHandle); 706 707 request->file_handle = fileHandle; 708 request->offset = offset; 709 request->length = length; 710 memcpy(request->file_data, fileData.data(), fileData.size()); 711 712 auto responseMsg = writeFile(requestMsgPtr, payload_length); 713 auto response = reinterpret_cast<pldm_read_file_resp*>( 714 responseMsg.data() + sizeof(pldm_msg_hdr)); 715 716 std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary); 717 stream.seekg(offset); 718 std::vector<char> buffer(length); 719 stream.read(buffer.data(), length); 720 721 ASSERT_EQ(response->completion_code, PLDM_SUCCESS); 722 ASSERT_EQ(response->length, length); 723 ASSERT_EQ(0, memcmp(fileData.data(), buffer.data(), length)); 724 725 table.clear(); 726 } 727 728 TEST(writeFileByTypeFromMemory, testBadPath) 729 { 730 const auto hdr_size = sizeof(pldm_msg_hdr); 731 std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES> 732 requestMsg{}; 733 auto req = reinterpret_cast<pldm_msg*>(requestMsg.data()); 734 size_t requestPayloadLength = requestMsg.size() - hdr_size; 735 struct pldm_read_write_file_by_type_memory_req* request = 736 reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>( 737 req->payload); 738 request->file_type = PLDM_FILE_TYPE_PEL; 739 request->file_handle = 0xFFFFFFFF; 740 request->offset = 0; 741 request->length = 17; 742 request->address = 0; 743 744 auto response = writeFileByTypeFromMemory(req, 0); 745 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 746 747 struct pldm_read_write_file_by_type_memory_resp* resp = 748 reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>( 749 responsePtr->payload); 750 ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code); 751 752 response = writeFileByTypeFromMemory(req, requestPayloadLength); 753 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 754 755 resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>( 756 responsePtr->payload); 757 ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code); 758 } 759 760 TEST(getHandlerByType, allPaths) 761 { 762 uint32_t fileHandle{}; 763 auto handler = getHandlerByType(PLDM_FILE_TYPE_PEL, fileHandle); 764 auto pelType = dynamic_cast<PelHandler*>(handler.get()); 765 ASSERT_TRUE(pelType != nullptr); 766 767 handler = getHandlerByType(PLDM_FILE_TYPE_LID_PERM, fileHandle); 768 auto lidType = dynamic_cast<LidHandler*>(handler.get()); 769 ASSERT_TRUE(lidType != nullptr); 770 pelType = dynamic_cast<PelHandler*>(handler.get()); 771 ASSERT_TRUE(pelType == nullptr); 772 handler = getHandlerByType(PLDM_FILE_TYPE_LID_TEMP, fileHandle); 773 lidType = dynamic_cast<LidHandler*>(handler.get()); 774 ASSERT_TRUE(lidType != nullptr); 775 776 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 777 ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure); 778 } 779 780 TEST(readFileByTypeIntoMemory, testBadPath) 781 { 782 const auto hdr_size = sizeof(pldm_msg_hdr); 783 std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES> 784 requestMsg{}; 785 auto req = reinterpret_cast<pldm_msg*>(requestMsg.data()); 786 struct pldm_read_write_file_by_type_memory_req* request = 787 reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>( 788 req->payload); 789 request->file_type = 0xFFFF; 790 request->file_handle = 0; 791 request->offset = 0; 792 request->length = 17; 793 request->address = 0; 794 795 auto response = readFileByTypeIntoMemory(req, 0); 796 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 797 struct pldm_read_write_file_by_type_memory_resp* resp = 798 reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>( 799 responsePtr->payload); 800 ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code); 801 802 response = 803 readFileByTypeIntoMemory(req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES); 804 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 805 resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>( 806 responsePtr->payload); 807 ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code); 808 809 request->length = 16; 810 response = 811 readFileByTypeIntoMemory(req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES); 812 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 813 resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>( 814 responsePtr->payload); 815 ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code); 816 } 817 818 TEST(readFileByType, testBadPath) 819 { 820 const auto hdr_size = sizeof(pldm_msg_hdr); 821 std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_REQ_BYTES> requestMsg{}; 822 auto payloadLength = requestMsg.size() - hdr_size; 823 auto req = reinterpret_cast<pldm_msg*>(requestMsg.data()); 824 struct pldm_read_write_file_by_type_req* request = 825 reinterpret_cast<struct pldm_read_write_file_by_type_req*>( 826 req->payload); 827 request->file_type = 0xFFFF; 828 request->file_handle = 0; 829 request->offset = 0; 830 request->length = 13; 831 832 auto response = readFileByType(req, 0); 833 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 834 struct pldm_read_write_file_by_type_resp* resp = 835 reinterpret_cast<struct pldm_read_write_file_by_type_resp*>( 836 responsePtr->payload); 837 ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code); 838 839 response = readFileByType(req, payloadLength); 840 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 841 resp = reinterpret_cast<struct pldm_read_write_file_by_type_resp*>( 842 responsePtr->payload); 843 ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code); 844 } 845 846 TEST(readFileByType, testReadFile) 847 { 848 LidHandler handler(0, true); 849 Response response; 850 uint32_t length{}; 851 852 auto rc = handler.readFile({}, 0, length, response); 853 ASSERT_EQ(PLDM_INVALID_FILE_HANDLE, rc); 854 855 char tmplt[] = "/tmp/lid.XXXXXX"; 856 auto fd = mkstemp(tmplt); 857 std::vector<uint8_t> in = {100, 10, 56, 78, 34, 56, 79, 235, 111}; 858 write(fd, in.data(), in.size()); 859 close(fd); 860 length = in.size() + 1000; 861 rc = handler.readFile(tmplt, 0, length, response); 862 ASSERT_EQ(rc, PLDM_SUCCESS); 863 ASSERT_EQ(length, in.size()); 864 ASSERT_EQ(response.size(), in.size()); 865 ASSERT_EQ(std::equal(in.begin(), in.end(), response.begin()), true); 866 } 867