1 #include "common/bios_utils.hpp"
2 #include "common/test/mocked_utils.hpp"
3 #include "libpldmresponder/bios_config.hpp"
4 #include "libpldmresponder/bios_string_attribute.hpp"
5 #include "libpldmresponder/oem_handler.hpp"
6 #include "libpldmresponder/platform_config.hpp"
7 #include "mocked_bios.hpp"
8 
9 #include <nlohmann/json.hpp>
10 
11 #include <fstream>
12 #include <memory>
13 
14 #include <gmock/gmock.h>
15 #include <gtest/gtest.h>
16 
17 using namespace pldm::bios::utils;
18 using namespace pldm::responder::bios;
19 using namespace pldm::utils;
20 
21 using ::testing::_;
22 using ::testing::ElementsAreArray;
23 using ::testing::Return;
24 using ::testing::StrEq;
25 using ::testing::Throw;
26 
27 class TestSystemSpecificBIOSConfig : public ::testing::Test
28 {
29   public:
SetUpTestCase()30     static void SetUpTestCase() // will execute once at the beginning of all
31                                 // TestSystemSpecificBIOSConfig objects
32     {
33         char tmpdir[] = "/tmp/BIOSTables.XXXXXX";
34         tableDir = fs::path(mkdtemp(tmpdir));
35 
36         std::vector<fs::path> paths = {
37             "./bios_jsons/string_attrs.json",
38             "./bios_jsons/integer_attrs.json",
39             "./bios_jsons/enum_attrs.json",
40         };
41 
42         for (auto& path : paths)
43         {
44             std::ifstream file;
45             file.open(path);
46             auto j = Json::parse(file);
47             jsons.emplace_back(j);
48         }
49     }
50 
findJsonEntry(const std::string & name)51     std::optional<Json> findJsonEntry(const std::string& name)
52     {
53         for (auto& json : jsons)
54         {
55             auto entries = json.at("entries");
56             for (auto& entry : entries)
57             {
58                 auto n = entry.at("attribute_name").get<std::string>();
59                 if (n == name)
60                 {
61                     return entry;
62                 }
63             }
64         }
65         return std::nullopt;
66     }
67 
TearDownTestCase()68     static void TearDownTestCase() // will be executed once at th end of all
69                                    // TestSystemSpecificBIOSConfig objects
70     {
71         fs::remove_all(tableDir);
72     }
73 
74     static fs::path tableDir;
75     static std::vector<Json> jsons;
76 };
77 
78 fs::path TestSystemSpecificBIOSConfig::tableDir;
79 std::vector<Json> TestSystemSpecificBIOSConfig::jsons;
80 
81 class MockSystemConfig : public pldm::responder::platform_config::Handler
82 {
83   public:
MockSystemConfig()84     MockSystemConfig() {}
85     MOCK_METHOD(void, ibmCompatibleAddedCallback, (sdbusplus::message_t&), ());
86     MOCK_METHOD(std::optional<std::filesystem::path>, getPlatformName, ());
87 };
88 
TEST_F(TestSystemSpecificBIOSConfig,buildTablesTest)89 TEST_F(TestSystemSpecificBIOSConfig, buildTablesTest)
90 {
91     MockdBusHandler dbusHandler;
92     MockSystemConfig mockSystemConfig;
93     std::string biosFilePath("./");
94 
95     EXPECT_CALL(mockSystemConfig, getPlatformName())
96         .WillOnce(Return(std::filesystem::path("bios_jsons")));
97 
98     BIOSConfig biosConfig(biosFilePath.c_str(), tableDir.c_str(), &dbusHandler,
99                           0, 0, nullptr, nullptr, &mockSystemConfig, []() {});
100     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
101     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
102     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
103 
104     EXPECT_TRUE(stringTable);
105     EXPECT_TRUE(attrTable);
106     EXPECT_TRUE(attrValueTable);
107 
108     std::set<std::string> expectedStrings = {"HMCManagedState",
109                                              "On",
110                                              "Off",
111                                              "FWBootSide",
112                                              "Perm",
113                                              "Temp",
114                                              "InbandCodeUpdate",
115                                              "Allowed",
116                                              "Allowed",
117                                              "NotAllowed",
118                                              "CodeUpdatePolicy",
119                                              "Concurrent",
120                                              "Disruptive",
121                                              "VDD_AVSBUS_RAIL",
122                                              "SBE_IMAGE_MINIMUM_VALID_ECS",
123                                              "INTEGER_INVALID_CASE",
124                                              "str_example1",
125                                              "str_example2",
126                                              "str_example3"};
127     std::set<std::string> strings;
128     for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>(
129              stringTable->data(), stringTable->size()))
130     {
131         auto str = table::string::decodeString(entry);
132         strings.emplace(str);
133     }
134 
135     EXPECT_EQ(strings, expectedStrings);
136 
137     BIOSStringTable biosStringTable(*stringTable);
138 
139     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
140                                                           attrTable->size()))
141     {
142         auto header = table::attribute::decodeHeader(entry);
143         auto attrName = biosStringTable.findString(header.stringHandle);
144         auto jsonEntry = findJsonEntry(attrName);
145         EXPECT_TRUE(jsonEntry);
146         switch (header.attrType)
147         {
148             case PLDM_BIOS_STRING:
149             case PLDM_BIOS_STRING_READ_ONLY:
150             {
151                 auto stringField = table::attribute::decodeStringEntry(entry);
152                 auto stringType = BIOSStringAttribute::strTypeMap.at(
153                     jsonEntry->at("string_type").get<std::string>());
154                 EXPECT_EQ(stringField.stringType,
155                           static_cast<uint8_t>(stringType));
156 
157                 EXPECT_EQ(
158                     stringField.minLength,
159                     jsonEntry->at("minimum_string_length").get<uint16_t>());
160                 EXPECT_EQ(
161                     stringField.maxLength,
162                     jsonEntry->at("maximum_string_length").get<uint16_t>());
163                 EXPECT_EQ(stringField.defString,
164                           jsonEntry->at("default_string").get<std::string>());
165                 EXPECT_EQ(stringField.defLength,
166                           (stringField.defString).length());
167                 break;
168             }
169             case PLDM_BIOS_INTEGER:
170             case PLDM_BIOS_INTEGER_READ_ONLY:
171             {
172                 auto integerField = table::attribute::decodeIntegerEntry(entry);
173                 EXPECT_EQ(integerField.lowerBound,
174                           jsonEntry->at("lower_bound").get<uint64_t>());
175                 EXPECT_EQ(integerField.upperBound,
176                           jsonEntry->at("upper_bound").get<uint64_t>());
177                 EXPECT_EQ(integerField.scalarIncrement,
178                           jsonEntry->at("scalar_increment").get<uint32_t>());
179                 EXPECT_EQ(integerField.defaultValue,
180                           jsonEntry->at("default_value").get<uint64_t>());
181                 break;
182             }
183             case PLDM_BIOS_ENUMERATION:
184             case PLDM_BIOS_ENUMERATION_READ_ONLY:
185             {
186                 auto [pvHdls,
187                       defInds] = table::attribute::decodeEnumEntry(entry);
188                 auto possibleValues = jsonEntry->at("possible_values")
189                                           .get<std::vector<std::string>>();
190                 std::vector<std::string> strings;
191                 for (auto pv : pvHdls)
192                 {
193                     auto s = biosStringTable.findString(pv);
194                     strings.emplace_back(s);
195                 }
196                 EXPECT_EQ(strings, possibleValues);
197                 EXPECT_EQ(defInds.size(), 1);
198 
199                 auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
200                 auto defaultValues = jsonEntry->at("default_values")
201                                          .get<std::vector<std::string>>();
202                 EXPECT_EQ(defValue, defaultValues[0]);
203 
204                 break;
205             }
206             default:
207                 EXPECT_TRUE(false);
208                 break;
209         }
210     }
211 
212     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
213              attrValueTable->data(), attrValueTable->size()))
214     {
215         auto header = table::attribute_value::decodeHeader(entry);
216         auto attrEntry = table::attribute::findByHandle(*attrTable,
217                                                         header.attrHandle);
218         auto attrHeader = table::attribute::decodeHeader(attrEntry);
219         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
220         auto jsonEntry = findJsonEntry(attrName);
221         EXPECT_TRUE(jsonEntry);
222         switch (header.attrType)
223         {
224             case PLDM_BIOS_STRING:
225             case PLDM_BIOS_STRING_READ_ONLY:
226             {
227                 auto value = table::attribute_value::decodeStringEntry(entry);
228                 auto defValue =
229                     jsonEntry->at("default_string").get<std::string>();
230                 EXPECT_EQ(value, defValue);
231                 break;
232             }
233             case PLDM_BIOS_INTEGER:
234             case PLDM_BIOS_INTEGER_READ_ONLY:
235             {
236                 auto value = table::attribute_value::decodeIntegerEntry(entry);
237                 auto defValue = jsonEntry->at("default_value").get<uint64_t>();
238                 EXPECT_EQ(value, defValue);
239                 break;
240             }
241             case PLDM_BIOS_ENUMERATION:
242             case PLDM_BIOS_ENUMERATION_READ_ONLY:
243             {
244                 auto indices = table::attribute_value::decodeEnumEntry(entry);
245                 EXPECT_EQ(indices.size(), 1);
246                 auto possibleValues = jsonEntry->at("possible_values")
247                                           .get<std::vector<std::string>>();
248 
249                 auto defValues = jsonEntry->at("default_values")
250                                      .get<std::vector<std::string>>();
251                 EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
252                 break;
253             }
254             default:
255                 EXPECT_TRUE(false);
256                 break;
257         }
258     }
259 }
260 
TEST_F(TestSystemSpecificBIOSConfig,buildTablesSystemSpecificTest)261 TEST_F(TestSystemSpecificBIOSConfig, buildTablesSystemSpecificTest)
262 {
263     MockdBusHandler dbusHandler;
264     MockSystemConfig mockSystemConfig;
265 
266     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return(""));
267     ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _))
268         .WillByDefault(Throw(std::exception()));
269 
270     BIOSConfig biosConfig("./system_type1/bios_jsons", tableDir.c_str(),
271                           &dbusHandler, 0, 0, nullptr, nullptr,
272                           &mockSystemConfig, []() {});
273 
274     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
275     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
276     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
277 
278     EXPECT_TRUE(stringTable);
279     EXPECT_TRUE(attrTable);
280     EXPECT_TRUE(attrValueTable);
281 
282     BIOSStringTable biosStringTable(*stringTable);
283 
284     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
285                                                           attrTable->size()))
286     {
287         auto header = table::attribute::decodeHeader(entry);
288         auto attrName = biosStringTable.findString(header.stringHandle);
289         auto jsonEntry = findJsonEntry(attrName);
290         EXPECT_TRUE(jsonEntry);
291         switch (header.attrType)
292         {
293             case PLDM_BIOS_STRING:
294             case PLDM_BIOS_STRING_READ_ONLY:
295             {
296                 if (attrName == "str_example2")
297                 {
298                     auto stringField =
299                         table::attribute::decodeStringEntry(entry);
300                     EXPECT_EQ(stringField.maxLength, 200);
301                 }
302 
303                 break;
304             }
305             case PLDM_BIOS_INTEGER:
306             case PLDM_BIOS_INTEGER_READ_ONLY:
307             {
308                 if (attrName == "SBE_IMAGE_MINIMUM_VALID_ECS")
309                 {
310                     auto integerField =
311                         table::attribute::decodeIntegerEntry(entry);
312                     EXPECT_EQ(integerField.upperBound, 30);
313                 }
314                 break;
315             }
316             case PLDM_BIOS_ENUMERATION:
317             case PLDM_BIOS_ENUMERATION_READ_ONLY:
318             {
319                 if (attrName == "FWBootSide")
320                 {
321                     auto [pvHdls,
322                           defInds] = table::attribute::decodeEnumEntry(entry);
323                     auto defValue =
324                         biosStringTable.findString(pvHdls[defInds[0]]);
325                     EXPECT_EQ(defValue, "Temp");
326                 }
327             }
328         }
329     }
330 }
331 
TEST_F(TestSystemSpecificBIOSConfig,setBIOSTable)332 TEST_F(TestSystemSpecificBIOSConfig, setBIOSTable)
333 {
334     MockdBusHandler dbusHandler;
335     MockSystemConfig mockSystemConfig;
336 
337     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
338     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
339                           nullptr, &mockSystemConfig, []() {});
340 
341     std::set<std::string> strings{"pvm_system_name", "pvm_stop_at_standby",
342                                   "fw_boot_side", "fw_next_boot_side"};
343 
344     Table table;
345     for (const auto& elem : strings)
346     {
347         table::string::constructEntry(table, elem);
348     }
349 
350     table::appendPadAndChecksum(table);
351     auto rc = biosConfig.setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
352     EXPECT_EQ(rc, PLDM_SUCCESS);
353 
354     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
355     EXPECT_TRUE(stringTable);
356 }
357 
TEST_F(TestSystemSpecificBIOSConfig,getBIOSTableFailure)358 TEST_F(TestSystemSpecificBIOSConfig, getBIOSTableFailure)
359 {
360     MockdBusHandler dbusHandler;
361     MockSystemConfig mockSystemConfig;
362 
363     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
364     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
365                           nullptr, &mockSystemConfig, []() {});
366 
367     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
368     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
369     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
370 
371     EXPECT_FALSE(stringTable);
372     EXPECT_FALSE(attrTable);
373     EXPECT_FALSE(attrValueTable);
374 }
375 
TEST_F(TestSystemSpecificBIOSConfig,setAttrValueFailure)376 TEST_F(TestSystemSpecificBIOSConfig, setAttrValueFailure)
377 {
378     MockdBusHandler dbusHandler;
379     MockSystemConfig mockSystemConfig;
380 
381     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
382     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
383                           nullptr, &mockSystemConfig, []() {});
384 
385     std::vector<uint8_t> attrValueEntry{
386         0,   0,             /* attr handle */
387         1,                  /* attr type string read-write */
388         4,   0,             /* current string length */
389         'a', 'b', 'c', 'd', /* default value string handle index */
390     };
391 
392     uint16_t attrHandle{10};
393     attrValueEntry[0] = attrHandle & 0xff;
394     attrValueEntry[1] = (attrHandle >> 8) & 0xff;
395 
396     auto rc = biosConfig.setAttrValue(attrValueEntry.data(),
397                                       attrValueEntry.size(), false);
398     std::cout << "Error in setting Attribute " << rc << std::endl;
399     EXPECT_EQ(rc, PLDM_BIOS_TABLE_UNAVAILABLE);
400 }
401 
TEST_F(TestSystemSpecificBIOSConfig,setAttrValue)402 TEST_F(TestSystemSpecificBIOSConfig, setAttrValue)
403 {
404     MockdBusHandler dbusHandler;
405     MockSystemConfig mockSystemConfig;
406 
407     EXPECT_CALL(mockSystemConfig, getPlatformName())
408         .WillOnce(Return(std::filesystem::path("")));
409 
410     BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0,
411                           nullptr, nullptr, &mockSystemConfig, []() {});
412 
413     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
414     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
415 
416     BIOSStringTable biosStringTable(*stringTable);
417     BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(),
418                                                       attrTable->size());
419     auto stringHandle = biosStringTable.findHandle("str_example1");
420     uint16_t attrHandle{};
421 
422     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
423                                                           attrTable->size()))
424     {
425         auto header = table::attribute::decodeHeader(entry);
426         if (header.stringHandle == stringHandle)
427         {
428             attrHandle = header.attrHandle;
429             break;
430         }
431     }
432 
433     EXPECT_NE(attrHandle, 0);
434 
435     std::vector<uint8_t> attrValueEntry{
436         0,   0,             /* attr handle */
437         1,                  /* attr type string read-write */
438         4,   0,             /* current string length */
439         'a', 'b', 'c', 'd', /* default value string handle index */
440     };
441 
442     attrValueEntry[0] = attrHandle & 0xff;
443     attrValueEntry[1] = (attrHandle >> 8) & 0xff;
444 
445     DBusMapping dbusMapping{"/xyz/abc/def",
446                             "xyz.openbmc_project.str_example1.value",
447                             "Str_example1", "string"};
448     PropertyValue value = std::string("abcd");
449     EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1);
450 
451     auto rc = biosConfig.setAttrValue(attrValueEntry.data(),
452                                       attrValueEntry.size(), false);
453     EXPECT_EQ(rc, PLDM_SUCCESS);
454 
455     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
456     auto findEntry =
457         [&attrValueTable](
458             uint16_t handle) -> const pldm_bios_attr_val_table_entry* {
459         for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
460                  attrValueTable->data(), attrValueTable->size()))
461         {
462             auto [attrHandle, _] = table::attribute_value::decodeHeader(entry);
463             if (attrHandle == handle)
464                 return entry;
465         }
466         return nullptr;
467     };
468 
469     auto entry = findEntry(attrHandle);
470     EXPECT_NE(entry, nullptr);
471 
472     auto p = reinterpret_cast<const uint8_t*>(entry);
473     EXPECT_THAT(std::vector<uint8_t>(p, p + attrValueEntry.size()),
474                 ElementsAreArray(attrValueEntry));
475 }
476