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