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