1 #include "bios_config.hpp"
2 
3 #include "bios_enum_attribute.hpp"
4 #include "bios_integer_attribute.hpp"
5 #include "bios_string_attribute.hpp"
6 #include "bios_table.hpp"
7 
8 #include <fstream>
9 #include <iostream>
10 
11 namespace pldm
12 {
13 namespace responder
14 {
15 namespace bios
16 {
17 namespace
18 {
19 
20 constexpr auto enumJsonFile = "enum_attrs.json";
21 constexpr auto stringJsonFile = "string_attrs.json";
22 constexpr auto integerJsonFile = "integer_attrs.json";
23 
24 constexpr auto stringTableFile = "stringTable";
25 constexpr auto attrTableFile = "attributeTable";
26 constexpr auto attrValueTableFile = "attributeValueTable";
27 
28 } // namespace
29 
30 BIOSConfig::BIOSConfig(const char* jsonDir, const char* tableDir,
31                        DBusHandler* const dbusHandler) :
32     jsonDir(jsonDir),
33     tableDir(tableDir), dbusHandler(dbusHandler)
34 {
35     fs::create_directories(tableDir);
36     constructAttributes();
37 }
38 
39 void BIOSConfig::buildTables()
40 {
41     auto stringTable = buildAndStoreStringTable();
42     if (stringTable)
43     {
44         buildAndStoreAttrTables(*stringTable);
45     }
46 }
47 
48 std::optional<Table> BIOSConfig::getBIOSTable(pldm_bios_table_types tableType)
49 {
50     fs::path tablePath;
51     switch (tableType)
52     {
53         case PLDM_BIOS_STRING_TABLE:
54             tablePath = tableDir / stringTableFile;
55             break;
56         case PLDM_BIOS_ATTR_TABLE:
57             tablePath = tableDir / attrTableFile;
58             break;
59         case PLDM_BIOS_ATTR_VAL_TABLE:
60             tablePath = tableDir / attrValueTableFile;
61             break;
62     }
63     return loadTable(tablePath);
64 }
65 
66 void BIOSConfig::constructAttributes()
67 {
68     load(jsonDir / stringJsonFile, [this](const Json& entry) {
69         constructAttribute<BIOSStringAttribute>(entry);
70     });
71     load(jsonDir / integerJsonFile, [this](const Json& entry) {
72         constructAttribute<BIOSIntegerAttribute>(entry);
73     });
74     load(jsonDir / enumJsonFile, [this](const Json& entry) {
75         constructAttribute<BIOSEnumAttribute>(entry);
76     });
77 }
78 
79 void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable)
80 {
81     BIOSStringTable biosStringTable(stringTable);
82 
83     if (biosAttributes.empty())
84     {
85         return;
86     }
87 
88     Table attrTable, attrValueTable;
89 
90     for (auto& attr : biosAttributes)
91     {
92         try
93         {
94             attr->constructEntry(biosStringTable, attrTable, attrValueTable);
95         }
96         catch (const std::exception& e)
97         {
98             std::cerr << "Construct Table Entry Error, AttributeName = "
99                       << attr->name << std::endl;
100         }
101     }
102 
103     table::appendPadAndChecksum(attrTable);
104     table::appendPadAndChecksum(attrValueTable);
105 
106     storeTable(tableDir / attrTableFile, attrTable);
107     storeTable(tableDir / attrValueTableFile, attrValueTable);
108 }
109 
110 std::optional<Table> BIOSConfig::buildAndStoreStringTable()
111 {
112     std::set<std::string> strings;
113     auto handler = [&strings](const Json& entry) {
114         strings.emplace(entry.at("attribute_name"));
115     };
116 
117     load(jsonDir / stringJsonFile, handler);
118     load(jsonDir / integerJsonFile, handler);
119     load(jsonDir / enumJsonFile, [&strings](const Json& entry) {
120         strings.emplace(entry.at("attribute_name"));
121         auto possibleValues = entry.at("possible_values");
122         for (auto& pv : possibleValues)
123         {
124             strings.emplace(pv);
125         }
126     });
127 
128     if (strings.empty())
129     {
130         return std::nullopt;
131     }
132 
133     Table table;
134     for (const auto& elem : strings)
135     {
136         table::string::constructEntry(table, elem);
137     }
138 
139     table::appendPadAndChecksum(table);
140     storeTable(tableDir / stringTableFile, table);
141     return table;
142 }
143 
144 void BIOSConfig::storeTable(const fs::path& path, const Table& table)
145 {
146     BIOSTable biosTable(path.c_str());
147     biosTable.store(table);
148 }
149 
150 std::optional<Table> BIOSConfig::loadTable(const fs::path& path)
151 {
152     BIOSTable biosTable(path.c_str());
153     if (biosTable.isEmpty())
154     {
155         return std::nullopt;
156     }
157 
158     Table table;
159     biosTable.load(table);
160     return table;
161 }
162 
163 void BIOSConfig::load(const fs::path& filePath, ParseHandler handler)
164 {
165     std::ifstream file;
166     Json jsonConf;
167     if (fs::exists(filePath))
168     {
169         try
170         {
171             file.open(filePath);
172             jsonConf = Json::parse(file);
173             auto entries = jsonConf.at("entries");
174             for (auto& entry : entries)
175             {
176                 try
177                 {
178                     handler(entry);
179                 }
180                 catch (const std::exception& e)
181                 {
182                     std::cerr
183                         << "Failed to parse JSON config file(entry handler) : "
184                         << filePath.c_str() << ", " << e.what() << std::endl;
185                 }
186             }
187         }
188         catch (const std::exception& e)
189         {
190             std::cerr << "Failed to parse JSON config file : "
191                       << filePath.c_str() << std::endl;
192         }
193     }
194 }
195 
196 int BIOSConfig::checkAttrValueToUpdate(
197     const pldm_bios_attr_val_table_entry* attrValueEntry,
198     const pldm_bios_attr_table_entry* attrEntry, Table&)
199 
200 {
201     auto [attrHandle, attrType] =
202         table::attribute_value::decodeHeader(attrValueEntry);
203 
204     switch (attrType)
205     {
206         case PLDM_BIOS_ENUMERATION:
207         {
208             auto value =
209                 table::attribute_value::decodeEnumEntry(attrValueEntry);
210             auto [pvHdls, defIndex] =
211                 table::attribute::decodeEnumEntry(attrEntry);
212             assert(value.size() == 1);
213             if (value[0] >= pvHdls.size())
214             {
215                 std::cerr << "Enum: Illgeal index, Index = " << (int)value[0]
216                           << std::endl;
217                 return PLDM_ERROR_INVALID_DATA;
218             }
219 
220             return PLDM_SUCCESS;
221         }
222         case PLDM_BIOS_INTEGER:
223         {
224             auto value =
225                 table::attribute_value::decodeIntegerEntry(attrValueEntry);
226             auto [lower, upper, scalar, def] =
227                 table::attribute::decodeIntegerEntry(attrEntry);
228 
229             if (value < lower || value > upper)
230             {
231                 std::cerr << "Integer: out of bound, value = " << value
232                           << std::endl;
233                 return PLDM_ERROR_INVALID_DATA;
234             }
235             return PLDM_SUCCESS;
236         }
237         case PLDM_BIOS_STRING:
238         {
239             auto stringConf = table::attribute::decodeStringEntry(attrEntry);
240             auto value =
241                 table::attribute_value::decodeStringEntry(attrValueEntry);
242             if (value.size() < stringConf.minLength ||
243                 value.size() > stringConf.maxLength)
244             {
245                 std::cerr << "String: Length error, string = " << value
246                           << " length = " << value.size() << std::endl;
247                 return PLDM_ERROR_INVALID_LENGTH;
248             }
249             return PLDM_SUCCESS;
250         }
251         default:
252             std::cerr << "ReadOnly or Unspported type, type = " << attrType
253                       << std::endl;
254             return PLDM_ERROR;
255     };
256 }
257 
258 int BIOSConfig::setAttrValue(const void* entry, size_t size)
259 {
260     auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
261     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
262     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
263     if (!attrValueTable || !attrTable || !stringTable)
264     {
265         return PLDM_BIOS_TABLE_UNAVAILABLE;
266     }
267 
268     auto attrValueEntry =
269         reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry);
270 
271     auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry);
272 
273     auto attrEntry =
274         table::attribute::findByHandle(*attrTable, attrValHeader.attrHandle);
275     if (!attrEntry)
276     {
277         return PLDM_ERROR;
278     }
279 
280     auto rc = checkAttrValueToUpdate(attrValueEntry, attrEntry, *stringTable);
281     if (rc != PLDM_SUCCESS)
282     {
283         return rc;
284     }
285 
286     auto destTable =
287         table::attribute_value::updateTable(*attrValueTable, entry, size);
288 
289     if (!destTable)
290     {
291         return PLDM_ERROR;
292     }
293 
294     try
295     {
296         auto attrHeader = table::attribute::decodeHeader(attrEntry);
297 
298         BIOSStringTable biosStringTable(*stringTable);
299         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
300 
301         auto iter = std::find_if(
302             biosAttributes.begin(), biosAttributes.end(),
303             [&attrName](const auto& attr) { return attr->name == attrName; });
304 
305         if (iter == biosAttributes.end())
306         {
307             return PLDM_ERROR;
308         }
309         (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry, biosStringTable);
310     }
311     catch (const std::exception& e)
312     {
313         std::cerr << "Set attribute value error: " << e.what() << std::endl;
314         return PLDM_ERROR;
315     }
316 
317     BIOSTable biosAttrValueTable((tableDir / attrValueTableFile).c_str());
318     biosAttrValueTable.store(*destTable);
319     return PLDM_SUCCESS;
320 }
321 
322 void BIOSConfig::removeTables()
323 {
324     try
325     {
326         fs::remove(tableDir / stringTableFile);
327         fs::remove(tableDir / attrTableFile);
328         fs::remove(tableDir / attrValueTableFile);
329     }
330     catch (const std::exception& e)
331     {
332         std::cerr << "Remove the tables error: " << e.what() << std::endl;
333     }
334 }
335 
336 void BIOSConfig::processBiosAttrChangeNotification(
337     const DbusChObjProperties& chProperties, uint32_t biosAttrIndex)
338 {
339     const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap();
340     const auto& propertyName = dBusMap->propertyName;
341     const auto& attrName = biosAttributes[biosAttrIndex]->name;
342 
343     const auto it = chProperties.find(propertyName);
344     if (it == chProperties.end())
345     {
346         return;
347     }
348 
349     PropertyValue newPropVal = it->second;
350     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
351     if (!stringTable.has_value())
352     {
353         std::cerr << "BIOS string table unavailable\n";
354         return;
355     }
356     BIOSStringTable biosStringTable(*stringTable);
357     uint16_t attrNameHdl{};
358     try
359     {
360         attrNameHdl = biosStringTable.findHandle(attrName);
361     }
362     catch (std::invalid_argument& e)
363     {
364         std::cerr << "Could not find handle for BIOS string, ATTRIBUTE="
365                   << attrName.c_str() << "\n";
366         return;
367     }
368 
369     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
370     if (!attrTable.has_value())
371     {
372         std::cerr << "Attribute table not present\n";
373         return;
374     }
375     const struct pldm_bios_attr_table_entry* tableEntry =
376         table::attribute::findByStringHandle(*attrTable, attrNameHdl);
377     if (tableEntry == nullptr)
378     {
379         std::cerr << "Attribute not found in attribute table, name= "
380                   << attrName.c_str() << "name handle=" << attrNameHdl << "\n";
381         return;
382     }
383 
384     auto [attrHdl, attrType, stringHdl] =
385         table::attribute::decodeHeader(tableEntry);
386 
387     auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
388 
389     if (!attrValueSrcTable.has_value())
390     {
391         std::cerr << "Attribute value table not present\n";
392         return;
393     }
394 
395     Table newValue;
396     auto rc = biosAttributes[biosAttrIndex]->updateAttrVal(
397         newValue, attrHdl, attrType, newPropVal);
398     if (rc != PLDM_SUCCESS)
399     {
400         std::cerr << "Could not update the attribute value table for attribute "
401                      "handle="
402                   << attrHdl << " and type=" << (uint32_t)attrType << "\n";
403         return;
404     }
405     auto destTable = table::attribute_value::updateTable(
406         *attrValueSrcTable, newValue.data(), newValue.size());
407     if (destTable.has_value())
408     {
409         storeTable(tableDir / attrValueTableFile, *destTable);
410     }
411 }
412 
413 } // namespace bios
414 } // namespace responder
415 } // namespace pldm
416