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