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