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