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