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