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, ¤tString);
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 method.append(biosConfigInterface, biosConfigPropertyName, value);
526 bus.call_noreply(method, dbusTimeout);
527 }
528 catch (const std::exception& e)
529 {
530 error("Failed to update BaseBIOSTable property, error - {ERROR}",
531 "ERROR", e);
532 }
533 }
534
constructAttributes()535 void BIOSConfig::constructAttributes()
536 {
537 info("Bios Attribute file path: {PATH}", "PATH",
538 (jsonDir / sysType / attributesJsonFile));
539 load(jsonDir / sysType / attributesJsonFile, [this](const Json& entry) {
540 std::string attrType = entry.at("attribute_type");
541 if (attrType == "string")
542 {
543 constructAttribute<BIOSStringAttribute>(entry);
544 }
545 else if (attrType == "integer")
546 {
547 constructAttribute<BIOSIntegerAttribute>(entry);
548 }
549 else if (attrType == "enum")
550 {
551 constructAttribute<BIOSEnumAttribute>(entry);
552 }
553 });
554 }
555
buildAndStoreAttrTables(const Table & stringTable)556 void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable)
557 {
558 BIOSStringTable biosStringTable(stringTable);
559
560 if (biosAttributes.empty())
561 {
562 return;
563 }
564
565 BaseBIOSTable biosTable{};
566 constexpr auto biosObjPath = "/xyz/openbmc_project/bios_config/manager";
567 constexpr auto biosInterface = "xyz.openbmc_project.BIOSConfig.Manager";
568
569 try
570 {
571 auto& bus = dbusHandler->getBus();
572 auto service = dbusHandler->getService(biosObjPath, biosInterface);
573 auto method =
574 bus.new_method_call(service.c_str(), biosObjPath,
575 "org.freedesktop.DBus.Properties", "Get");
576 method.append(biosInterface, "BaseBIOSTable");
577 auto reply = bus.call(method, dbusTimeout);
578 std::variant<BaseBIOSTable> varBiosTable{};
579 reply.read(varBiosTable);
580 biosTable = std::get<BaseBIOSTable>(varBiosTable);
581 }
582 // Failed to read the BaseBIOSTable, so update the BaseBIOSTable with the
583 // default values populated from the BIOS JSONs to keep PLDM and
584 // bios-settings-manager in sync
585 catch (const std::exception& e)
586 {
587 error("Failed to read BaseBIOSTable property, error - {ERROR}", "ERROR",
588 e);
589 }
590
591 Table attrTable, attrValueTable;
592
593 for (auto& attr : biosAttributes)
594 {
595 try
596 {
597 auto iter = biosTable.find(attr->name);
598 if (iter == biosTable.end())
599 {
600 attr->constructEntry(biosStringTable, attrTable, attrValueTable,
601 std::nullopt);
602 }
603 else
604 {
605 attr->constructEntry(
606 biosStringTable, attrTable, attrValueTable,
607 std::get<static_cast<uint8_t>(Index::currentValue)>(
608 iter->second));
609 }
610 }
611 catch (const std::exception& e)
612 {
613 error(
614 "Failed to construct table entry for attribute '{ATTRIBUTE}', error - {ERROR}",
615 "ATTRIBUTE", attr->name, "ERROR", e);
616 }
617 }
618
619 table::appendPadAndChecksum(attrTable);
620 table::appendPadAndChecksum(attrValueTable);
621 setBIOSTable(PLDM_BIOS_ATTR_TABLE, attrTable);
622 setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, attrValueTable);
623 }
624
buildAndStoreStringTable()625 std::optional<Table> BIOSConfig::buildAndStoreStringTable()
626 {
627 std::set<std::string> strings;
628 load(jsonDir / sysType / attributesJsonFile, [&strings](const Json& entry) {
629 if (entry.at("attribute_type") == "enum")
630 {
631 strings.emplace(entry.at("attribute_name"));
632 auto possibleValues = entry.at("possible_values");
633 for (auto& pv : possibleValues)
634 {
635 strings.emplace(pv);
636 }
637 }
638 else
639 {
640 strings.emplace(entry.at("attribute_name"));
641 }
642 });
643
644 if (strings.empty())
645 {
646 return std::nullopt;
647 }
648
649 Table table;
650 for (const auto& elem : strings)
651 {
652 table::string::constructEntry(table, elem);
653 }
654
655 table::appendPadAndChecksum(table);
656 setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
657 return table;
658 }
659
storeTable(const fs::path & path,const Table & table)660 void BIOSConfig::storeTable(const fs::path& path, const Table& table)
661 {
662 BIOSTable biosTable(path.c_str());
663 biosTable.store(table);
664 }
665
loadTable(const fs::path & path)666 std::optional<Table> BIOSConfig::loadTable(const fs::path& path)
667 {
668 BIOSTable biosTable(path.c_str());
669 if (biosTable.isEmpty())
670 {
671 return std::nullopt;
672 }
673
674 Table table;
675 biosTable.load(table);
676 return table;
677 }
678
load(const fs::path & filePath,ParseHandler handler)679 void BIOSConfig::load(const fs::path& filePath, ParseHandler handler)
680 {
681 std::ifstream file;
682 Json jsonConf;
683 if (fs::exists(filePath))
684 {
685 try
686 {
687 file.open(filePath);
688 jsonConf = Json::parse(file);
689 auto entries = jsonConf.at("entries");
690 for (auto& entry : entries)
691 {
692 try
693 {
694 handler(entry);
695 }
696 catch (const std::exception& e)
697 {
698 error(
699 "Failed to parse JSON config file at path '{PATH}', error - {ERROR}",
700 "PATH", filePath, "ERROR", e);
701 }
702 }
703 }
704 catch (const std::exception& e)
705 {
706 error("Failed to parse JSON config file '{PATH}', error - {ERROR}",
707 "PATH", filePath, "ERROR", e);
708 }
709 }
710 }
711
decodeStringFromStringEntry(const pldm_bios_string_table_entry * stringEntry)712 std::string BIOSConfig::decodeStringFromStringEntry(
713 const pldm_bios_string_table_entry* stringEntry)
714 {
715 auto strLength =
716 pldm_bios_table_string_entry_decode_string_length(stringEntry);
717 std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
718 // Preconditions are upheld therefore no error check necessary
719 pldm_bios_table_string_entry_decode_string(stringEntry, buffer.data(),
720 buffer.size());
721 return std::string(buffer.data(), buffer.data() + strLength);
722 }
723
displayStringHandle(uint16_t handle,uint8_t index,const std::optional<Table> & attrTable,const std::optional<Table> & stringTable)724 std::string BIOSConfig::displayStringHandle(
725 uint16_t handle, uint8_t index, 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(attrEntry, &pvNum);
732 if (rc != PLDM_SUCCESS)
733 {
734 error(
735 "Failed to decode BIOS table possible values for attribute entry, response code '{RC}'",
736 "RC", rc);
737 throw std::runtime_error(
738 "Failed to decode BIOS table possible values for attribute entry");
739 }
740
741 std::vector<uint16_t> pvHandls(pvNum);
742 // Preconditions are upheld therefore no error check necessary
743 pldm_bios_table_attr_entry_enum_decode_pv_hdls(attrEntry, pvHandls.data(),
744 pvHandls.size());
745
746 std::string displayString = std::to_string(pvHandls[index]);
747
748 auto stringEntry = pldm_bios_table_string_find_by_handle(
749 stringTable->data(), stringTable->size(), pvHandls[index]);
750
751 auto decodedStr = decodeStringFromStringEntry(stringEntry);
752
753 return decodedStr + "(" + displayString + ")";
754 }
755
traceBIOSUpdate(const pldm_bios_attr_val_table_entry * attrValueEntry,const pldm_bios_attr_table_entry * attrEntry,bool isBMC)756 void BIOSConfig::traceBIOSUpdate(
757 const pldm_bios_attr_val_table_entry* attrValueEntry,
758 const pldm_bios_attr_table_entry* attrEntry, bool isBMC)
759 {
760 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
761 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
762
763 auto [attrHandle,
764 attrType] = table::attribute_value::decodeHeader(attrValueEntry);
765
766 auto attrHeader = table::attribute::decodeHeader(attrEntry);
767 BIOSStringTable biosStringTable(*stringTable);
768 auto attrName = biosStringTable.findString(attrHeader.stringHandle);
769
770 switch (attrType)
771 {
772 case PLDM_BIOS_ENUMERATION:
773 case PLDM_BIOS_ENUMERATION_READ_ONLY:
774 {
775 auto count = pldm_bios_table_attr_value_entry_enum_decode_number(
776 attrValueEntry);
777 std::vector<uint8_t> handles(count);
778 pldm_bios_table_attr_value_entry_enum_decode_handles(
779 attrValueEntry, handles.data(), handles.size());
780
781 for (uint8_t handle : handles)
782 {
783 auto nwVal = displayStringHandle(attrHandle, handle, attrTable,
784 stringTable);
785 auto chkBMC = isBMC ? "true" : "false";
786 info(
787 "BIOS attribute '{ATTRIBUTE}' updated to value '{VALUE}' by BMC '{CHECK_BMC}'",
788 "ATTRIBUTE", attrName, "VALUE", nwVal, "CHECK_BMC", chkBMC);
789 }
790 break;
791 }
792 case PLDM_BIOS_INTEGER:
793 case PLDM_BIOS_INTEGER_READ_ONLY:
794 {
795 auto value =
796 table::attribute_value::decodeIntegerEntry(attrValueEntry);
797 auto chkBMC = isBMC ? "true" : "false";
798 info(
799 "BIOS attribute '{ATTRIBUTE}' updated to value '{VALUE}' by BMC '{CHECK_BMC}'",
800 "ATTRIBUTE", attrName, "VALUE", value, "CHECK_BMC", chkBMC);
801 break;
802 }
803 case PLDM_BIOS_STRING:
804 case PLDM_BIOS_STRING_READ_ONLY:
805 {
806 auto value =
807 table::attribute_value::decodeStringEntry(attrValueEntry);
808 auto chkBMC = isBMC ? "true" : "false";
809 info(
810 "BIOS attribute '{ATTRIBUTE}' updated to value '{VALUE}' by BMC '{CHECK_BMC}'",
811 "ATTRIBUTE", attrName, "VALUE", value, "CHECK_BMC", chkBMC);
812 break;
813 }
814 default:
815 break;
816 };
817 }
818
checkAttrValueToUpdate(const pldm_bios_attr_val_table_entry * attrValueEntry,const pldm_bios_attr_table_entry * attrEntry,Table &)819 int BIOSConfig::checkAttrValueToUpdate(
820 const pldm_bios_attr_val_table_entry* attrValueEntry,
821 const pldm_bios_attr_table_entry* attrEntry, Table&)
822
823 {
824 auto [attrHandle,
825 attrType] = table::attribute_value::decodeHeader(attrValueEntry);
826
827 switch (attrType)
828 {
829 case PLDM_BIOS_ENUMERATION:
830 case PLDM_BIOS_ENUMERATION_READ_ONLY:
831 {
832 auto value =
833 table::attribute_value::decodeEnumEntry(attrValueEntry);
834 auto [pvHdls,
835 defIndex] = table::attribute::decodeEnumEntry(attrEntry);
836 if (!(value.size() == 1))
837 {
838 return PLDM_ERROR_INVALID_LENGTH;
839 }
840 if (value[0] >= pvHdls.size())
841 {
842 error(
843 "Invalid index '{INDEX}' encountered for Enum type BIOS attribute",
844 "INDEX", value[0]);
845 return PLDM_ERROR_INVALID_DATA;
846 }
847 return PLDM_SUCCESS;
848 }
849 case PLDM_BIOS_INTEGER:
850 case PLDM_BIOS_INTEGER_READ_ONLY:
851 {
852 auto value =
853 table::attribute_value::decodeIntegerEntry(attrValueEntry);
854 auto [lower, upper, scalar,
855 def] = table::attribute::decodeIntegerEntry(attrEntry);
856
857 if (value < lower || value > upper)
858 {
859 error(
860 "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}'.",
861 "ATTRIBUTE_VALUE", value, "LOWER", lower, "UPPER", upper,
862 "SCALAR", scalar);
863 return PLDM_ERROR_INVALID_DATA;
864 }
865 return PLDM_SUCCESS;
866 }
867 case PLDM_BIOS_STRING:
868 case PLDM_BIOS_STRING_READ_ONLY:
869 {
870 auto stringConf = table::attribute::decodeStringEntry(attrEntry);
871 auto value =
872 table::attribute_value::decodeStringEntry(attrValueEntry);
873 if (value.size() < stringConf.minLength ||
874 value.size() > stringConf.maxLength)
875 {
876 error(
877 "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}'",
878 "ATTRIBUTE_VALUE", value, "LENGTH", value.size(), "MIN_LEN",
879 stringConf.minLength, "MAX_LEN", stringConf.maxLength);
880 return PLDM_ERROR_INVALID_LENGTH;
881 }
882 return PLDM_SUCCESS;
883 }
884 default:
885 error("ReadOnly or Unsupported type '{TYPE}'", "TYPE", attrType);
886 return PLDM_ERROR;
887 };
888 }
889
setAttrValue(const void * entry,size_t size,bool isBMC,bool updateDBus,bool updateBaseBIOSTable)890 int BIOSConfig::setAttrValue(const void* entry, size_t size, bool isBMC,
891 bool updateDBus, bool updateBaseBIOSTable)
892 {
893 auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
894 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
895 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
896 if (!attrValueTable || !attrTable || !stringTable)
897 {
898 return PLDM_BIOS_TABLE_UNAVAILABLE;
899 }
900
901 auto attrValueEntry =
902 reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry);
903
904 auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry);
905
906 auto attrEntry =
907 table::attribute::findByHandle(*attrTable, attrValHeader.attrHandle);
908 if (!attrEntry)
909 {
910 return PLDM_ERROR;
911 }
912
913 auto rc = checkAttrValueToUpdate(attrValueEntry, attrEntry, *stringTable);
914 if (rc != PLDM_SUCCESS)
915 {
916 return rc;
917 }
918
919 auto destTable =
920 table::attribute_value::updateTable(*attrValueTable, entry, size);
921
922 if (!destTable)
923 {
924 return PLDM_ERROR;
925 }
926
927 try
928 {
929 auto attrHeader = table::attribute::decodeHeader(attrEntry);
930
931 BIOSStringTable biosStringTable(*stringTable);
932 auto attrName = biosStringTable.findString(attrHeader.stringHandle);
933 auto iter = std::find_if(
934 biosAttributes.begin(), biosAttributes.end(),
935 [&attrName](const auto& attr) { return attr->name == attrName; });
936
937 if (iter == biosAttributes.end())
938 {
939 return PLDM_ERROR;
940 }
941 if (updateDBus)
942 {
943 (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry,
944 biosStringTable);
945 }
946 }
947 catch (const std::exception& e)
948 {
949 error("Set attribute value error - {ERROR}", "ERROR", e);
950 return PLDM_ERROR;
951 }
952
953 setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, *destTable, updateBaseBIOSTable);
954
955 traceBIOSUpdate(attrValueEntry, attrEntry, isBMC);
956
957 return PLDM_SUCCESS;
958 }
959
removeTables()960 void BIOSConfig::removeTables()
961 {
962 try
963 {
964 fs::remove(tableDir / stringTableFile);
965 fs::remove(tableDir / attrTableFile);
966 fs::remove(tableDir / attrValueTableFile);
967 }
968 catch (const std::exception& e)
969 {
970 error("Remove the tables error - {ERROR}", "ERROR", e);
971 }
972 }
973
processBiosAttrChangeNotification(const DbusChObjProperties & chProperties,uint32_t biosAttrIndex)974 void BIOSConfig::processBiosAttrChangeNotification(
975 const DbusChObjProperties& chProperties, uint32_t biosAttrIndex)
976 {
977 const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap();
978 const auto& propertyName = dBusMap->propertyName;
979 const auto& attrName = biosAttributes[biosAttrIndex]->name;
980
981 const auto it = chProperties.find(propertyName);
982 if (it == chProperties.end())
983 {
984 return;
985 }
986
987 PropertyValue newPropVal = it->second;
988 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
989 if (!stringTable.has_value())
990 {
991 error("BIOS string table unavailable");
992 return;
993 }
994 BIOSStringTable biosStringTable(*stringTable);
995 uint16_t attrNameHdl{};
996 try
997 {
998 attrNameHdl = biosStringTable.findHandle(attrName);
999 }
1000 catch (const std::invalid_argument& e)
1001 {
1002 error(
1003 "Missing handle for attribute '{ATTRIBUTE}' in BIOS String Table, error - '{ERROR}'",
1004 "ATTRIBUTE", attrName, "ERROR", e);
1005 return;
1006 }
1007
1008 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
1009 if (!attrTable.has_value())
1010 {
1011 error("BIOS Attribute table not present");
1012 return;
1013 }
1014 const struct pldm_bios_attr_table_entry* tableEntry =
1015 table::attribute::findByStringHandle(*attrTable, attrNameHdl);
1016 if (tableEntry == nullptr)
1017 {
1018 error(
1019 "Failed to find attribute {ATTRIBUTE} in BIOS Attribute table with attribute handle '{ATTR_HANDLE}'",
1020 "ATTRIBUTE", attrName, "ATTR_HANDLE", attrNameHdl);
1021 return;
1022 }
1023
1024 auto [attrHdl, attrType,
1025 stringHdl] = table::attribute::decodeHeader(tableEntry);
1026
1027 auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
1028
1029 if (!attrValueSrcTable.has_value())
1030 {
1031 error("Attribute value table not present");
1032 return;
1033 }
1034
1035 Table newValue;
1036 auto rc = biosAttributes[biosAttrIndex]->updateAttrVal(
1037 newValue, attrHdl, attrType, newPropVal);
1038 if (rc != PLDM_SUCCESS)
1039 {
1040 error(
1041 "Failed to update the attribute value table for attribute handle '{ATTR_HANDLE}' and attribute type '{TYPE}'",
1042 "ATTR_HANDLE", attrHdl, "TYPE", attrType);
1043 return;
1044 }
1045 auto destTable = table::attribute_value::updateTable(
1046 *attrValueSrcTable, newValue.data(), newValue.size());
1047 if (destTable.has_value())
1048 {
1049 storeTable(tableDir / attrValueTableFile, *destTable);
1050 }
1051
1052 rc = setAttrValue(newValue.data(), newValue.size(), true, false);
1053 if (rc != PLDM_SUCCESS)
1054 {
1055 error(
1056 "Failed to setAttrValue on base bios table and dbus, response code '{RC}'",
1057 "RC", rc);
1058 }
1059 }
1060
findAttrHandle(const std::string & attrName)1061 uint16_t BIOSConfig::findAttrHandle(const std::string& attrName)
1062 {
1063 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
1064 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
1065
1066 BIOSStringTable biosStringTable(*stringTable);
1067 pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(
1068 attrTable->data(), attrTable->size());
1069 auto stringHandle = biosStringTable.findHandle(attrName);
1070
1071 for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(
1072 attrTable->data(), attrTable->size()))
1073 {
1074 auto header = table::attribute::decodeHeader(entry);
1075 if (header.stringHandle == stringHandle)
1076 {
1077 return header.attrHandle;
1078 }
1079 }
1080
1081 throw std::invalid_argument("Unknown attribute Name");
1082 }
1083
constructPendingAttribute(const PendingAttributes & pendingAttributes)1084 void BIOSConfig::constructPendingAttribute(
1085 const PendingAttributes& pendingAttributes)
1086 {
1087 std::vector<uint16_t> listOfHandles{};
1088
1089 for (auto& attribute : pendingAttributes)
1090 {
1091 std::string attributeName = attribute.first;
1092 auto& [attributeType, attributevalue] = attribute.second;
1093
1094 auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(),
1095 [&attributeName](const auto& attr) {
1096 return attr->name == attributeName;
1097 });
1098
1099 if (iter == biosAttributes.end())
1100 {
1101 error("Wrong attribute name {NAME}", "NAME", attributeName);
1102 continue;
1103 }
1104
1105 Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0);
1106 auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
1107 attrValueEntry.data());
1108
1109 auto handler = findAttrHandle(attributeName);
1110 auto type =
1111 BIOSConfigManager::convertAttributeTypeFromString(attributeType);
1112
1113 if (type != BIOSConfigManager::AttributeType::Enumeration &&
1114 type != BIOSConfigManager::AttributeType::String &&
1115 type != BIOSConfigManager::AttributeType::Integer)
1116 {
1117 error("Attribute type '{TYPE}' not supported", "TYPE",
1118 attributeType);
1119 continue;
1120 }
1121
1122 const auto [attrType, readonlyStatus, displayName, description,
1123 menuPath, currentValue, defaultValue,
1124 option] = baseBIOSTableMaps.at(attributeName);
1125
1126 entry->attr_handle = htole16(handler);
1127
1128 // Need to verify that the current value has really changed
1129 if (attributeType == attrType && attributevalue != currentValue)
1130 {
1131 listOfHandles.emplace_back(htole16(handler));
1132 }
1133
1134 (*iter)->generateAttributeEntry(attributevalue, attrValueEntry);
1135
1136 setAttrValue(attrValueEntry.data(), attrValueEntry.size(), true);
1137 }
1138
1139 if (listOfHandles.size())
1140 {
1141 #ifdef OEM_IBM
1142 auto rc = pldm::responder::platform::sendBiosAttributeUpdateEvent(
1143 eid, instanceIdDb, listOfHandles, handler);
1144 if (rc != PLDM_SUCCESS)
1145 {
1146 return;
1147 }
1148 #endif
1149 }
1150 }
1151
listenPendingAttributes()1152 void BIOSConfig::listenPendingAttributes()
1153 {
1154 constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager";
1155 constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager";
1156
1157 using namespace sdbusplus::bus::match::rules;
1158 auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>(
1159 pldm::utils::DBusHandler::getBus(),
1160 propertiesChanged(objPath, objInterface),
1161 [this](sdbusplus::message_t& msg) {
1162 constexpr auto propertyName = "PendingAttributes";
1163
1164 using Value =
1165 std::variant<std::string, PendingAttributes, BaseBIOSTable>;
1166 using Properties = std::map<DbusProp, Value>;
1167
1168 Properties props{};
1169 std::string intf;
1170 msg.read(intf, props);
1171
1172 auto valPropMap = props.find(propertyName);
1173 if (valPropMap == props.end())
1174 {
1175 return;
1176 }
1177
1178 PendingAttributes pendingAttributes =
1179 std::get<PendingAttributes>(valPropMap->second);
1180 this->constructPendingAttribute(pendingAttributes);
1181 });
1182
1183 biosAttrMatch.emplace_back(std::move(updateBIOSMatch));
1184 }
1185
1186 } // namespace bios
1187 } // namespace responder
1188 } // namespace pldm
1189