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