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, dbusTimeout);
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(method, dbusTimeout);
518         std::variant<BaseBIOSTable> varBiosTable{};
519         reply.read(varBiosTable);
520         biosTable = std::get<BaseBIOSTable>(varBiosTable);
521     }
522     // Failed to read the BaseBIOSTable, so update the BaseBIOSTable with the
523     // default values populated from the BIOS JSONs to keep PLDM and
524     // bios-settings-manager in sync
525     catch (const std::exception& e)
526     {
527         error("Failed to read BaseBIOSTable property, ERROR={ERR_EXCEP}",
528               "ERR_EXCEP", e.what());
529     }
530 
531     Table attrTable, attrValueTable;
532 
533     for (auto& attr : biosAttributes)
534     {
535         try
536         {
537             auto iter = biosTable.find(attr->name);
538             if (iter == biosTable.end())
539             {
540                 attr->constructEntry(biosStringTable, attrTable, attrValueTable,
541                                      std::nullopt);
542             }
543             else
544             {
545                 attr->constructEntry(
546                     biosStringTable, attrTable, attrValueTable,
547                     std::get<static_cast<uint8_t>(Index::currentValue)>(
548                         iter->second));
549             }
550         }
551         catch (const std::exception& e)
552         {
553             error("Construct Table Entry Error, AttributeName = {ATTR_NAME}",
554                   "ATTR_NAME", attr->name);
555         }
556     }
557 
558     table::appendPadAndChecksum(attrTable);
559     table::appendPadAndChecksum(attrValueTable);
560     setBIOSTable(PLDM_BIOS_ATTR_TABLE, attrTable);
561     setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, attrValueTable);
562 }
563 
564 std::optional<Table> BIOSConfig::buildAndStoreStringTable()
565 {
566     std::set<std::string> strings;
567     auto handler = [&strings](const Json& entry) {
568         strings.emplace(entry.at("attribute_name"));
569     };
570 
571     load(jsonDir / sysType / stringJsonFile, handler);
572     load(jsonDir / sysType / integerJsonFile, handler);
573     load(jsonDir / sysType / enumJsonFile, [&strings](const Json& entry) {
574         strings.emplace(entry.at("attribute_name"));
575         auto possibleValues = entry.at("possible_values");
576         for (auto& pv : possibleValues)
577         {
578             strings.emplace(pv);
579         }
580     });
581 
582     if (strings.empty())
583     {
584         return std::nullopt;
585     }
586 
587     Table table;
588     for (const auto& elem : strings)
589     {
590         table::string::constructEntry(table, elem);
591     }
592 
593     table::appendPadAndChecksum(table);
594     setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
595     return table;
596 }
597 
598 void BIOSConfig::storeTable(const fs::path& path, const Table& table)
599 {
600     BIOSTable biosTable(path.c_str());
601     biosTable.store(table);
602 }
603 
604 std::optional<Table> BIOSConfig::loadTable(const fs::path& path)
605 {
606     BIOSTable biosTable(path.c_str());
607     if (biosTable.isEmpty())
608     {
609         return std::nullopt;
610     }
611 
612     Table table;
613     biosTable.load(table);
614     return table;
615 }
616 
617 void BIOSConfig::load(const fs::path& filePath, ParseHandler handler)
618 {
619     std::ifstream file;
620     Json jsonConf;
621     if (fs::exists(filePath))
622     {
623         try
624         {
625             file.open(filePath);
626             jsonConf = Json::parse(file);
627             auto entries = jsonConf.at("entries");
628             for (auto& entry : entries)
629             {
630                 try
631                 {
632                     handler(entry);
633                 }
634                 catch (const std::exception& e)
635                 {
636                     error(
637                         "Failed to parse JSON config file(entry handler) : {JSON_PATH}, {ERR_EXCEP}",
638                         "JSON_PATH", filePath.c_str(), "ERR_EXCEP", e.what());
639                 }
640             }
641         }
642         catch (const std::exception& e)
643         {
644             error("Failed to parse JSON config file : {JSON_PATH}", "JSON_PATH",
645                   filePath.c_str());
646         }
647     }
648 }
649 
650 std::string BIOSConfig::decodeStringFromStringEntry(
651     const pldm_bios_string_table_entry* stringEntry)
652 {
653     auto strLength =
654         pldm_bios_table_string_entry_decode_string_length(stringEntry);
655     std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
656     // Preconditions are upheld therefore no error check necessary
657     pldm_bios_table_string_entry_decode_string_check(stringEntry, buffer.data(),
658                                                      buffer.size());
659     return std::string(buffer.data(), buffer.data() + strLength);
660 }
661 
662 std::string
663     BIOSConfig::displayStringHandle(uint16_t handle, uint8_t index,
664                                     const std::optional<Table>& attrTable,
665                                     const std::optional<Table>& stringTable)
666 {
667     auto attrEntry = pldm_bios_table_attr_find_by_handle(
668         attrTable->data(), attrTable->size(), handle);
669     uint8_t pvNum;
670     int rc = pldm_bios_table_attr_entry_enum_decode_pv_num_check(attrEntry,
671                                                                  &pvNum);
672     if (rc != PLDM_SUCCESS)
673     {
674         error(
675             "Failed to decode BIOS table possible values for attribute entry: {LIPBLDM_ERROR}",
676             "LIBPLDM_ERROR", rc);
677         throw std::runtime_error(
678             "Failed to decode BIOS table possible values for attribute entry");
679     }
680 
681     std::vector<uint16_t> pvHandls(pvNum);
682     // Preconditions are upheld therefore no error check necessary
683     pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
684         attrEntry, pvHandls.data(), pvHandls.size());
685 
686     std::string displayString = std::to_string(pvHandls[index]);
687 
688     auto stringEntry = pldm_bios_table_string_find_by_handle(
689         stringTable->data(), stringTable->size(), pvHandls[index]);
690 
691     auto decodedStr = decodeStringFromStringEntry(stringEntry);
692 
693     return decodedStr + "(" + displayString + ")";
694 }
695 
696 void BIOSConfig::traceBIOSUpdate(
697     const pldm_bios_attr_val_table_entry* attrValueEntry,
698     const pldm_bios_attr_table_entry* attrEntry, bool isBMC)
699 {
700     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
701     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
702 
703     auto [attrHandle,
704           attrType] = table::attribute_value::decodeHeader(attrValueEntry);
705 
706     auto attrHeader = table::attribute::decodeHeader(attrEntry);
707     BIOSStringTable biosStringTable(*stringTable);
708     auto attrName = biosStringTable.findString(attrHeader.stringHandle);
709 
710     switch (attrType)
711     {
712         case PLDM_BIOS_ENUMERATION:
713         case PLDM_BIOS_ENUMERATION_READ_ONLY:
714         {
715             auto count = pldm_bios_table_attr_value_entry_enum_decode_number(
716                 attrValueEntry);
717             std::vector<uint8_t> handles(count);
718             pldm_bios_table_attr_value_entry_enum_decode_handles(
719                 attrValueEntry, handles.data(), handles.size());
720 
721             for (uint8_t handle : handles)
722             {
723                 auto nwVal = displayStringHandle(attrHandle, handle, attrTable,
724                                                  stringTable);
725                 auto chkBMC = isBMC ? "true" : "false";
726                 info(
727                     "BIOS:{ATTR_NAME}, updated to value: {NEW_VAL}, by BMC: {CHK_BMC} ",
728                     "ATTR_NAME", attrName, "NEW_VAL", nwVal, "CHK_BMC", chkBMC);
729             }
730             break;
731         }
732         case PLDM_BIOS_INTEGER:
733         case PLDM_BIOS_INTEGER_READ_ONLY:
734         {
735             auto value =
736                 table::attribute_value::decodeIntegerEntry(attrValueEntry);
737             auto chkBMC = isBMC ? "true" : "false";
738             info(
739                 "BIOS:  {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}",
740                 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC);
741             break;
742         }
743         case PLDM_BIOS_STRING:
744         case PLDM_BIOS_STRING_READ_ONLY:
745         {
746             auto value =
747                 table::attribute_value::decodeStringEntry(attrValueEntry);
748             auto chkBMC = isBMC ? "true" : "false";
749             info(
750                 "BIOS:  {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}",
751                 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC);
752             break;
753         }
754         default:
755             break;
756     };
757 }
758 
759 int BIOSConfig::checkAttrValueToUpdate(
760     const pldm_bios_attr_val_table_entry* attrValueEntry,
761     const pldm_bios_attr_table_entry* attrEntry, Table&)
762 
763 {
764     auto [attrHandle,
765           attrType] = table::attribute_value::decodeHeader(attrValueEntry);
766 
767     switch (attrType)
768     {
769         case PLDM_BIOS_ENUMERATION:
770         case PLDM_BIOS_ENUMERATION_READ_ONLY:
771         {
772             auto value =
773                 table::attribute_value::decodeEnumEntry(attrValueEntry);
774             auto [pvHdls,
775                   defIndex] = table::attribute::decodeEnumEntry(attrEntry);
776             if (!(value.size() == 1))
777             {
778                 return PLDM_ERROR_INVALID_LENGTH;
779             }
780             if (value[0] >= pvHdls.size())
781             {
782                 error("Enum: Illgeal index, Index = {ATTR_INDEX}", "ATTR_INDEX",
783                       (int)value[0]);
784                 return PLDM_ERROR_INVALID_DATA;
785             }
786             return PLDM_SUCCESS;
787         }
788         case PLDM_BIOS_INTEGER:
789         case PLDM_BIOS_INTEGER_READ_ONLY:
790         {
791             auto value =
792                 table::attribute_value::decodeIntegerEntry(attrValueEntry);
793             auto [lower, upper, scalar,
794                   def] = table::attribute::decodeIntegerEntry(attrEntry);
795 
796             if (value < lower || value > upper)
797             {
798                 error("Integer: out of bound, value = {ATTR_VALUE}",
799                       "ATTR_VALUE", value);
800                 return PLDM_ERROR_INVALID_DATA;
801             }
802             return PLDM_SUCCESS;
803         }
804         case PLDM_BIOS_STRING:
805         case PLDM_BIOS_STRING_READ_ONLY:
806         {
807             auto stringConf = table::attribute::decodeStringEntry(attrEntry);
808             auto value =
809                 table::attribute_value::decodeStringEntry(attrValueEntry);
810             if (value.size() < stringConf.minLength ||
811                 value.size() > stringConf.maxLength)
812             {
813                 error(
814                     "String: Length error, string = {ATTR_VALUE} length {LEN}",
815                     "ATTR_VALUE", value, "LEN", value.size());
816                 return PLDM_ERROR_INVALID_LENGTH;
817             }
818             return PLDM_SUCCESS;
819         }
820         default:
821             error("ReadOnly or Unspported type, type = {ATTR_TYPE}",
822                   "ATTR_TYPE", attrType);
823             return PLDM_ERROR;
824     };
825 }
826 
827 int BIOSConfig::setAttrValue(const void* entry, size_t size, bool isBMC,
828                              bool updateDBus, bool updateBaseBIOSTable)
829 {
830     auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
831     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
832     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
833     if (!attrValueTable || !attrTable || !stringTable)
834     {
835         return PLDM_BIOS_TABLE_UNAVAILABLE;
836     }
837 
838     auto attrValueEntry =
839         reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry);
840 
841     auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry);
842 
843     auto attrEntry = table::attribute::findByHandle(*attrTable,
844                                                     attrValHeader.attrHandle);
845     if (!attrEntry)
846     {
847         return PLDM_ERROR;
848     }
849 
850     auto rc = checkAttrValueToUpdate(attrValueEntry, attrEntry, *stringTable);
851     if (rc != PLDM_SUCCESS)
852     {
853         return rc;
854     }
855 
856     auto destTable = table::attribute_value::updateTable(*attrValueTable, entry,
857                                                          size);
858 
859     if (!destTable)
860     {
861         return PLDM_ERROR;
862     }
863 
864     try
865     {
866         auto attrHeader = table::attribute::decodeHeader(attrEntry);
867 
868         BIOSStringTable biosStringTable(*stringTable);
869         auto attrName = biosStringTable.findString(attrHeader.stringHandle);
870         auto iter = std::find_if(
871             biosAttributes.begin(), biosAttributes.end(),
872             [&attrName](const auto& attr) { return attr->name == attrName; });
873 
874         if (iter == biosAttributes.end())
875         {
876             return PLDM_ERROR;
877         }
878         if (updateDBus)
879         {
880             (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry,
881                                         biosStringTable);
882         }
883     }
884     catch (const std::exception& e)
885     {
886         error("Set attribute value error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
887         return PLDM_ERROR;
888     }
889 
890     setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, *destTable, updateBaseBIOSTable);
891 
892     traceBIOSUpdate(attrValueEntry, attrEntry, isBMC);
893 
894     return PLDM_SUCCESS;
895 }
896 
897 void BIOSConfig::removeTables()
898 {
899     try
900     {
901         fs::remove(tableDir / stringTableFile);
902         fs::remove(tableDir / attrTableFile);
903         fs::remove(tableDir / attrValueTableFile);
904     }
905     catch (const std::exception& e)
906     {
907         error("Remove the tables error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
908     }
909 }
910 
911 void BIOSConfig::processBiosAttrChangeNotification(
912     const DbusChObjProperties& chProperties, uint32_t biosAttrIndex)
913 {
914     const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap();
915     const auto& propertyName = dBusMap->propertyName;
916     const auto& attrName = biosAttributes[biosAttrIndex]->name;
917 
918     const auto it = chProperties.find(propertyName);
919     if (it == chProperties.end())
920     {
921         return;
922     }
923 
924     PropertyValue newPropVal = it->second;
925     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
926     if (!stringTable.has_value())
927     {
928         error("BIOS string table unavailable");
929         return;
930     }
931     BIOSStringTable biosStringTable(*stringTable);
932     uint16_t attrNameHdl{};
933     try
934     {
935         attrNameHdl = biosStringTable.findHandle(attrName);
936     }
937     catch (const std::invalid_argument& e)
938     {
939         error("Could not find handle for BIOS string, ATTRIBUTE={ATTR_NAME}",
940               "ATTR_NAME", attrName.c_str());
941         return;
942     }
943 
944     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
945     if (!attrTable.has_value())
946     {
947         error("Attribute table not present");
948         return;
949     }
950     const struct pldm_bios_attr_table_entry* tableEntry =
951         table::attribute::findByStringHandle(*attrTable, attrNameHdl);
952     if (tableEntry == nullptr)
953     {
954         error(
955             "Attribute not found in attribute table, name= {ATTR_NAME} name handle={ATTR_HANDLE}",
956             "ATTR_NAME", attrName.c_str(), "ATTR_HANDLE", attrNameHdl);
957         return;
958     }
959 
960     auto [attrHdl, attrType,
961           stringHdl] = table::attribute::decodeHeader(tableEntry);
962 
963     auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
964 
965     if (!attrValueSrcTable.has_value())
966     {
967         error("Attribute value table not present");
968         return;
969     }
970 
971     Table newValue;
972     auto rc = biosAttributes[biosAttrIndex]->updateAttrVal(
973         newValue, attrHdl, attrType, newPropVal);
974     if (rc != PLDM_SUCCESS)
975     {
976         error(
977             "Could not update the attribute value table for attribute handle={ATTR_HANDLE} and type={ATTR_TYPE}",
978             "ATTR_HANDLE", attrHdl, "ATTR_TYPE", (uint32_t)attrType);
979         return;
980     }
981     auto destTable = table::attribute_value::updateTable(
982         *attrValueSrcTable, newValue.data(), newValue.size());
983     if (destTable.has_value())
984     {
985         storeTable(tableDir / attrValueTableFile, *destTable);
986     }
987 
988     rc = setAttrValue(newValue.data(), newValue.size(), true, false);
989     if (rc != PLDM_SUCCESS)
990     {
991         error("could not setAttrValue on base bios table and dbus, rc = {RC}",
992               "RC", rc);
993     }
994 }
995 
996 uint16_t BIOSConfig::findAttrHandle(const std::string& attrName)
997 {
998     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
999     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
1000 
1001     BIOSStringTable biosStringTable(*stringTable);
1002     pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(
1003         attrTable->data(), attrTable->size());
1004     auto stringHandle = biosStringTable.findHandle(attrName);
1005 
1006     for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(
1007              attrTable->data(), attrTable->size()))
1008     {
1009         auto header = table::attribute::decodeHeader(entry);
1010         if (header.stringHandle == stringHandle)
1011         {
1012             return header.attrHandle;
1013         }
1014     }
1015 
1016     throw std::invalid_argument("Unknow attribute Name");
1017 }
1018 
1019 void BIOSConfig::constructPendingAttribute(
1020     const PendingAttributes& pendingAttributes)
1021 {
1022     std::vector<uint16_t> listOfHandles{};
1023 
1024     for (auto& attribute : pendingAttributes)
1025     {
1026         std::string attributeName = attribute.first;
1027         auto& [attributeType, attributevalue] = attribute.second;
1028 
1029         auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(),
1030                                  [&attributeName](const auto& attr) {
1031             return attr->name == attributeName;
1032         });
1033 
1034         if (iter == biosAttributes.end())
1035         {
1036             error("Wrong attribute name, attributeName = {ATTR_NAME}",
1037                   "ATTR_NAME", attributeName);
1038             continue;
1039         }
1040 
1041         Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0);
1042         auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
1043             attrValueEntry.data());
1044 
1045         auto handler = findAttrHandle(attributeName);
1046         auto type =
1047             BIOSConfigManager::convertAttributeTypeFromString(attributeType);
1048 
1049         if (type != BIOSConfigManager::AttributeType::Enumeration &&
1050             type != BIOSConfigManager::AttributeType::String &&
1051             type != BIOSConfigManager::AttributeType::Integer)
1052         {
1053             error("Attribute type not supported, attributeType = {ATTR_TYPE}",
1054                   "ATTR_TYPE", attributeType);
1055             continue;
1056         }
1057 
1058         const auto [attrType, readonlyStatus, displayName, description,
1059                     menuPath, currentValue, defaultValue,
1060                     option] = baseBIOSTableMaps.at(attributeName);
1061 
1062         entry->attr_handle = htole16(handler);
1063 
1064         // Need to verify that the current value has really changed
1065         if (attributeType == attrType && attributevalue != currentValue)
1066         {
1067             listOfHandles.emplace_back(htole16(handler));
1068         }
1069 
1070         (*iter)->generateAttributeEntry(attributevalue, attrValueEntry);
1071 
1072         setAttrValue(attrValueEntry.data(), attrValueEntry.size(), true);
1073     }
1074 
1075     if (listOfHandles.size())
1076     {
1077 #ifdef OEM_IBM
1078         auto rc = pldm::responder::platform::sendBiosAttributeUpdateEvent(
1079             eid, instanceIdDb, listOfHandles, handler);
1080         if (rc != PLDM_SUCCESS)
1081         {
1082             return;
1083         }
1084 #endif
1085     }
1086 }
1087 
1088 void BIOSConfig::listenPendingAttributes()
1089 {
1090     constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager";
1091     constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager";
1092 
1093     using namespace sdbusplus::bus::match::rules;
1094     auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>(
1095         pldm::utils::DBusHandler::getBus(),
1096         propertiesChanged(objPath, objInterface),
1097         [this](sdbusplus::message_t& msg) {
1098         constexpr auto propertyName = "PendingAttributes";
1099 
1100         using Value =
1101             std::variant<std::string, PendingAttributes, BaseBIOSTable>;
1102         using Properties = std::map<DbusProp, Value>;
1103 
1104         Properties props{};
1105         std::string intf;
1106         msg.read(intf, props);
1107 
1108         auto valPropMap = props.find(propertyName);
1109         if (valPropMap == props.end())
1110         {
1111             return;
1112         }
1113 
1114         PendingAttributes pendingAttributes =
1115             std::get<PendingAttributes>(valPropMap->second);
1116         this->constructPendingAttribute(pendingAttributes);
1117     });
1118 
1119     biosAttrMatch.emplace_back(std::move(updateBIOSMatch));
1120 }
1121 
1122 } // namespace bios
1123 } // namespace responder
1124 } // namespace pldm
1125