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