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:
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 
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 
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:
84     MockSystemConfig() {}
85     MOCK_METHOD(void, ibmCompatibleAddedCallback, (sdbusplus::message_t&), ());
86     MOCK_METHOD(std::optional<std::filesystem::path>, getPlatformName, ());
87 };
88 
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 = {
109         "HMCManagedState",
110         "On",
111         "Off",
112         "FWBootSide",
113         "Perm",
114         "Temp",
115         "InbandCodeUpdate",
116         "Allowed",
117         "Allowed",
118         "NotAllowed",
119         "CodeUpdatePolicy",
120         "Concurrent",
121         "Disruptive",
122         "VDD_AVSBUS_RAIL",
123         "SBE_IMAGE_MINIMUM_VALID_ECS",
124         "INTEGER_INVALID_CASE",
125         "str_example1",
126         "str_example2",
127         "str_example3"};
128     std::set<std::string> strings;
129     for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>(
130              stringTable->data(), stringTable->size()))
131     {
132         auto str = table::string::decodeString(entry);
133         strings.emplace(str);
134     }
135 
136     EXPECT_EQ(strings, expectedStrings);
137 
138     BIOSStringTable biosStringTable(*stringTable);
139 
140     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
141                                                           attrTable->size()))
142     {
143         auto header = table::attribute::decodeHeader(entry);
144         auto attrName = biosStringTable.findString(header.stringHandle);
145         auto jsonEntry = findJsonEntry(attrName);
146         EXPECT_TRUE(jsonEntry);
147         switch (header.attrType)
148         {
149             case PLDM_BIOS_STRING:
150             case PLDM_BIOS_STRING_READ_ONLY:
151             {
152                 auto stringField = table::attribute::decodeStringEntry(entry);
153                 auto stringType = BIOSStringAttribute::strTypeMap.at(
154                     jsonEntry->at("string_type").get<std::string>());
155                 EXPECT_EQ(stringField.stringType,
156                           static_cast<uint8_t>(stringType));
157 
158                 EXPECT_EQ(
159                     stringField.minLength,
160                     jsonEntry->at("minimum_string_length").get<uint16_t>());
161                 EXPECT_EQ(
162                     stringField.maxLength,
163                     jsonEntry->at("maximum_string_length").get<uint16_t>());
164                 EXPECT_EQ(stringField.defString,
165                           jsonEntry->at("default_string").get<std::string>());
166                 EXPECT_EQ(stringField.defLength,
167                           (stringField.defString).length());
168                 break;
169             }
170             case PLDM_BIOS_INTEGER:
171             case PLDM_BIOS_INTEGER_READ_ONLY:
172             {
173                 auto integerField = table::attribute::decodeIntegerEntry(entry);
174                 EXPECT_EQ(integerField.lowerBound,
175                           jsonEntry->at("lower_bound").get<uint64_t>());
176                 EXPECT_EQ(integerField.upperBound,
177                           jsonEntry->at("upper_bound").get<uint64_t>());
178                 EXPECT_EQ(integerField.scalarIncrement,
179                           jsonEntry->at("scalar_increment").get<uint32_t>());
180                 EXPECT_EQ(integerField.defaultValue,
181                           jsonEntry->at("default_value").get<uint64_t>());
182                 break;
183             }
184             case PLDM_BIOS_ENUMERATION:
185             case PLDM_BIOS_ENUMERATION_READ_ONLY:
186             {
187                 auto [pvHdls,
188                       defInds] = table::attribute::decodeEnumEntry(entry);
189                 auto possibleValues = jsonEntry->at("possible_values")
190                                           .get<std::vector<std::string>>();
191                 std::vector<std::string> strings;
192                 for (auto pv : pvHdls)
193                 {
194                     auto s = biosStringTable.findString(pv);
195                     strings.emplace_back(s);
196                 }
197                 EXPECT_EQ(strings, possibleValues);
198                 EXPECT_EQ(defInds.size(), 1);
199 
200                 auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
201                 auto defaultValues = jsonEntry->at("default_values")
202                                          .get<std::vector<std::string>>();
203                 EXPECT_EQ(defValue, defaultValues[0]);
204 
205                 break;
206             }
207             default:
208                 EXPECT_TRUE(false);
209                 break;
210         }
211     }
212 
213     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
214              attrValueTable->data(), attrValueTable->size()))
215     {
216         auto header = table::attribute_value::decodeHeader(entry);
217         auto attrEntry =
218             table::attribute::findByHandle(*attrTable, header.attrHandle);
219         auto attrHeader = table::attribute::decodeHeader(attrEntry);
220         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
221         auto jsonEntry = findJsonEntry(attrName);
222         EXPECT_TRUE(jsonEntry);
223         switch (header.attrType)
224         {
225             case PLDM_BIOS_STRING:
226             case PLDM_BIOS_STRING_READ_ONLY:
227             {
228                 auto value = table::attribute_value::decodeStringEntry(entry);
229                 auto defValue =
230                     jsonEntry->at("default_string").get<std::string>();
231                 EXPECT_EQ(value, defValue);
232                 break;
233             }
234             case PLDM_BIOS_INTEGER:
235             case PLDM_BIOS_INTEGER_READ_ONLY:
236             {
237                 auto value = table::attribute_value::decodeIntegerEntry(entry);
238                 auto defValue = jsonEntry->at("default_value").get<uint64_t>();
239                 EXPECT_EQ(value, defValue);
240                 break;
241             }
242             case PLDM_BIOS_ENUMERATION:
243             case PLDM_BIOS_ENUMERATION_READ_ONLY:
244             {
245                 auto indices = table::attribute_value::decodeEnumEntry(entry);
246                 EXPECT_EQ(indices.size(), 1);
247                 auto possibleValues = jsonEntry->at("possible_values")
248                                           .get<std::vector<std::string>>();
249 
250                 auto defValues = jsonEntry->at("default_values")
251                                      .get<std::vector<std::string>>();
252                 EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
253                 break;
254             }
255             default:
256                 EXPECT_TRUE(false);
257                 break;
258         }
259     }
260 }
261 
262 TEST_F(TestSystemSpecificBIOSConfig, buildTablesSystemSpecificTest)
263 {
264     MockdBusHandler dbusHandler;
265     MockSystemConfig mockSystemConfig;
266 
267     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return(""));
268     ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _))
269         .WillByDefault(Throw(std::exception()));
270 
271     BIOSConfig biosConfig("./system_type1/bios_jsons", tableDir.c_str(),
272                           &dbusHandler, 0, 0, nullptr, nullptr,
273                           &mockSystemConfig, []() {});
274 
275     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
276     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
277     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
278 
279     EXPECT_TRUE(stringTable);
280     EXPECT_TRUE(attrTable);
281     EXPECT_TRUE(attrValueTable);
282 
283     BIOSStringTable biosStringTable(*stringTable);
284 
285     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
286                                                           attrTable->size()))
287     {
288         auto header = table::attribute::decodeHeader(entry);
289         auto attrName = biosStringTable.findString(header.stringHandle);
290         auto jsonEntry = findJsonEntry(attrName);
291         EXPECT_TRUE(jsonEntry);
292         switch (header.attrType)
293         {
294             case PLDM_BIOS_STRING:
295             case PLDM_BIOS_STRING_READ_ONLY:
296             {
297                 if (attrName == "str_example2")
298                 {
299                     auto stringField =
300                         table::attribute::decodeStringEntry(entry);
301                     EXPECT_EQ(stringField.maxLength, 200);
302                 }
303 
304                 break;
305             }
306             case PLDM_BIOS_INTEGER:
307             case PLDM_BIOS_INTEGER_READ_ONLY:
308             {
309                 if (attrName == "SBE_IMAGE_MINIMUM_VALID_ECS")
310                 {
311                     auto integerField =
312                         table::attribute::decodeIntegerEntry(entry);
313                     EXPECT_EQ(integerField.upperBound, 30);
314                 }
315                 break;
316             }
317             case PLDM_BIOS_ENUMERATION:
318             case PLDM_BIOS_ENUMERATION_READ_ONLY:
319             {
320                 if (attrName == "FWBootSide")
321                 {
322                     auto [pvHdls,
323                           defInds] = table::attribute::decodeEnumEntry(entry);
324                     auto defValue =
325                         biosStringTable.findString(pvHdls[defInds[0]]);
326                     EXPECT_EQ(defValue, "Temp");
327                 }
328             }
329         }
330     }
331 }
332 
333 TEST_F(TestSystemSpecificBIOSConfig, setBIOSTable)
334 {
335     MockdBusHandler dbusHandler;
336     MockSystemConfig mockSystemConfig;
337 
338     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
339     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
340                           nullptr, &mockSystemConfig, []() {});
341 
342     std::set<std::string> strings{"pvm_system_name", "pvm_stop_at_standby",
343                                   "fw_boot_side", "fw_next_boot_side"};
344 
345     Table table;
346     for (const auto& elem : strings)
347     {
348         table::string::constructEntry(table, elem);
349     }
350 
351     table::appendPadAndChecksum(table);
352     auto rc = biosConfig.setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
353     EXPECT_EQ(rc, PLDM_SUCCESS);
354 
355     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
356     EXPECT_TRUE(stringTable);
357 }
358 
359 TEST_F(TestSystemSpecificBIOSConfig, getBIOSTableFailure)
360 {
361     MockdBusHandler dbusHandler;
362     MockSystemConfig mockSystemConfig;
363 
364     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
365     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
366                           nullptr, &mockSystemConfig, []() {});
367 
368     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
369     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
370     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
371 
372     EXPECT_FALSE(stringTable);
373     EXPECT_FALSE(attrTable);
374     EXPECT_FALSE(attrValueTable);
375 }
376 
377 TEST_F(TestSystemSpecificBIOSConfig, setAttrValueFailure)
378 {
379     MockdBusHandler dbusHandler;
380     MockSystemConfig mockSystemConfig;
381 
382     EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
383     BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
384                           nullptr, &mockSystemConfig, []() {});
385 
386     std::vector<uint8_t> attrValueEntry{
387         0,   0,             /* attr handle */
388         1,                  /* attr type string read-write */
389         4,   0,             /* current string length */
390         'a', 'b', 'c', 'd', /* default value string handle index */
391     };
392 
393     uint16_t attrHandle{10};
394     attrValueEntry[0] = attrHandle & 0xff;
395     attrValueEntry[1] = (attrHandle >> 8) & 0xff;
396 
397     auto rc = biosConfig.setAttrValue(attrValueEntry.data(),
398                                       attrValueEntry.size(), false);
399     std::cout << "Error in setting Attribute " << rc << std::endl;
400     EXPECT_EQ(rc, PLDM_BIOS_TABLE_UNAVAILABLE);
401 }
402 
403 TEST_F(TestSystemSpecificBIOSConfig, setAttrValue)
404 {
405     MockdBusHandler dbusHandler;
406     MockSystemConfig mockSystemConfig;
407 
408     EXPECT_CALL(mockSystemConfig, getPlatformName())
409         .WillOnce(Return(std::filesystem::path("")));
410 
411     BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0,
412                           nullptr, nullptr, &mockSystemConfig, []() {});
413 
414     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
415     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
416 
417     BIOSStringTable biosStringTable(*stringTable);
418     BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(),
419                                                       attrTable->size());
420     auto stringHandle = biosStringTable.findHandle("str_example1");
421     uint16_t attrHandle{};
422 
423     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
424                                                           attrTable->size()))
425     {
426         auto header = table::attribute::decodeHeader(entry);
427         if (header.stringHandle == stringHandle)
428         {
429             attrHandle = header.attrHandle;
430             break;
431         }
432     }
433 
434     EXPECT_NE(attrHandle, 0);
435 
436     std::vector<uint8_t> attrValueEntry{
437         0,   0,             /* attr handle */
438         1,                  /* attr type string read-write */
439         4,   0,             /* current string length */
440         'a', 'b', 'c', 'd', /* default value string handle index */
441     };
442 
443     attrValueEntry[0] = attrHandle & 0xff;
444     attrValueEntry[1] = (attrHandle >> 8) & 0xff;
445 
446     DBusMapping dbusMapping{"/xyz/abc/def",
447                             "xyz.openbmc_project.str_example1.value",
448                             "Str_example1", "string"};
449     PropertyValue value = std::string("abcd");
450     EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1);
451 
452     auto rc = biosConfig.setAttrValue(attrValueEntry.data(),
453                                       attrValueEntry.size(), false);
454     EXPECT_EQ(rc, PLDM_SUCCESS);
455 
456     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
457     auto findEntry = [&attrValueTable](uint16_t handle)
458         -> 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