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     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
226     oem_ibm::Handler handler(oemPlatformHandler.get());
227     auto response = handler.readFileIntoMemory(request, 0);
228     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
229     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
230 }
231 
232 TEST_F(TestFileTable, ReadFileInvalidFileHandle)
233 {
234     // Invalid file handle in the file table
235     uint32_t fileHandle = 2;
236     uint32_t offset = 0;
237     uint32_t length = 0;
238     uint64_t address = 0;
239 
240     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
241         requestMsg{};
242     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
243     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
244     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
245     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
246     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
247            sizeof(length));
248     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
249                sizeof(length),
250            &address, sizeof(address));
251 
252     using namespace pldm::filetable;
253     // Initialise the file table with 2 valid file handles 0 & 1.
254     auto& table = buildFileTable(fileTableConfig.c_str());
255 
256     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
257     oem_ibm::Handler handler(oemPlatformHandler.get());
258     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
259     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
260     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
261     // Clear the file table contents.
262     table.clear();
263 }
264 
265 TEST_F(TestFileTable, ReadFileInvalidOffset)
266 {
267     uint32_t fileHandle = 0;
268     // The file size is 1024, so the offset is invalid
269     uint32_t offset = 1024;
270     uint32_t length = 0;
271     uint64_t address = 0;
272 
273     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
274         requestMsg{};
275     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
276     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
277     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
278     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
279     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
280            sizeof(length));
281     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
282                sizeof(length),
283            &address, sizeof(address));
284 
285     using namespace pldm::filetable;
286     auto& table = buildFileTable(fileTableConfig.c_str());
287 
288     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
289     oem_ibm::Handler handler(oemPlatformHandler.get());
290     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
291     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
292     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
293     // Clear the file table contents.
294     table.clear();
295 }
296 
297 TEST_F(TestFileTable, ReadFileInvalidLength)
298 {
299     uint32_t fileHandle = 0;
300     uint32_t offset = 100;
301     // Length should be a multiple of dma min size(16)
302     uint32_t length = 10;
303     uint64_t address = 0;
304 
305     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
306         requestMsg{};
307     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
308     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
309     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
310     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
311     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
312            sizeof(length));
313     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
314                sizeof(length),
315            &address, sizeof(address));
316 
317     using namespace pldm::filetable;
318     auto& table = buildFileTable(fileTableConfig.c_str());
319 
320     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
321     oem_ibm::Handler handler(oemPlatformHandler.get());
322     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
323     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
324     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
325     // Clear the file table contents.
326     table.clear();
327 }
328 
329 TEST_F(TestFileTable, ReadFileInvalidEffectiveLength)
330 {
331     uint32_t fileHandle = 0;
332     // valid offset
333     uint32_t offset = 100;
334     // length + offset exceeds the size, so effective length is
335     // filesize(1024) - offset(100). The effective length is not a multiple of
336     // DMA min size(16)
337     uint32_t length = 1024;
338     uint64_t address = 0;
339 
340     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
341         requestMsg{};
342     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
343     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
344     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
345     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
346     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
347            sizeof(length));
348     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
349                sizeof(length),
350            &address, sizeof(address));
351 
352     using namespace pldm::filetable;
353     auto& table = buildFileTable(fileTableConfig.c_str());
354 
355     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
356     oem_ibm::Handler handler(oemPlatformHandler.get());
357     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
358     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
359     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
360     // Clear the file table contents.
361     table.clear();
362 }
363 
364 TEST(WriteFileFromMemory, BadPath)
365 {
366     uint32_t fileHandle = 0;
367     uint32_t offset = 0;
368     uint32_t length = 10;
369     uint64_t address = 0;
370 
371     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
372         requestMsg{};
373     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
374     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
375     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
376     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
377     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
378            sizeof(length));
379     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
380                sizeof(length),
381            &address, sizeof(address));
382 
383     // Pass invalid payload length
384     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
385     oem_ibm::Handler handler(oemPlatformHandler.get());
386     auto response = handler.writeFileFromMemory(request, 0);
387     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
388     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
389 
390     // The length field is not a multiple of DMA minsize
391     response = handler.writeFileFromMemory(request, requestPayloadLength);
392     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
393     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
394 }
395 
396 TEST_F(TestFileTable, WriteFileInvalidFileHandle)
397 {
398     // Invalid file handle in the file table
399     uint32_t fileHandle = 2;
400     uint32_t offset = 0;
401     uint32_t length = 16;
402     uint64_t address = 0;
403 
404     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
405         requestMsg{};
406     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
407     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
408     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
409     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
410     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
411            sizeof(length));
412     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
413                sizeof(length),
414            &address, sizeof(address));
415 
416     using namespace pldm::filetable;
417     // Initialise the file table with 2 valid file handles 0 & 1.
418     auto& table = buildFileTable(fileTableConfig.c_str());
419 
420     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
421     oem_ibm::Handler handler(oemPlatformHandler.get());
422     auto response = handler.writeFileFromMemory(request, requestPayloadLength);
423     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
424     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
425     // Clear the file table contents.
426     table.clear();
427 }
428 
429 TEST_F(TestFileTable, WriteFileInvalidOffset)
430 {
431     uint32_t fileHandle = 0;
432     // The file size is 1024, so the offset is invalid
433     uint32_t offset = 1024;
434     uint32_t length = 16;
435     uint64_t address = 0;
436 
437     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
438         requestMsg{};
439     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
440     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
441     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
442     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
443     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
444            sizeof(length));
445     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
446                sizeof(length),
447            &address, sizeof(address));
448 
449     using namespace pldm::filetable;
450     // Initialise the file table with 2 valid file handles 0 & 1.
451     auto& table = buildFileTable(TestFileTable::fileTableConfig.c_str());
452 
453     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
454     oem_ibm::Handler handler(oemPlatformHandler.get());
455     auto response = handler.writeFileFromMemory(request, requestPayloadLength);
456     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
457     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
458     // Clear the file table contents.
459     table.clear();
460 }
461 
462 TEST(FileTable, ConfigNotExist)
463 {
464     FileTable tableObj("");
465     EXPECT_EQ(tableObj.isEmpty(), true);
466 }
467 
468 TEST_F(TestFileTable, ValidateFileEntry)
469 {
470     FileTable tableObj(fileTableConfig.c_str());
471 
472     // Test file handle 0, the file size is 1K bytes.
473     auto value = tableObj.at(0);
474     ASSERT_EQ(value.handle, 0);
475     ASSERT_EQ(strcmp(value.fsPath.c_str(), imageFile.c_str()), 0);
476     ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value.fsPath)), 1024);
477     ASSERT_EQ(value.traits.value, 1);
478     ASSERT_EQ(true, fs::exists(value.fsPath));
479 
480     // Test file handle 1, the file size is 16 bytes
481     auto value1 = tableObj.at(1);
482     ASSERT_EQ(value1.handle, 1);
483     ASSERT_EQ(strcmp(value1.fsPath.c_str(), cksumFile.c_str()), 0);
484     ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value1.fsPath)), 16);
485     ASSERT_EQ(value1.traits.value, 4);
486     ASSERT_EQ(true, fs::exists(value1.fsPath));
487 
488     // Test invalid file handle
489     ASSERT_THROW(tableObj.at(2), std::out_of_range);
490 }
491 
492 TEST_F(TestFileTable, ValidateFileTable)
493 {
494     FileTable tableObj(fileTableConfig.c_str());
495 
496     // Validate file attribute table
497     auto table = tableObj();
498     ASSERT_EQ(true,
499               std::equal(attrTable.begin(), attrTable.end(), table.begin()));
500 }
501 
502 TEST_F(TestFileTable, GetFileTableCommand)
503 {
504     // Initialise the file table with a valid handle of 0 & 1
505     auto& table = buildFileTable(fileTableConfig.c_str());
506 
507     uint32_t transferHandle = 0;
508     uint8_t opFlag = 0;
509     uint8_t type = PLDM_FILE_ATTRIBUTE_TABLE;
510     uint32_t nextTransferHandle = 0;
511     uint8_t transferFlag = PLDM_START_AND_END;
512 
513     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
514         requestMsg{};
515     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
516     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
517     auto request = reinterpret_cast<pldm_get_file_table_req*>(
518         requestMsg.data() + sizeof(pldm_msg_hdr));
519     request->transfer_handle = transferHandle;
520     request->operation_flag = opFlag;
521     request->table_type = type;
522 
523     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
524     oem_ibm::Handler handler(oemPlatformHandler.get());
525     auto response = handler.getFileTable(requestMsgPtr, requestPayloadLength);
526     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
527     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
528     size_t offsetSize = sizeof(responsePtr->payload[0]);
529     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &nextTransferHandle,
530                         sizeof(nextTransferHandle)));
531     offsetSize += sizeof(nextTransferHandle);
532     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &transferFlag,
533                         sizeof(transferFlag)));
534     offsetSize += sizeof(transferFlag);
535     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, attrTable.data(),
536                         attrTable.size()));
537     table.clear();
538 }
539 
540 TEST_F(TestFileTable, GetFileTableCommandReqLengthMismatch)
541 {
542     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
543         requestMsg{};
544     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
545 
546     // Pass invalid command payload length
547     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
548     oem_ibm::Handler handler(oemPlatformHandler.get());
549     auto response = handler.getFileTable(request, 0);
550     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
551     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
552 }
553 
554 TEST_F(TestFileTable, GetFileTableCommandOEMAttrTable)
555 {
556     uint32_t transferHandle = 0;
557     uint8_t opFlag = 0;
558     uint8_t type = PLDM_OEM_FILE_ATTRIBUTE_TABLE;
559 
560     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
561         requestMsg{};
562     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
563     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
564     auto request = reinterpret_cast<pldm_get_file_table_req*>(
565         requestMsg.data() + sizeof(pldm_msg_hdr));
566     request->transfer_handle = transferHandle;
567     request->operation_flag = opFlag;
568     request->table_type = type;
569 
570     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
571     oem_ibm::Handler handler(oemPlatformHandler.get());
572     auto response = handler.getFileTable(requestMsgPtr, requestPayloadLength);
573     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
574     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_TABLE_TYPE);
575 }
576 
577 TEST_F(TestFileTable, ReadFileBadPath)
578 {
579     uint32_t fileHandle = 1;
580     uint32_t offset = 0;
581     uint32_t length = 0x4;
582 
583     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
584         requestMsg{};
585     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
586     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
587     auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
588                                                          sizeof(pldm_msg_hdr));
589 
590     request->file_handle = fileHandle;
591     request->offset = offset;
592     request->length = length;
593 
594     using namespace pldm::filetable;
595     // Initialise the file table with 2 valid file handles 0 & 1.
596     auto& table = buildFileTable(fileTableConfig.c_str());
597 
598     // Invalid payload length
599     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
600     oem_ibm::Handler handler(oemPlatformHandler.get());
601     auto response = handler.readFile(requestMsgPtr, 0);
602     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
603     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
604 
605     // Data out of range. File size is 1024, offset = 1024 is invalid.
606     request->offset = 1024;
607 
608     response = handler.readFile(requestMsgPtr, payload_length);
609     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
610     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
611 
612     // Invalid file handle
613     request->file_handle = 2;
614 
615     response = handler.readFile(requestMsgPtr, payload_length);
616     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
617     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
618 
619     table.clear();
620 }
621 
622 TEST_F(TestFileTable, ReadFileGoodPath)
623 {
624     uint32_t fileHandle = 0;
625     uint32_t offset = 0;
626     uint32_t length = 0x4;
627 
628     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
629         requestMsg{};
630     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
631     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
632     auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
633                                                          sizeof(pldm_msg_hdr));
634 
635     request->file_handle = fileHandle;
636     request->offset = offset;
637     request->length = length;
638 
639     using namespace pldm::filetable;
640     // Initialise the file table with 2 valid file handles 0 & 1.
641     auto& table = buildFileTable(fileTableConfig.c_str());
642     FileEntry value{};
643     value = table.at(fileHandle);
644 
645     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
646     stream.seekg(offset);
647     std::vector<char> buffer(length);
648     stream.read(buffer.data(), length);
649 
650     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
651     oem_ibm::Handler handler(oemPlatformHandler.get());
652     auto responseMsg = handler.readFile(requestMsgPtr, payload_length);
653     auto response = reinterpret_cast<pldm_read_file_resp*>(
654         responseMsg.data() + sizeof(pldm_msg_hdr));
655     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
656     ASSERT_EQ(response->length, length);
657     ASSERT_EQ(0, memcmp(response->file_data, buffer.data(), length));
658 
659     // Test condition offset + length > fileSize;
660     size_t fileSize = 1024;
661     request->offset = 1023;
662     request->length = 10;
663 
664     stream.seekg(request->offset);
665     buffer.resize(fileSize - request->offset);
666     stream.read(buffer.data(), (fileSize - request->offset));
667 
668     responseMsg = handler.readFile(requestMsgPtr, payload_length);
669     response = reinterpret_cast<pldm_read_file_resp*>(responseMsg.data() +
670                                                       sizeof(pldm_msg_hdr));
671     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
672     ASSERT_EQ(response->length, (fileSize - request->offset));
673     ASSERT_EQ(0, memcmp(response->file_data, buffer.data(),
674                         (fileSize - request->offset)));
675 
676     table.clear();
677 }
678 
679 TEST_F(TestFileTable, WriteFileBadPath)
680 {
681     uint32_t fileHandle = 0;
682     uint32_t offset = 0;
683     uint32_t length = 0x10;
684 
685     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
686                                     PLDM_WRITE_FILE_REQ_BYTES + length);
687     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
688     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
689     auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
690                                                           sizeof(pldm_msg_hdr));
691 
692     using namespace pldm::filetable;
693     // Initialise the file table with 2 valid file handles 0 & 1.
694     auto& table = buildFileTable(fileTableConfig.c_str());
695 
696     request->file_handle = fileHandle;
697     request->offset = offset;
698     request->length = length;
699 
700     // Invalid payload length
701     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
702     oem_ibm::Handler handler(oemPlatformHandler.get());
703     auto response = handler.writeFile(requestMsgPtr, 0);
704     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
705     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
706 
707     // Data out of range. File size is 1024, offset = 1024 is invalid.
708     request->offset = 1024;
709 
710     response = handler.writeFile(requestMsgPtr, payload_length);
711     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
712     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
713 
714     // Invalid file handle
715     request->file_handle = 2;
716 
717     response = handler.writeFile(requestMsgPtr, payload_length);
718     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
719     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
720 
721     table.clear();
722 }
723 
724 TEST_F(TestFileTable, WriteFileGoodPath)
725 {
726     uint32_t fileHandle = 1;
727     uint32_t offset = 0;
728     std::array<uint8_t, 4> fileData = {0x41, 0x42, 0x43, 0x44};
729     uint32_t length = fileData.size();
730 
731     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
732                                     PLDM_WRITE_FILE_REQ_BYTES + length);
733     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
734     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
735     auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
736                                                           sizeof(pldm_msg_hdr));
737 
738     using namespace pldm::filetable;
739     // Initialise the file table with 2 valid file handles 0 & 1.
740     auto& table = buildFileTable(fileTableConfig.c_str());
741     FileEntry value{};
742     value = table.at(fileHandle);
743 
744     request->file_handle = fileHandle;
745     request->offset = offset;
746     request->length = length;
747     memcpy(request->file_data, fileData.data(), fileData.size());
748 
749     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
750     oem_ibm::Handler handler(oemPlatformHandler.get());
751     auto responseMsg = handler.writeFile(requestMsgPtr, payload_length);
752     auto response = reinterpret_cast<pldm_read_file_resp*>(
753         responseMsg.data() + sizeof(pldm_msg_hdr));
754 
755     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
756     stream.seekg(offset);
757     std::vector<char> buffer(length);
758     stream.read(buffer.data(), length);
759 
760     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
761     ASSERT_EQ(response->length, length);
762     ASSERT_EQ(0, memcmp(fileData.data(), buffer.data(), length));
763 
764     table.clear();
765 }
766 
767 TEST(writeFileByTypeFromMemory, testBadPath)
768 {
769     const auto hdr_size = sizeof(pldm_msg_hdr);
770     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
771         requestMsg{};
772     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
773     size_t requestPayloadLength = requestMsg.size() - hdr_size;
774     struct pldm_read_write_file_by_type_memory_req* request =
775         reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
776             req->payload);
777     request->file_type = PLDM_FILE_TYPE_PEL;
778     request->file_handle = 0xFFFFFFFF;
779     request->offset = 0;
780     request->length = 17;
781     request->address = 0;
782 
783     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
784     oem_ibm::Handler handler(oemPlatformHandler.get());
785     auto response = handler.writeFileByTypeFromMemory(req, 0);
786     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
787 
788     struct pldm_read_write_file_by_type_memory_resp* resp =
789         reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
790             responsePtr->payload);
791     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
792 
793     response = handler.writeFileByTypeFromMemory(req, requestPayloadLength);
794     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
795 
796     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
797         responsePtr->payload);
798     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
799 }
800 
801 TEST(getHandlerByType, allPaths)
802 {
803     uint32_t fileHandle{};
804     auto handler = getHandlerByType(PLDM_FILE_TYPE_PEL, fileHandle);
805     auto pelType = dynamic_cast<PelHandler*>(handler.get());
806     ASSERT_TRUE(pelType != nullptr);
807 
808     handler = getHandlerByType(PLDM_FILE_TYPE_LID_PERM, fileHandle);
809     auto lidType = dynamic_cast<LidHandler*>(handler.get());
810     ASSERT_TRUE(lidType != nullptr);
811     pelType = dynamic_cast<PelHandler*>(handler.get());
812     ASSERT_TRUE(pelType == nullptr);
813     handler = getHandlerByType(PLDM_FILE_TYPE_LID_TEMP, fileHandle);
814     lidType = dynamic_cast<LidHandler*>(handler.get());
815     ASSERT_TRUE(lidType != nullptr);
816 
817     handler = getHandlerByType(PLDM_FILE_TYPE_DUMP, fileHandle);
818     auto dumpType = dynamic_cast<DumpHandler*>(handler.get());
819     ASSERT_TRUE(dumpType != nullptr);
820 
821     handler = getHandlerByType(PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS, fileHandle);
822     dumpType = dynamic_cast<DumpHandler*>(handler.get());
823     ASSERT_TRUE(dumpType != nullptr);
824 
825     handler = getHandlerByType(PLDM_FILE_TYPE_RESOURCE_DUMP, fileHandle);
826     dumpType = dynamic_cast<DumpHandler*>(handler.get());
827     ASSERT_TRUE(dumpType != nullptr);
828 
829     handler = getHandlerByType(PLDM_FILE_TYPE_CERT_SIGNING_REQUEST, fileHandle);
830     auto certType = dynamic_cast<CertHandler*>(handler.get());
831     ASSERT_TRUE(certType != nullptr);
832 
833     handler = getHandlerByType(PLDM_FILE_TYPE_SIGNED_CERT, fileHandle);
834     certType = dynamic_cast<CertHandler*>(handler.get());
835     ASSERT_TRUE(certType != nullptr);
836 
837     handler = getHandlerByType(PLDM_FILE_TYPE_ROOT_CERT, fileHandle);
838     certType = dynamic_cast<CertHandler*>(handler.get());
839     ASSERT_TRUE(certType != nullptr);
840 
841     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
842     ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure);
843 }
844 
845 TEST(readFileByTypeIntoMemory, testBadPath)
846 {
847     const auto hdr_size = sizeof(pldm_msg_hdr);
848     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
849         requestMsg{};
850     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
851     struct pldm_read_write_file_by_type_memory_req* request =
852         reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
853             req->payload);
854     request->file_type = 0xFFFF;
855     request->file_handle = 0;
856     request->offset = 0;
857     request->length = 17;
858     request->address = 0;
859 
860     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
861     oem_ibm::Handler handler(oemPlatformHandler.get());
862     auto response = handler.readFileByTypeIntoMemory(req, 0);
863     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
864     struct pldm_read_write_file_by_type_memory_resp* resp =
865         reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
866             responsePtr->payload);
867     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
868 
869     response = handler.readFileByTypeIntoMemory(
870         req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
871     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
872     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
873         responsePtr->payload);
874     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
875 
876     request->length = 16;
877     response = handler.readFileByTypeIntoMemory(
878         req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
879     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
880     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
881         responsePtr->payload);
882     ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
883 }
884 
885 TEST(readFileByType, testBadPath)
886 {
887     const auto hdr_size = sizeof(pldm_msg_hdr);
888     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_REQ_BYTES> requestMsg{};
889     auto payloadLength = requestMsg.size() - hdr_size;
890     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
891     struct pldm_read_write_file_by_type_req* request =
892         reinterpret_cast<struct pldm_read_write_file_by_type_req*>(
893             req->payload);
894     request->file_type = 0xFFFF;
895     request->file_handle = 0;
896     request->offset = 0;
897     request->length = 13;
898 
899     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
900     oem_ibm::Handler handler(oemPlatformHandler.get());
901     auto response = handler.readFileByType(req, 0);
902     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
903     struct pldm_read_write_file_by_type_resp* resp =
904         reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
905             responsePtr->payload);
906     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
907 
908     response = handler.readFileByType(req, payloadLength);
909     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
910     resp = reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
911         responsePtr->payload);
912     ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
913 }
914 
915 TEST(readFileByType, testReadFile)
916 {
917     LidHandler handler(0, true);
918     Response response;
919     uint32_t length{};
920 
921     auto rc = handler.readFile({}, 0, length, response);
922     ASSERT_EQ(PLDM_INVALID_FILE_HANDLE, rc);
923 
924     char tmplt[] = "/tmp/lid.XXXXXX";
925     auto fd = mkstemp(tmplt);
926     std::vector<uint8_t> in = {100, 10, 56, 78, 34, 56, 79, 235, 111};
927     write(fd, in.data(), in.size());
928     close(fd);
929     length = in.size() + 1000;
930     rc = handler.readFile(tmplt, 0, length, response);
931     ASSERT_EQ(rc, PLDM_SUCCESS);
932     ASSERT_EQ(length, in.size());
933     ASSERT_EQ(response.size(), in.size());
934     ASSERT_EQ(std::equal(in.begin(), in.end(), response.begin()), true);
935 }
936