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