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