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