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