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