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