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 "mocked_bios.hpp"
6 
7 #include <nlohmann/json.hpp>
8 
9 #include <fstream>
10 #include <memory>
11 
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14 
15 using namespace pldm::bios::utils;
16 using namespace pldm::responder::bios;
17 using namespace pldm::utils;
18 
19 using ::testing::_;
20 using ::testing::ElementsAreArray;
21 using ::testing::Throw;
22 
23 class TestBIOSConfig : public ::testing::Test
24 {
25   public:
26     static void SetUpTestCase() // will execute once at the begining of all
27                                 // TestBIOSConfig objects
28     {
29         char tmpdir[] = "/tmp/BIOSTables.XXXXXX";
30         tableDir = fs::path(mkdtemp(tmpdir));
31 
32         std::vector<fs::path> paths = {
33             "./bios_jsons/string_attrs.json",
34             "./bios_jsons/integer_attrs.json",
35             "./bios_jsons/enum_attrs.json",
36         };
37 
38         for (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 TEST_F(TestBIOSConfig, buildTablesTest)
78 {
79     MockdBusHandler dbusHandler;
80 
81     ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _))
82         .WillByDefault(Throw(std::exception()));
83 
84     BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0,
85                           nullptr, nullptr);
86     biosConfig.buildTables();
87 
88     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
89     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
90     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
91 
92     EXPECT_TRUE(stringTable);
93     EXPECT_TRUE(attrTable);
94     EXPECT_TRUE(attrValueTable);
95 
96     std::set<std::string> expectedStrings = {"HMCManagedState",
97                                              "On",
98                                              "Off",
99                                              "FWBootSide",
100                                              "Perm",
101                                              "Temp",
102                                              "InbandCodeUpdate",
103                                              "Allowed",
104                                              "NotAllowed",
105                                              "CodeUpdatePolicy",
106                                              "Concurrent",
107                                              "Disruptive",
108                                              "VDD_AVSBUS_RAIL",
109                                              "SBE_IMAGE_MINIMUM_VALID_ECS",
110                                              "INTEGER_INVALID_CASE",
111                                              "str_example1",
112                                              "str_example2",
113                                              "str_example3"};
114     std::set<std::string> strings;
115     for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>(
116              stringTable->data(), stringTable->size()))
117     {
118         auto str = table::string::decodeString(entry);
119         strings.emplace(str);
120     }
121 
122     EXPECT_EQ(strings, expectedStrings);
123 
124     BIOSStringTable biosStringTable(*stringTable);
125 
126     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
127                                                           attrTable->size()))
128     {
129         auto header = table::attribute::decodeHeader(entry);
130         auto attrName = biosStringTable.findString(header.stringHandle);
131         auto jsonEntry = findJsonEntry(attrName);
132         EXPECT_TRUE(jsonEntry);
133         switch (header.attrType)
134         {
135             case PLDM_BIOS_STRING:
136             case PLDM_BIOS_STRING_READ_ONLY:
137             {
138                 auto stringField = table::attribute::decodeStringEntry(entry);
139                 auto stringType = BIOSStringAttribute::strTypeMap.at(
140                     jsonEntry->at("string_type").get<std::string>());
141                 EXPECT_EQ(stringField.stringType,
142                           static_cast<uint8_t>(stringType));
143 
144                 EXPECT_EQ(
145                     stringField.minLength,
146                     jsonEntry->at("minimum_string_length").get<uint16_t>());
147                 EXPECT_EQ(
148                     stringField.maxLength,
149                     jsonEntry->at("maximum_string_length").get<uint16_t>());
150                 EXPECT_EQ(
151                     stringField.defLength,
152                     jsonEntry->at("default_string_length").get<uint16_t>());
153                 EXPECT_EQ(stringField.defString,
154                           jsonEntry->at("default_string").get<std::string>());
155                 break;
156             }
157             case PLDM_BIOS_INTEGER:
158             case PLDM_BIOS_INTEGER_READ_ONLY:
159             {
160                 auto integerField = table::attribute::decodeIntegerEntry(entry);
161                 EXPECT_EQ(integerField.lowerBound,
162                           jsonEntry->at("lower_bound").get<uint64_t>());
163                 EXPECT_EQ(integerField.upperBound,
164                           jsonEntry->at("upper_bound").get<uint64_t>());
165                 EXPECT_EQ(integerField.scalarIncrement,
166                           jsonEntry->at("scalar_increment").get<uint32_t>());
167                 EXPECT_EQ(integerField.defaultValue,
168                           jsonEntry->at("default_value").get<uint64_t>());
169                 break;
170             }
171             case PLDM_BIOS_ENUMERATION:
172             case PLDM_BIOS_ENUMERATION_READ_ONLY:
173             {
174                 auto [pvHdls, defInds] =
175                     table::attribute::decodeEnumEntry(entry);
176                 auto possibleValues = jsonEntry->at("possible_values")
177                                           .get<std::vector<std::string>>();
178                 std::vector<std::string> strings;
179                 for (auto pv : pvHdls)
180                 {
181                     auto s = biosStringTable.findString(pv);
182                     strings.emplace_back(s);
183                 }
184                 EXPECT_EQ(strings, possibleValues);
185                 EXPECT_EQ(defInds.size(), 1);
186 
187                 auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
188                 auto defaultValues = jsonEntry->at("default_values")
189                                          .get<std::vector<std::string>>();
190                 EXPECT_EQ(defValue, defaultValues[0]);
191 
192                 break;
193             }
194             default:
195                 EXPECT_TRUE(false);
196                 break;
197         }
198     }
199 
200     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
201              attrValueTable->data(), attrValueTable->size()))
202     {
203         auto header = table::attribute_value::decodeHeader(entry);
204         auto attrEntry =
205             table::attribute::findByHandle(*attrTable, header.attrHandle);
206         auto attrHeader = table::attribute::decodeHeader(attrEntry);
207         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
208         auto jsonEntry = findJsonEntry(attrName);
209         EXPECT_TRUE(jsonEntry);
210         switch (header.attrType)
211         {
212             case PLDM_BIOS_STRING:
213             case PLDM_BIOS_STRING_READ_ONLY:
214             {
215                 auto value = table::attribute_value::decodeStringEntry(entry);
216                 auto defValue =
217                     jsonEntry->at("default_string").get<std::string>();
218                 EXPECT_EQ(value, defValue);
219                 break;
220             }
221             case PLDM_BIOS_INTEGER:
222             case PLDM_BIOS_INTEGER_READ_ONLY:
223             {
224                 auto value = table::attribute_value::decodeIntegerEntry(entry);
225                 auto defValue = jsonEntry->at("default_value").get<uint64_t>();
226                 EXPECT_EQ(value, defValue);
227                 break;
228             }
229             case PLDM_BIOS_ENUMERATION:
230             case PLDM_BIOS_ENUMERATION_READ_ONLY:
231             {
232                 auto indices = table::attribute_value::decodeEnumEntry(entry);
233                 EXPECT_EQ(indices.size(), 1);
234                 auto possibleValues = jsonEntry->at("possible_values")
235                                           .get<std::vector<std::string>>();
236 
237                 auto defValues = jsonEntry->at("default_values")
238                                      .get<std::vector<std::string>>();
239                 EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
240                 break;
241             }
242             default:
243                 EXPECT_TRUE(false);
244                 break;
245         }
246     }
247 }
248 
249 TEST_F(TestBIOSConfig, setAttrValue)
250 {
251     MockdBusHandler dbusHandler;
252 
253     BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0,
254                           nullptr, nullptr);
255     biosConfig.removeTables();
256     biosConfig.buildTables();
257 
258     auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
259     auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
260 
261     BIOSStringTable biosStringTable(*stringTable);
262     BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(),
263                                                       attrTable->size());
264     auto stringHandle = biosStringTable.findHandle("str_example1");
265     uint16_t attrHandle{};
266 
267     for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
268                                                           attrTable->size()))
269     {
270         auto header = table::attribute::decodeHeader(entry);
271         if (header.stringHandle == stringHandle)
272         {
273             attrHandle = header.attrHandle;
274             break;
275         }
276     }
277 
278     EXPECT_NE(attrHandle, 0);
279 
280     std::vector<uint8_t> attrValueEntry{
281         0,   0,             /* attr handle */
282         1,                  /* attr type string read-write */
283         4,   0,             /* current string length */
284         'a', 'b', 'c', 'd', /* defaut value string handle index */
285     };
286 
287     attrValueEntry[0] = attrHandle & 0xff;
288     attrValueEntry[1] = (attrHandle >> 8) & 0xff;
289 
290     DBusMapping dbusMapping{"/xyz/abc/def",
291                             "xyz.openbmc_project.str_example1.value",
292                             "Str_example1", "string"};
293     PropertyValue value = std::string("abcd");
294     EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1);
295 
296     auto rc =
297         biosConfig.setAttrValue(attrValueEntry.data(), attrValueEntry.size());
298     EXPECT_EQ(rc, PLDM_SUCCESS);
299 
300     auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
301     auto findEntry =
302         [&attrValueTable](
303             uint16_t handle) -> const pldm_bios_attr_val_table_entry* {
304         for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
305                  attrValueTable->data(), attrValueTable->size()))
306         {
307             auto [attrHandle, _] = table::attribute_value::decodeHeader(entry);
308             if (attrHandle == handle)
309                 return entry;
310         }
311         return nullptr;
312     };
313 
314     auto entry = findEntry(attrHandle);
315     EXPECT_NE(entry, nullptr);
316 
317     auto p = reinterpret_cast<const uint8_t*>(entry);
318     EXPECT_THAT(std::vector<uint8_t>(p, p + attrValueEntry.size()),
319                 ElementsAreArray(attrValueEntry));
320 }
321