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