xref: /openbmc/pldm/libpldmresponder/bios_config.cpp (revision 2abbce76d005520fe066ae45828d686fca30366d)
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(biosAttributes.begin(), biosAttributes.end(),
871                                  [&attrName](const auto& attr) {
872             return attr->name == attrName;
873         });
874 
875         if (iter == biosAttributes.end())
876         {
877             return PLDM_ERROR;
878         }
879         if (updateDBus)
880         {
881             (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry,
882                                         biosStringTable);
883         }
884     }
885     catch (const std::exception& e)
886     {
887         error("Set attribute value error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
888         return PLDM_ERROR;
889     }
890 
891     setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, *destTable, updateBaseBIOSTable);
892 
893     traceBIOSUpdate(attrValueEntry, attrEntry, isBMC);
894 
895     return PLDM_SUCCESS;
896 }
897 
898 void BIOSConfig::removeTables()
899 {
900     try
901     {
902         fs::remove(tableDir / stringTableFile);
903         fs::remove(tableDir / attrTableFile);
904         fs::remove(tableDir / attrValueTableFile);
905     }
906     catch (const std::exception& e)
907     {
908         error("Remove the tables error: {ERR_EXCEP}", "ERR_EXCEP", e.what());
909     }
910 }
911 
912 void BIOSConfig::processBiosAttrChangeNotification(
913     const DbusChObjProperties& chProperties, uint32_t biosAttrIndex)
914 {
915     const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap();
916     const auto& propertyName = dBusMap->propertyName;
917     const auto& attrName = biosAttributes[biosAttrIndex]->name;
918 
919     const auto it = chProperties.find(propertyName);
920     if (it == chProperties.end())
921     {
922         return;
923     }
924 
925     PropertyValue newPropVal = it->second;
926     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
927     if (!stringTable.has_value())
928     {
929         error("BIOS string table unavailable");
930         return;
931     }
932     BIOSStringTable biosStringTable(*stringTable);
933     uint16_t attrNameHdl{};
934     try
935     {
936         attrNameHdl = biosStringTable.findHandle(attrName);
937     }
938     catch (const std::invalid_argument& e)
939     {
940         error("Could not find handle for BIOS string, ATTRIBUTE={ATTR_NAME}",
941               "ATTR_NAME", attrName.c_str());
942         return;
943     }
944 
945     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
946     if (!attrTable.has_value())
947     {
948         error("Attribute table not present");
949         return;
950     }
951     const struct pldm_bios_attr_table_entry* tableEntry =
952         table::attribute::findByStringHandle(*attrTable, attrNameHdl);
953     if (tableEntry == nullptr)
954     {
955         error(
956             "Attribute not found in attribute table, name= {ATTR_NAME} name handle={ATTR_HANDLE}",
957             "ATTR_NAME", attrName.c_str(), "ATTR_HANDLE", attrNameHdl);
958         return;
959     }
960 
961     auto [attrHdl, attrType,
962           stringHdl] = table::attribute::decodeHeader(tableEntry);
963 
964     auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
965 
966     if (!attrValueSrcTable.has_value())
967     {
968         error("Attribute value table not present");
969         return;
970     }
971 
972     Table newValue;
973     auto rc = biosAttributes[biosAttrIndex]->updateAttrVal(
974         newValue, attrHdl, attrType, newPropVal);
975     if (rc != PLDM_SUCCESS)
976     {
977         error(
978             "Could not update the attribute value table for attribute handle={ATTR_HANDLE} and type={ATTR_TYPE}",
979             "ATTR_HANDLE", attrHdl, "ATTR_TYPE", (uint32_t)attrType);
980         return;
981     }
982     auto destTable = table::attribute_value::updateTable(
983         *attrValueSrcTable, newValue.data(), newValue.size());
984     if (destTable.has_value())
985     {
986         storeTable(tableDir / attrValueTableFile, *destTable);
987     }
988 
989     rc = setAttrValue(newValue.data(), newValue.size(), true, false);
990     if (rc != PLDM_SUCCESS)
991     {
992         error("could not setAttrValue on base bios table and dbus, rc = {RC}",
993               "RC", rc);
994     }
995 }
996 
997 uint16_t BIOSConfig::findAttrHandle(const std::string& attrName)
998 {
999     auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
1000     auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
1001 
1002     BIOSStringTable biosStringTable(*stringTable);
1003     pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(
1004         attrTable->data(), attrTable->size());
1005     auto stringHandle = biosStringTable.findHandle(attrName);
1006 
1007     for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(
1008              attrTable->data(), attrTable->size()))
1009     {
1010         auto header = table::attribute::decodeHeader(entry);
1011         if (header.stringHandle == stringHandle)
1012         {
1013             return header.attrHandle;
1014         }
1015     }
1016 
1017     throw std::invalid_argument("Unknow attribute Name");
1018 }
1019 
1020 void BIOSConfig::constructPendingAttribute(
1021     const PendingAttributes& pendingAttributes)
1022 {
1023     std::vector<uint16_t> listOfHandles{};
1024 
1025     for (auto& attribute : pendingAttributes)
1026     {
1027         std::string attributeName = attribute.first;
1028         auto& [attributeType, attributevalue] = attribute.second;
1029 
1030         auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(),
1031                                  [&attributeName](const auto& attr) {
1032             return attr->name == attributeName;
1033         });
1034 
1035         if (iter == biosAttributes.end())
1036         {
1037             error("Wrong attribute name, attributeName = {ATTR_NAME}",
1038                   "ATTR_NAME", attributeName);
1039             continue;
1040         }
1041 
1042         Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0);
1043         auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
1044             attrValueEntry.data());
1045 
1046         auto handler = findAttrHandle(attributeName);
1047         auto type =
1048             BIOSConfigManager::convertAttributeTypeFromString(attributeType);
1049 
1050         if (type != BIOSConfigManager::AttributeType::Enumeration &&
1051             type != BIOSConfigManager::AttributeType::String &&
1052             type != BIOSConfigManager::AttributeType::Integer)
1053         {
1054             error("Attribute type not supported, attributeType = {ATTR_TYPE}",
1055                   "ATTR_TYPE", attributeType);
1056             continue;
1057         }
1058 
1059         const auto [attrType, readonlyStatus, displayName, description,
1060                     menuPath, currentValue, defaultValue,
1061                     option] = baseBIOSTableMaps.at(attributeName);
1062 
1063         entry->attr_handle = htole16(handler);
1064 
1065         // Need to verify that the current value has really changed
1066         if (attributeType == attrType && attributevalue != currentValue)
1067         {
1068             listOfHandles.emplace_back(htole16(handler));
1069         }
1070 
1071         (*iter)->generateAttributeEntry(attributevalue, attrValueEntry);
1072 
1073         setAttrValue(attrValueEntry.data(), attrValueEntry.size(), true);
1074     }
1075 
1076     if (listOfHandles.size())
1077     {
1078 #ifdef OEM_IBM
1079         auto rc = pldm::responder::platform::sendBiosAttributeUpdateEvent(
1080             eid, instanceIdDb, listOfHandles, handler);
1081         if (rc != PLDM_SUCCESS)
1082         {
1083             return;
1084         }
1085 #endif
1086     }
1087 }
1088 
1089 void BIOSConfig::listenPendingAttributes()
1090 {
1091     constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager";
1092     constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager";
1093 
1094     using namespace sdbusplus::bus::match::rules;
1095     auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>(
1096         pldm::utils::DBusHandler::getBus(),
1097         propertiesChanged(objPath, objInterface),
1098         [this](sdbusplus::message_t& msg) {
1099         constexpr auto propertyName = "PendingAttributes";
1100 
1101         using Value =
1102             std::variant<std::string, PendingAttributes, BaseBIOSTable>;
1103         using Properties = std::map<DbusProp, Value>;
1104 
1105         Properties props{};
1106         std::string intf;
1107         msg.read(intf, props);
1108 
1109         auto valPropMap = props.find(propertyName);
1110         if (valPropMap == props.end())
1111         {
1112             return;
1113         }
1114 
1115         PendingAttributes pendingAttributes =
1116             std::get<PendingAttributes>(valPropMap->second);
1117         this->constructPendingAttribute(pendingAttributes);
1118         });
1119 
1120     biosAttrMatch.emplace_back(std::move(updateBIOSMatch));
1121 }
1122 
1123 } // namespace bios
1124 } // namespace responder
1125 } // namespace pldm
1126