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