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