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