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