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 #include "common/bios_utils.hpp"
8 
9 #include <phosphor-logging/lg2.hpp>
10 #include <xyz/openbmc_project/BIOSConfig/Manager/server.hpp>
11 
12 #include <filesystem>
13 #include <fstream>
14 
15 #ifdef OEM_IBM
16 #include "oem/ibm/libpldmresponder/platform_oem_ibm.hpp"
17 #endif
18 
19 PHOSPHOR_LOG2_USING;
20 
21 using namespace pldm::utils;
22 
23 namespace pldm
24 {
25 namespace responder
26 {
27 namespace bios
28 {
29 namespace
30 {
31 using BIOSConfigManager =
32     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager;
33 
34 constexpr auto enumJsonFile = "enum_attrs.json";
35 constexpr auto stringJsonFile = "string_attrs.json";
36 constexpr auto integerJsonFile = "integer_attrs.json";
37 
38 constexpr auto stringTableFile = "stringTable";
39 constexpr auto attrTableFile = "attributeTable";
40 constexpr auto attrValueTableFile = "attributeValueTable";
41 
42 } // namespace
43 
44 BIOSConfig::BIOSConfig(
45     const char* jsonDir, const char* tableDir, DBusHandler* const dbusHandler,
46     int fd, uint8_t eid, pldm::InstanceIdDb* instanceIdDb,
47     pldm::requester::Handler<pldm::requester::Request>* handler,
48     pldm::responder::platform_config::Handler* platformConfigHandler,
49     pldm::responder::bios::Callback requestPLDMServiceName) :
50     jsonDir(jsonDir),
51     tableDir(tableDir), dbusHandler(dbusHandler), fd(fd), eid(eid),
52     instanceIdDb(instanceIdDb), handler(handler),
53     platformConfigHandler(platformConfigHandler),
54     requestPLDMServiceName(requestPLDMServiceName)
55 {
56     fs::create_directories(tableDir);
57     removeTables();
58     if (isSystemTypeAvailable())
59     {
60         initBIOSAttributes(sysType);
61     }
62     listenPendingAttributes();
63 }
64 
65 bool BIOSConfig::isSystemTypeAvailable()
66 {
67     if (platformConfigHandler)
68     {
69         auto systemType = platformConfigHandler->getPlatformName();
70         if (systemType.has_value())
71         {
72             sysType = systemType.value();
73         }
74         else
75         {
76             platformConfigHandler->registerSystemTypeCallback(std::bind(
77                 &BIOSConfig::initBIOSAttributes, this, std::placeholders::_1));
78             return false;
79         }
80     }
81     return true;
82 }
83 
84 void BIOSConfig::initBIOSAttributes(const std::string& systemType)
85 {
86     sysType = systemType;
87     fs::path dir{jsonDir / sysType};
88     if (!fs::exists(dir))
89     {
90         error("System specific bios attribute directory {DIR} does not exit",
91               "DIR", dir.string());
92         return;
93     }
94     constructAttributes();
95     buildTables();
96     requestPLDMServiceName();
97 }
98 
99 void BIOSConfig::buildTables()
100 {
101     auto stringTable = buildAndStoreStringTable();
102     if (stringTable)
103     {
104         buildAndStoreAttrTables(*stringTable);
105     }
106 }
107 
108 std::optional<Table> BIOSConfig::getBIOSTable(pldm_bios_table_types tableType)
109 {
110     fs::path tablePath;
111     switch (tableType)
112     {
113         case PLDM_BIOS_STRING_TABLE:
114             tablePath = tableDir / stringTableFile;
115             break;
116         case PLDM_BIOS_ATTR_TABLE:
117             tablePath = tableDir / attrTableFile;
118             break;
119         case PLDM_BIOS_ATTR_VAL_TABLE:
120             tablePath = tableDir / attrValueTableFile;
121             break;
122     }
123     return loadTable(tablePath);
124 }
125 
126 int BIOSConfig::setBIOSTable(uint8_t tableType, const Table& table,
127                              bool updateBaseBIOSTable)
128 {
129     fs::path stringTablePath(tableDir / stringTableFile);
130     fs::path attrTablePath(tableDir / attrTableFile);
131     fs::path attrValueTablePath(tableDir / attrValueTableFile);
132 
133     if (!pldm_bios_table_checksum(table.data(), table.size()))
134     {
135         return PLDM_INVALID_BIOS_TABLE_DATA_INTEGRITY_CHECK;
136     }
137 
138     if (tableType == PLDM_BIOS_STRING_TABLE)
139     {
140         storeTable(stringTablePath, table);
141     }
142     else if (tableType == PLDM_BIOS_ATTR_TABLE)
143     {
144         BIOSTable biosStringTable(stringTablePath.c_str());
145         if (biosStringTable.isEmpty())
146         {
147             return PLDM_INVALID_BIOS_TABLE_TYPE;
148         }
149 
150         auto rc = checkAttributeTable(table);
151         if (rc != PLDM_SUCCESS)
152         {
153             return rc;
154         }
155 
156         storeTable(attrTablePath, table);
157     }
158     else if (tableType == PLDM_BIOS_ATTR_VAL_TABLE)
159     {
160         BIOSTable biosStringTable(stringTablePath.c_str());
161         BIOSTable biosStringValueTable(attrTablePath.c_str());
162         if (biosStringTable.isEmpty() || biosStringValueTable.isEmpty())
163         {
164             return PLDM_INVALID_BIOS_TABLE_TYPE;
165         }
166 
167         auto rc = checkAttributeValueTable(table);
168         if (rc != PLDM_SUCCESS)
169         {
170             return rc;
171         }
172 
173         storeTable(attrValueTablePath, table);
174     }
175     else
176     {
177         return PLDM_INVALID_BIOS_TABLE_TYPE;
178     }
179 
180     if ((tableType == PLDM_BIOS_ATTR_VAL_TABLE) && updateBaseBIOSTable)
181     {
182         updateBaseBIOSTableProperty();
183     }
184 
185     return PLDM_SUCCESS;
186 }
187 
188 int BIOSConfig::checkAttributeTable(const Table& table)
189 {
190     using namespace pldm::bios::utils;
191     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
192     for (auto entry :
193          BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(table.data(), table.size()))
194     {
195         auto attrNameHandle =
196             pldm_bios_table_attr_entry_decode_string_handle(entry);
197 
198         auto stringEnty = pldm_bios_table_string_find_by_handle(
199             stringTable->data(), stringTable->size(), attrNameHandle);
200         if (stringEnty == nullptr)
201         {
202             return PLDM_INVALID_BIOS_ATTR_HANDLE;
203         }
204 
205         auto attrType = static_cast<pldm_bios_attribute_type>(
206             pldm_bios_table_attr_entry_decode_attribute_type(entry));
207 
208         switch (attrType)
209         {
210             case PLDM_BIOS_ENUMERATION:
211             case PLDM_BIOS_ENUMERATION_READ_ONLY:
212             {
213                 uint8_t pvNum;
214                 // Preconditions are upheld therefore no error check necessary
215                 pldm_bios_table_attr_entry_enum_decode_pv_num_check(entry,
216                                                                     &pvNum);
217                 std::vector<uint16_t> pvHandls(pvNum);
218                 // Preconditions are upheld therefore no error check necessary
219                 pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
220                     entry, pvHandls.data(), pvHandls.size());
221                 uint8_t defNum;
222                 pldm_bios_table_attr_entry_enum_decode_def_num_check(entry,
223                                                                      &defNum);
224                 std::vector<uint8_t> defIndices(defNum);
225                 pldm_bios_table_attr_entry_enum_decode_def_indices(
226                     entry, defIndices.data(), defIndices.size());
227 
228                 for (size_t i = 0; i < pvHandls.size(); i++)
229                 {
230                     auto stringEntry = pldm_bios_table_string_find_by_handle(
231                         stringTable->data(), stringTable->size(), pvHandls[i]);
232                     if (stringEntry == nullptr)
233                     {
234                         return PLDM_INVALID_BIOS_ATTR_HANDLE;
235                     }
236                 }
237 
238                 for (size_t i = 0; i < defIndices.size(); i++)
239                 {
240                     auto stringEntry = pldm_bios_table_string_find_by_handle(
241                         stringTable->data(), stringTable->size(),
242                         pvHandls[defIndices[i]]);
243                     if (stringEntry == nullptr)
244                     {
245                         return PLDM_INVALID_BIOS_ATTR_HANDLE;
246                     }
247                 }
248                 break;
249             }
250             case PLDM_BIOS_INTEGER:
251             case PLDM_BIOS_INTEGER_READ_ONLY:
252             case PLDM_BIOS_STRING:
253             case PLDM_BIOS_STRING_READ_ONLY:
254             case PLDM_BIOS_PASSWORD:
255             case PLDM_BIOS_PASSWORD_READ_ONLY:
256                 break;
257             default:
258                 return PLDM_INVALID_BIOS_ATTR_HANDLE;
259         }
260     }
261 
262     return PLDM_SUCCESS;
263 }
264 
265 int BIOSConfig::checkAttributeValueTable(const Table& table)
266 {
267     using namespace pldm::bios::utils;
268     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
269     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
270 
271     baseBIOSTableMaps.clear();
272 
273     for (auto tableEntry :
274          BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(table.data(), table.size()))
275     {
276         AttributeName attributeName{};
277         AttributeType attributeType{};
278         ReadonlyStatus readonlyStatus{};
279         DisplayName displayName{};
280         Description description{};
281         MenuPath menuPath{};
282         CurrentValue currentValue{};
283         DefaultValue defaultValue{};
284         std::vector<ValueDisplayName> valueDisplayNames;
285         std::map<uint16_t, std::vector<std::string>> valueDisplayNamesMap;
286         Option options{};
287 
288         auto attrValueHandle =
289             pldm_bios_table_attr_value_entry_decode_attribute_handle(
290                 tableEntry);
291         auto attrType = static_cast<pldm_bios_attribute_type>(
292             pldm_bios_table_attr_value_entry_decode_attribute_type(tableEntry));
293 
294         auto attrEntry = pldm_bios_table_attr_find_by_handle(
295             attrTable->data(), attrTable->size(), attrValueHandle);
296         if (attrEntry == nullptr)
297         {
298             return PLDM_INVALID_BIOS_ATTR_HANDLE;
299         }
300         auto attrHandle =
301             pldm_bios_table_attr_entry_decode_attribute_handle(attrEntry);
302         auto attrNameHandle =
303             pldm_bios_table_attr_entry_decode_string_handle(attrEntry);
304 
305         auto stringEntry = pldm_bios_table_string_find_by_handle(
306             stringTable->data(), stringTable->size(), attrNameHandle);
307         if (stringEntry == nullptr)
308         {
309             return PLDM_INVALID_BIOS_ATTR_HANDLE;
310         }
311         auto strLength =
312             pldm_bios_table_string_entry_decode_string_length(stringEntry);
313         std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
314         // Preconditions are upheld therefore no error check necessary
315         pldm_bios_table_string_entry_decode_string_check(
316             stringEntry, buffer.data(), buffer.size());
317 
318         attributeName = std::string(buffer.data(), buffer.data() + strLength);
319 
320         if (!biosAttributes.empty())
321         {
322             readonlyStatus =
323                 biosAttributes[attrHandle % biosAttributes.size()]->readOnly;
324             description =
325                 biosAttributes[attrHandle % biosAttributes.size()]->helpText;
326             displayName =
327                 biosAttributes[attrHandle % biosAttributes.size()]->displayName;
328             valueDisplayNamesMap =
329                 biosAttributes[attrHandle % biosAttributes.size()]
330                     ->valueDisplayNamesMap;
331         }
332 
333         switch (attrType)
334         {
335             case PLDM_BIOS_ENUMERATION:
336             case PLDM_BIOS_ENUMERATION_READ_ONLY:
337             {
338                 if (valueDisplayNamesMap.contains(attrHandle))
339                 {
340                     const std::vector<ValueDisplayName>& vdn =
341                         valueDisplayNamesMap[attrHandle];
342                     valueDisplayNames.insert(valueDisplayNames.end(),
343                                              vdn.begin(), vdn.end());
344                 }
345                 auto getValue = [](uint16_t handle,
346                                    const Table& table) -> std::string {
347                     auto stringEntry = pldm_bios_table_string_find_by_handle(
348                         table.data(), table.size(), handle);
349 
350                     auto strLength =
351                         pldm_bios_table_string_entry_decode_string_length(
352                             stringEntry);
353                     std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
354                     // Preconditions are upheld therefore no error check
355                     // necessary
356                     pldm_bios_table_string_entry_decode_string_check(
357                         stringEntry, buffer.data(), buffer.size());
358 
359                     return std::string(buffer.data(),
360                                        buffer.data() + strLength);
361                 };
362 
363                 attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
364                                 "AttributeType.Enumeration";
365 
366                 uint8_t pvNum;
367                 // Preconditions are upheld therefore no error check necessary
368                 pldm_bios_table_attr_entry_enum_decode_pv_num_check(attrEntry,
369                                                                     &pvNum);
370                 std::vector<uint16_t> pvHandls(pvNum);
371                 // Preconditions are upheld therefore no error check necessary
372                 pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
373                     attrEntry, pvHandls.data(), pvHandls.size());
374 
375                 // get possible_value
376                 for (size_t i = 0; i < pvHandls.size(); i++)
377                 {
378                     options.push_back(
379                         std::make_tuple("xyz.openbmc_project.BIOSConfig."
380                                         "Manager.BoundType.OneOf",
381                                         getValue(pvHandls[i], *stringTable),
382                                         valueDisplayNames[i]));
383                 }
384 
385                 auto count =
386                     pldm_bios_table_attr_value_entry_enum_decode_number(
387                         tableEntry);
388                 std::vector<uint8_t> handles(count);
389                 pldm_bios_table_attr_value_entry_enum_decode_handles(
390                     tableEntry, handles.data(), handles.size());
391 
392                 // get current_value
393                 for (size_t i = 0; i < handles.size(); i++)
394                 {
395                     currentValue = getValue(pvHandls[handles[i]], *stringTable);
396                 }
397 
398                 uint8_t defNum;
399                 // Preconditions are upheld therefore no error check necessary
400                 pldm_bios_table_attr_entry_enum_decode_def_num_check(attrEntry,
401                                                                      &defNum);
402                 std::vector<uint8_t> defIndices(defNum);
403                 pldm_bios_table_attr_entry_enum_decode_def_indices(
404                     attrEntry, defIndices.data(), defIndices.size());
405 
406                 // get default_value
407                 for (size_t i = 0; i < defIndices.size(); i++)
408                 {
409                     defaultValue = getValue(pvHandls[defIndices[i]],
410                                             *stringTable);
411                 }
412 
413                 break;
414             }
415             case PLDM_BIOS_INTEGER:
416             case PLDM_BIOS_INTEGER_READ_ONLY:
417             {
418                 attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
419                                 "AttributeType.Integer";
420                 currentValue = static_cast<int64_t>(
421                     pldm_bios_table_attr_value_entry_integer_decode_cv(
422                         tableEntry));
423 
424                 uint64_t lower, upper, def;
425                 uint32_t scalar;
426                 pldm_bios_table_attr_entry_integer_decode(
427                     attrEntry, &lower, &upper, &scalar, &def);
428                 options.push_back(std::make_tuple(
429                     "xyz.openbmc_project.BIOSConfig.Manager."
430                     "BoundType.LowerBound",
431                     static_cast<int64_t>(lower), attributeName));
432                 options.push_back(std::make_tuple(
433                     "xyz.openbmc_project.BIOSConfig.Manager."
434                     "BoundType.UpperBound",
435                     static_cast<int64_t>(upper), attributeName));
436                 options.push_back(std::make_tuple(
437                     "xyz.openbmc_project.BIOSConfig.Manager."
438                     "BoundType.ScalarIncrement",
439                     static_cast<int64_t>(scalar), attributeName));
440                 defaultValue = static_cast<int64_t>(def);
441                 break;
442             }
443             case PLDM_BIOS_STRING:
444             case PLDM_BIOS_STRING_READ_ONLY:
445             {
446                 attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
447                                 "AttributeType.String";
448                 variable_field currentString;
449                 pldm_bios_table_attr_value_entry_string_decode_string(
450                     tableEntry, &currentString);
451                 currentValue = std::string(
452                     reinterpret_cast<const char*>(currentString.ptr),
453                     currentString.length);
454                 auto min = pldm_bios_table_attr_entry_string_decode_min_length(
455                     attrEntry);
456                 auto max = pldm_bios_table_attr_entry_string_decode_max_length(
457                     attrEntry);
458                 uint16_t def;
459                 // Preconditions are upheld therefore no error check necessary
460                 pldm_bios_table_attr_entry_string_decode_def_string_length_check(
461                     attrEntry, &def);
462                 std::vector<char> defString(def + 1);
463                 pldm_bios_table_attr_entry_string_decode_def_string(
464                     attrEntry, defString.data(), defString.size());
465                 options.push_back(
466                     std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
467                                     "BoundType.MinStringLength",
468                                     static_cast<int64_t>(min), attributeName));
469                 options.push_back(
470                     std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
471                                     "BoundType.MaxStringLength",
472                                     static_cast<int64_t>(max), attributeName));
473                 defaultValue = defString.data();
474                 break;
475             }
476             case PLDM_BIOS_PASSWORD:
477             case PLDM_BIOS_PASSWORD_READ_ONLY:
478             {
479                 attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
480                                 "AttributeType.Password";
481                 break;
482             }
483             default:
484                 return PLDM_INVALID_BIOS_ATTR_HANDLE;
485         }
486         baseBIOSTableMaps.emplace(
487             std::move(attributeName),
488             std::make_tuple(attributeType, readonlyStatus, displayName,
489                             description, menuPath, currentValue, defaultValue,
490                             std::move(options)));
491     }
492 
493     return PLDM_SUCCESS;
494 }
495 
496 void BIOSConfig::updateBaseBIOSTableProperty()
497 {
498     constexpr static auto biosConfigPath =
499         "/xyz/openbmc_project/bios_config/manager";
500     constexpr static auto biosConfigInterface =
501         "xyz.openbmc_project.BIOSConfig.Manager";
502     constexpr static auto biosConfigPropertyName = "BaseBIOSTable";
503     constexpr static auto dbusProperties = "org.freedesktop.DBus.Properties";
504 
505     if (baseBIOSTableMaps.empty())
506     {
507         return;
508     }
509 
510     try
511     {
512         auto& bus = dbusHandler->getBus();
513         auto service = dbusHandler->getService(biosConfigPath,
514                                                biosConfigInterface);
515         auto method = bus.new_method_call(service.c_str(), biosConfigPath,
516                                           dbusProperties, "Set");
517         std::variant<BaseBIOSTable> value = baseBIOSTableMaps;
518         method.append(biosConfigInterface, biosConfigPropertyName, value);
519         bus.call_noreply(method, dbusTimeout);
520     }
521     catch (const std::exception& e)
522     {
523         error("failed to update BaseBIOSTable property, ERROR={ERR_EXCEP}",
524               "ERR_EXCEP", e.what());
525     }
526 }
527 
528 void BIOSConfig::constructAttributes()
529 {
530     info("Bios Attribute file path: {PATH}", "PATH", (jsonDir / sysType));
531     load(jsonDir / sysType / stringJsonFile, [this](const Json& entry) {
532         constructAttribute<BIOSStringAttribute>(entry);
533     });
534     load(jsonDir / sysType / integerJsonFile, [this](const Json& entry) {
535         constructAttribute<BIOSIntegerAttribute>(entry);
536     });
537     load(jsonDir / sysType / enumJsonFile, [this](const Json& entry) {
538         constructAttribute<BIOSEnumAttribute>(entry);
539     });
540 }
541 
542 void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable)
543 {
544     BIOSStringTable biosStringTable(stringTable);
545 
546     if (biosAttributes.empty())
547     {
548         return;
549     }
550 
551     BaseBIOSTable biosTable{};
552     constexpr auto biosObjPath = "/xyz/openbmc_project/bios_config/manager";
553     constexpr auto biosInterface = "xyz.openbmc_project.BIOSConfig.Manager";
554 
555     try
556     {
557         auto& bus = dbusHandler->getBus();
558         auto service = dbusHandler->getService(biosObjPath, biosInterface);
559         auto method = bus.new_method_call(service.c_str(), biosObjPath,
560                                           "org.freedesktop.DBus.Properties",
561                                           "Get");
562         method.append(biosInterface, "BaseBIOSTable");
563         auto reply = bus.call(method, dbusTimeout);
564         std::variant<BaseBIOSTable> varBiosTable{};
565         reply.read(varBiosTable);
566         biosTable = std::get<BaseBIOSTable>(varBiosTable);
567     }
568     // Failed to read the BaseBIOSTable, so update the BaseBIOSTable with the
569     // default values populated from the BIOS JSONs to keep PLDM and
570     // bios-settings-manager in sync
571     catch (const std::exception& e)
572     {
573         error("Failed to read BaseBIOSTable property, ERROR={ERR_EXCEP}",
574               "ERR_EXCEP", e.what());
575     }
576 
577     Table attrTable, attrValueTable;
578 
579     for (auto& attr : biosAttributes)
580     {
581         try
582         {
583             auto iter = biosTable.find(attr->name);
584             if (iter == biosTable.end())
585             {
586                 attr->constructEntry(biosStringTable, attrTable, attrValueTable,
587                                      std::nullopt);
588             }
589             else
590             {
591                 attr->constructEntry(
592                     biosStringTable, attrTable, attrValueTable,
593                     std::get<static_cast<uint8_t>(Index::currentValue)>(
594                         iter->second));
595             }
596         }
597         catch (const std::exception& e)
598         {
599             error("Error constructing table entry for '{ATTR}': {ERROR}",
600                   "ATTR", attr->name, "ERROR", e);
601         }
602     }
603 
604     table::appendPadAndChecksum(attrTable);
605     table::appendPadAndChecksum(attrValueTable);
606     setBIOSTable(PLDM_BIOS_ATTR_TABLE, attrTable);
607     setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, attrValueTable);
608 }
609 
610 std::optional<Table> BIOSConfig::buildAndStoreStringTable()
611 {
612     std::set<std::string> strings;
613     auto handler = [&strings](const Json& entry) {
614         strings.emplace(entry.at("attribute_name"));
615     };
616 
617     load(jsonDir / sysType / stringJsonFile, handler);
618     load(jsonDir / sysType / integerJsonFile, handler);
619     load(jsonDir / sysType / enumJsonFile, [&strings](const Json& entry) {
620         strings.emplace(entry.at("attribute_name"));
621         auto possibleValues = entry.at("possible_values");
622         for (auto& pv : possibleValues)
623         {
624             strings.emplace(pv);
625         }
626     });
627 
628     if (strings.empty())
629     {
630         return std::nullopt;
631     }
632 
633     Table table;
634     for (const auto& elem : strings)
635     {
636         table::string::constructEntry(table, elem);
637     }
638 
639     table::appendPadAndChecksum(table);
640     setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
641     return table;
642 }
643 
644 void BIOSConfig::storeTable(const fs::path& path, const Table& table)
645 {
646     BIOSTable biosTable(path.c_str());
647     biosTable.store(table);
648 }
649 
650 std::optional<Table> BIOSConfig::loadTable(const fs::path& path)
651 {
652     BIOSTable biosTable(path.c_str());
653     if (biosTable.isEmpty())
654     {
655         return std::nullopt;
656     }
657 
658     Table table;
659     biosTable.load(table);
660     return table;
661 }
662 
663 void BIOSConfig::load(const fs::path& filePath, ParseHandler handler)
664 {
665     std::ifstream file;
666     Json jsonConf;
667     if (fs::exists(filePath))
668     {
669         try
670         {
671             file.open(filePath);
672             jsonConf = Json::parse(file);
673             auto entries = jsonConf.at("entries");
674             for (auto& entry : entries)
675             {
676                 try
677                 {
678                     handler(entry);
679                 }
680                 catch (const std::exception& e)
681                 {
682                     error(
683                         "Failed to parse JSON config file(entry handler) : {JSON_PATH}, {ERR_EXCEP}",
684                         "JSON_PATH", filePath.c_str(), "ERR_EXCEP", e.what());
685                 }
686             }
687         }
688         catch (const std::exception& e)
689         {
690             error("Failed to parse JSON config at '{PATH}': {ERROR}", "PATH",
691                   filePath.c_str(), "ERROR", e);
692         }
693     }
694 }
695 
696 std::string BIOSConfig::decodeStringFromStringEntry(
697     const pldm_bios_string_table_entry* stringEntry)
698 {
699     auto strLength =
700         pldm_bios_table_string_entry_decode_string_length(stringEntry);
701     std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
702     // Preconditions are upheld therefore no error check necessary
703     pldm_bios_table_string_entry_decode_string_check(stringEntry, buffer.data(),
704                                                      buffer.size());
705     return std::string(buffer.data(), buffer.data() + strLength);
706 }
707 
708 std::string
709     BIOSConfig::displayStringHandle(uint16_t handle, uint8_t index,
710                                     const std::optional<Table>& attrTable,
711                                     const std::optional<Table>& stringTable)
712 {
713     auto attrEntry = pldm_bios_table_attr_find_by_handle(
714         attrTable->data(), attrTable->size(), handle);
715     uint8_t pvNum;
716     int rc = pldm_bios_table_attr_entry_enum_decode_pv_num_check(attrEntry,
717                                                                  &pvNum);
718     if (rc != PLDM_SUCCESS)
719     {
720         error(
721             "Failed to decode BIOS table possible values for attribute entry: {LIPBLDM_ERROR}",
722             "LIBPLDM_ERROR", rc);
723         throw std::runtime_error(
724             "Failed to decode BIOS table possible values for attribute entry");
725     }
726 
727     std::vector<uint16_t> pvHandls(pvNum);
728     // Preconditions are upheld therefore no error check necessary
729     pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
730         attrEntry, pvHandls.data(), pvHandls.size());
731 
732     std::string displayString = std::to_string(pvHandls[index]);
733 
734     auto stringEntry = pldm_bios_table_string_find_by_handle(
735         stringTable->data(), stringTable->size(), pvHandls[index]);
736 
737     auto decodedStr = decodeStringFromStringEntry(stringEntry);
738 
739     return decodedStr + "(" + displayString + ")";
740 }
741 
742 void BIOSConfig::traceBIOSUpdate(
743     const pldm_bios_attr_val_table_entry* attrValueEntry,
744     const pldm_bios_attr_table_entry* attrEntry, bool isBMC)
745 {
746     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
747     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
748 
749     auto [attrHandle,
750           attrType] = table::attribute_value::decodeHeader(attrValueEntry);
751 
752     auto attrHeader = table::attribute::decodeHeader(attrEntry);
753     BIOSStringTable biosStringTable(*stringTable);
754     auto attrName = biosStringTable.findString(attrHeader.stringHandle);
755 
756     switch (attrType)
757     {
758         case PLDM_BIOS_ENUMERATION:
759         case PLDM_BIOS_ENUMERATION_READ_ONLY:
760         {
761             auto count = pldm_bios_table_attr_value_entry_enum_decode_number(
762                 attrValueEntry);
763             std::vector<uint8_t> handles(count);
764             pldm_bios_table_attr_value_entry_enum_decode_handles(
765                 attrValueEntry, handles.data(), handles.size());
766 
767             for (uint8_t handle : handles)
768             {
769                 auto nwVal = displayStringHandle(attrHandle, handle, attrTable,
770                                                  stringTable);
771                 auto chkBMC = isBMC ? "true" : "false";
772                 info(
773                     "BIOS:{ATTR_NAME}, updated to value: {NEW_VAL}, by BMC: {CHK_BMC} ",
774                     "ATTR_NAME", attrName, "NEW_VAL", nwVal, "CHK_BMC", chkBMC);
775             }
776             break;
777         }
778         case PLDM_BIOS_INTEGER:
779         case PLDM_BIOS_INTEGER_READ_ONLY:
780         {
781             auto value =
782                 table::attribute_value::decodeIntegerEntry(attrValueEntry);
783             auto chkBMC = isBMC ? "true" : "false";
784             info(
785                 "BIOS:  {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}",
786                 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC);
787             break;
788         }
789         case PLDM_BIOS_STRING:
790         case PLDM_BIOS_STRING_READ_ONLY:
791         {
792             auto value =
793                 table::attribute_value::decodeStringEntry(attrValueEntry);
794             auto chkBMC = isBMC ? "true" : "false";
795             info(
796                 "BIOS:  {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}",
797                 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC);
798             break;
799         }
800         default:
801             break;
802     };
803 }
804 
805 int BIOSConfig::checkAttrValueToUpdate(
806     const pldm_bios_attr_val_table_entry* attrValueEntry,
807     const pldm_bios_attr_table_entry* attrEntry, Table&)
808 
809 {
810     auto [attrHandle,
811           attrType] = table::attribute_value::decodeHeader(attrValueEntry);
812 
813     switch (attrType)
814     {
815         case PLDM_BIOS_ENUMERATION:
816         case PLDM_BIOS_ENUMERATION_READ_ONLY:
817         {
818             auto value =
819                 table::attribute_value::decodeEnumEntry(attrValueEntry);
820             auto [pvHdls,
821                   defIndex] = table::attribute::decodeEnumEntry(attrEntry);
822             if (!(value.size() == 1))
823             {
824                 return PLDM_ERROR_INVALID_LENGTH;
825             }
826             if (value[0] >= pvHdls.size())
827             {
828                 error("Enum: Illgeal index, Index = {ATTR_INDEX}", "ATTR_INDEX",
829                       (int)value[0]);
830                 return PLDM_ERROR_INVALID_DATA;
831             }
832             return PLDM_SUCCESS;
833         }
834         case PLDM_BIOS_INTEGER:
835         case PLDM_BIOS_INTEGER_READ_ONLY:
836         {
837             auto value =
838                 table::attribute_value::decodeIntegerEntry(attrValueEntry);
839             auto [lower, upper, scalar,
840                   def] = table::attribute::decodeIntegerEntry(attrEntry);
841 
842             if (value < lower || value > upper)
843             {
844                 error("Integer: out of bound, value = {ATTR_VALUE}",
845                       "ATTR_VALUE", value);
846                 return PLDM_ERROR_INVALID_DATA;
847             }
848             return PLDM_SUCCESS;
849         }
850         case PLDM_BIOS_STRING:
851         case PLDM_BIOS_STRING_READ_ONLY:
852         {
853             auto stringConf = table::attribute::decodeStringEntry(attrEntry);
854             auto value =
855                 table::attribute_value::decodeStringEntry(attrValueEntry);
856             if (value.size() < stringConf.minLength ||
857                 value.size() > stringConf.maxLength)
858             {
859                 error(
860                     "String: Length error, string = {ATTR_VALUE} length {LEN}",
861                     "ATTR_VALUE", value, "LEN", value.size());
862                 return PLDM_ERROR_INVALID_LENGTH;
863             }
864             return PLDM_SUCCESS;
865         }
866         default:
867             error("ReadOnly or Unspported type, type = {ATTR_TYPE}",
868                   "ATTR_TYPE", attrType);
869             return PLDM_ERROR;
870     };
871 }
872 
873 int BIOSConfig::setAttrValue(const void* entry, size_t size, bool isBMC,
874                              bool updateDBus, bool updateBaseBIOSTable)
875 {
876     auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
877     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
878     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
879     if (!attrValueTable || !attrTable || !stringTable)
880     {
881         return PLDM_BIOS_TABLE_UNAVAILABLE;
882     }
883 
884     auto attrValueEntry =
885         reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry);
886 
887     auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry);
888 
889     auto attrEntry = table::attribute::findByHandle(*attrTable,
890                                                     attrValHeader.attrHandle);
891     if (!attrEntry)
892     {
893         return PLDM_ERROR;
894     }
895 
896     auto rc = checkAttrValueToUpdate(attrValueEntry, attrEntry, *stringTable);
897     if (rc != PLDM_SUCCESS)
898     {
899         return rc;
900     }
901 
902     auto destTable = table::attribute_value::updateTable(*attrValueTable, entry,
903                                                          size);
904 
905     if (!destTable)
906     {
907         return PLDM_ERROR;
908     }
909 
910     try
911     {
912         auto attrHeader = table::attribute::decodeHeader(attrEntry);
913 
914         BIOSStringTable biosStringTable(*stringTable);
915         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
916         auto iter = std::find_if(
917             biosAttributes.begin(), biosAttributes.end(),
918             [&attrName](const auto& attr) { return attr->name == attrName; });
919 
920         if (iter == biosAttributes.end())
921         {
922             return PLDM_ERROR;
923         }
924         if (updateDBus)
925         {
926             (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry,
927                                         biosStringTable);
928         }
929     }
930     catch (const std::exception& e)
931     {
932         error("Set attribute value error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
933         return PLDM_ERROR;
934     }
935 
936     setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, *destTable, updateBaseBIOSTable);
937 
938     traceBIOSUpdate(attrValueEntry, attrEntry, isBMC);
939 
940     return PLDM_SUCCESS;
941 }
942 
943 void BIOSConfig::removeTables()
944 {
945     try
946     {
947         fs::remove(tableDir / stringTableFile);
948         fs::remove(tableDir / attrTableFile);
949         fs::remove(tableDir / attrValueTableFile);
950     }
951     catch (const std::exception& e)
952     {
953         error("Remove the tables error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
954     }
955 }
956 
957 void BIOSConfig::processBiosAttrChangeNotification(
958     const DbusChObjProperties& chProperties, uint32_t biosAttrIndex)
959 {
960     const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap();
961     const auto& propertyName = dBusMap->propertyName;
962     const auto& attrName = biosAttributes[biosAttrIndex]->name;
963 
964     const auto it = chProperties.find(propertyName);
965     if (it == chProperties.end())
966     {
967         return;
968     }
969 
970     PropertyValue newPropVal = it->second;
971     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
972     if (!stringTable.has_value())
973     {
974         error("BIOS string table unavailable");
975         return;
976     }
977     BIOSStringTable biosStringTable(*stringTable);
978     uint16_t attrNameHdl{};
979     try
980     {
981         attrNameHdl = biosStringTable.findHandle(attrName);
982     }
983     catch (const std::invalid_argument& e)
984     {
985         error("Missing handle for '{ATTR}': {ERROR}", "ATTR", attrName, "ERROR",
986               e);
987         return;
988     }
989 
990     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
991     if (!attrTable.has_value())
992     {
993         error("Attribute table not present");
994         return;
995     }
996     const struct pldm_bios_attr_table_entry* tableEntry =
997         table::attribute::findByStringHandle(*attrTable, attrNameHdl);
998     if (tableEntry == nullptr)
999     {
1000         error(
1001             "Attribute not found in attribute table, name= {ATTR_NAME} name handle={ATTR_HANDLE}",
1002             "ATTR_NAME", attrName.c_str(), "ATTR_HANDLE", attrNameHdl);
1003         return;
1004     }
1005 
1006     auto [attrHdl, attrType,
1007           stringHdl] = table::attribute::decodeHeader(tableEntry);
1008 
1009     auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
1010 
1011     if (!attrValueSrcTable.has_value())
1012     {
1013         error("Attribute value table not present");
1014         return;
1015     }
1016 
1017     Table newValue;
1018     auto rc = biosAttributes[biosAttrIndex]->updateAttrVal(
1019         newValue, attrHdl, attrType, newPropVal);
1020     if (rc != PLDM_SUCCESS)
1021     {
1022         error(
1023             "Could not update the attribute value table for attribute handle={ATTR_HANDLE} and type={ATTR_TYPE}",
1024             "ATTR_HANDLE", attrHdl, "ATTR_TYPE", (uint32_t)attrType);
1025         return;
1026     }
1027     auto destTable = table::attribute_value::updateTable(
1028         *attrValueSrcTable, newValue.data(), newValue.size());
1029     if (destTable.has_value())
1030     {
1031         storeTable(tableDir / attrValueTableFile, *destTable);
1032     }
1033 
1034     rc = setAttrValue(newValue.data(), newValue.size(), true, false);
1035     if (rc != PLDM_SUCCESS)
1036     {
1037         error("could not setAttrValue on base bios table and dbus, rc = {RC}",
1038               "RC", rc);
1039     }
1040 }
1041 
1042 uint16_t BIOSConfig::findAttrHandle(const std::string& attrName)
1043 {
1044     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
1045     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
1046 
1047     BIOSStringTable biosStringTable(*stringTable);
1048     pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(
1049         attrTable->data(), attrTable->size());
1050     auto stringHandle = biosStringTable.findHandle(attrName);
1051 
1052     for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(
1053              attrTable->data(), attrTable->size()))
1054     {
1055         auto header = table::attribute::decodeHeader(entry);
1056         if (header.stringHandle == stringHandle)
1057         {
1058             return header.attrHandle;
1059         }
1060     }
1061 
1062     throw std::invalid_argument("Unknow attribute Name");
1063 }
1064 
1065 void BIOSConfig::constructPendingAttribute(
1066     const PendingAttributes& pendingAttributes)
1067 {
1068     std::vector<uint16_t> listOfHandles{};
1069 
1070     for (auto& attribute : pendingAttributes)
1071     {
1072         std::string attributeName = attribute.first;
1073         auto& [attributeType, attributevalue] = attribute.second;
1074 
1075         auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(),
1076                                  [&attributeName](const auto& attr) {
1077             return attr->name == attributeName;
1078         });
1079 
1080         if (iter == biosAttributes.end())
1081         {
1082             error("Wrong attribute name, attributeName = {ATTR_NAME}",
1083                   "ATTR_NAME", attributeName);
1084             continue;
1085         }
1086 
1087         Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0);
1088         auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
1089             attrValueEntry.data());
1090 
1091         auto handler = findAttrHandle(attributeName);
1092         auto type =
1093             BIOSConfigManager::convertAttributeTypeFromString(attributeType);
1094 
1095         if (type != BIOSConfigManager::AttributeType::Enumeration &&
1096             type != BIOSConfigManager::AttributeType::String &&
1097             type != BIOSConfigManager::AttributeType::Integer)
1098         {
1099             error("Attribute type not supported, attributeType = {ATTR_TYPE}",
1100                   "ATTR_TYPE", attributeType);
1101             continue;
1102         }
1103 
1104         const auto [attrType, readonlyStatus, displayName, description,
1105                     menuPath, currentValue, defaultValue,
1106                     option] = baseBIOSTableMaps.at(attributeName);
1107 
1108         entry->attr_handle = htole16(handler);
1109 
1110         // Need to verify that the current value has really changed
1111         if (attributeType == attrType && attributevalue != currentValue)
1112         {
1113             listOfHandles.emplace_back(htole16(handler));
1114         }
1115 
1116         (*iter)->generateAttributeEntry(attributevalue, attrValueEntry);
1117 
1118         setAttrValue(attrValueEntry.data(), attrValueEntry.size(), true);
1119     }
1120 
1121     if (listOfHandles.size())
1122     {
1123 #ifdef OEM_IBM
1124         auto rc = pldm::responder::platform::sendBiosAttributeUpdateEvent(
1125             eid, instanceIdDb, listOfHandles, handler);
1126         if (rc != PLDM_SUCCESS)
1127         {
1128             return;
1129         }
1130 #endif
1131     }
1132 }
1133 
1134 void BIOSConfig::listenPendingAttributes()
1135 {
1136     constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager";
1137     constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager";
1138 
1139     using namespace sdbusplus::bus::match::rules;
1140     auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>(
1141         pldm::utils::DBusHandler::getBus(),
1142         propertiesChanged(objPath, objInterface),
1143         [this](sdbusplus::message_t& msg) {
1144         constexpr auto propertyName = "PendingAttributes";
1145 
1146         using Value =
1147             std::variant<std::string, PendingAttributes, BaseBIOSTable>;
1148         using Properties = std::map<DbusProp, Value>;
1149 
1150         Properties props{};
1151         std::string intf;
1152         msg.read(intf, props);
1153 
1154         auto valPropMap = props.find(propertyName);
1155         if (valPropMap == props.end())
1156         {
1157             return;
1158         }
1159 
1160         PendingAttributes pendingAttributes =
1161             std::get<PendingAttributes>(valPropMap->second);
1162         this->constructPendingAttribute(pendingAttributes);
1163     });
1164 
1165     biosAttrMatch.emplace_back(std::move(updateBIOSMatch));
1166 }
1167 
1168 } // namespace bios
1169 } // namespace responder
1170 } // namespace pldm
1171