| #include "bios_parser.hpp" |
| |
| #include "bios_table.hpp" |
| #include "utils.hpp" |
| |
| #include <cassert> |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include <nlohmann/json.hpp> |
| #include <optional> |
| #include <set> |
| |
| #include "libpldm/bios.h" |
| #include "libpldm/bios_table.h" |
| |
| namespace bios_parser |
| { |
| |
| using namespace pldm::utils; |
| using Json = nlohmann::json; |
| namespace fs = std::filesystem; |
| using namespace pldm::responder::bios; |
| |
| const std::vector<Json> emptyJsonList{}; |
| const Json emptyJson{}; |
| |
| using AttrType = uint8_t; |
| using Table = std::vector<uint8_t>; |
| using BIOSJsonName = std::string; |
| using AttrLookup = std::map<AttrName, std::optional<DBusMapping>>; |
| |
| const std::set<std::string> SupportedDbusPropertyTypes = { |
| "bool", "uint8_t", "int16_t", "uint16_t", "int32_t", |
| "uint32_t", "int64_t", "uint64_t", "double", "string"}; |
| |
| using BIOSStringHandler = |
| std::function<int(const Json& entry, Strings& strings)>; |
| using AttrLookupHandler = std::function<int(const Json& entry, AttrLookup)>; |
| using typeHandler = std::function<int(const Json& entry)>; |
| |
| Strings BIOSStrings; |
| AttrLookup BIOSAttrLookup; |
| |
| const Strings& getStrings() |
| { |
| return BIOSStrings; |
| } |
| |
| int parseBIOSJsonFile(const fs::path& dirPath, const std::string& fileName, |
| Json& fileData) |
| { |
| int rc = 0; |
| |
| fs::path filePath = dirPath / fileName; |
| |
| std::ifstream jsonFile(filePath); |
| if (!jsonFile.is_open()) |
| { |
| std::cerr << "BIOS config file does not exist, FILE=" |
| << filePath.c_str() << "\n"; |
| rc = -1; |
| } |
| else |
| { |
| fileData = Json::parse(jsonFile, nullptr, false); |
| if (fileData.is_discarded()) |
| { |
| std::cerr << "Parsing config file failed, FILE=" << filePath.c_str() |
| << "\n"; |
| rc = -1; |
| } |
| } |
| |
| return rc; |
| } |
| |
| namespace bios_enum |
| { |
| |
| namespace internal |
| { |
| |
| using Value = std::string; |
| |
| /** @brief Map of DBus property value to attribute value |
| */ |
| using DbusValToValMap = std::map<PropertyValue, Value>; |
| |
| /** @brief Map containing the DBus property value to attribute value map for the |
| * BIOS enumeration type attributes |
| */ |
| std::map<AttrName, DbusValToValMap> dbusValToValMaps; |
| |
| /** @brief Map containing the possible and the default values for the BIOS |
| * enumeration type attributes. |
| */ |
| AttrValuesMap valueMap; |
| |
| /** @brief Populate the mapping between D-Bus property value and attribute value |
| * for the BIOS enumeration attribute. |
| * |
| * @param[in] type - type of the D-Bus property |
| * @param[in] dBusValues - json array of D-Bus property values |
| * @param[in] pv - Possible values for the BIOS enumeration attribute |
| * |
| */ |
| DbusValToValMap populateMapping(const std::string& type, const Json& dBusValues, |
| const PossibleValues& pv) |
| { |
| size_t pos = 0; |
| PropertyValue value; |
| DbusValToValMap valueMap; |
| for (auto it = dBusValues.begin(); it != dBusValues.end(); ++it, ++pos) |
| { |
| if (type == "uint8_t") |
| { |
| value = static_cast<uint8_t>(it.value()); |
| } |
| else if (type == "uint16_t") |
| { |
| value = static_cast<uint16_t>(it.value()); |
| } |
| else if (type == "uint32_t") |
| { |
| value = static_cast<uint32_t>(it.value()); |
| } |
| else if (type == "uint64_t") |
| { |
| value = static_cast<uint64_t>(it.value()); |
| } |
| else if (type == "int16_t") |
| { |
| value = static_cast<int16_t>(it.value()); |
| } |
| else if (type == "int32_t") |
| { |
| value = static_cast<int32_t>(it.value()); |
| } |
| else if (type == "int64_t") |
| { |
| value = static_cast<int64_t>(it.value()); |
| } |
| else if (type == "bool") |
| { |
| value = static_cast<bool>(it.value()); |
| } |
| else if (type == "double") |
| { |
| value = static_cast<double>(it.value()); |
| } |
| else if (type == "string") |
| { |
| value = static_cast<std::string>(it.value()); |
| } |
| else |
| { |
| std::cerr << "Unknown D-Bus property type, TYPE=" << type.c_str() |
| << "\n"; |
| } |
| |
| valueMap.emplace(value, pv[pos]); |
| } |
| |
| return valueMap; |
| } |
| } // namespace internal |
| |
| int setupBIOSStrings(const Json& entry, Strings& strings) |
| { |
| Json pvs = entry.value("possible_values", emptyJsonList); |
| |
| for (auto& pv : pvs) |
| { |
| strings.emplace_back(std::move(pv.get<std::string>())); |
| } |
| |
| return 0; |
| } |
| |
| int setup(const Json& entry) |
| { |
| PossibleValues possibleValues; |
| DefaultValues defaultValues; |
| |
| std::string attrName = entry.value("attribute_name", ""); |
| Json pv = entry["possible_values"]; |
| for (auto& val : pv) |
| { |
| possibleValues.emplace_back(std::move(val)); |
| } |
| Json dv = entry["default_values"]; |
| for (auto& val : dv) |
| { |
| defaultValues.emplace_back(std::move(val)); |
| } |
| if (entry.count("dbus") != 0) |
| { |
| auto dbusEntry = entry.value("dbus", emptyJson); |
| std::string propertyType = dbusEntry.value("property_type", ""); |
| Json propValues = dbusEntry["property_values"]; |
| internal::dbusValToValMaps.emplace( |
| attrName, internal::populateMapping(propertyType, propValues, |
| possibleValues)); |
| } |
| // Defaulting all the types of attributes to BIOSEnumeration |
| internal::valueMap.emplace(std::move(attrName), |
| std::make_tuple(entry.count("dbus") == 0, |
| std::move(possibleValues), |
| std::move(defaultValues))); |
| return 0; |
| } |
| |
| const AttrValuesMap& getValues() |
| { |
| return internal::valueMap; |
| } |
| |
| CurrentValues getAttrValue(const AttrName& attrName) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| CurrentValues currentValues; |
| PropertyValue propValue; |
| |
| if (dBusMap == std::nullopt) |
| { |
| const auto& valueEntry = internal::valueMap.at(attrName); |
| const auto& [readOnly, possibleValues, currentValues] = valueEntry; |
| return currentValues; |
| } |
| |
| const auto& dbusValToValMap = internal::dbusValToValMaps.at(attrName); |
| propValue = |
| pldm::utils::DBusHandler().getDbusPropertyVariant<PropertyValue>( |
| dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(), |
| dBusMap->interface.c_str()); |
| |
| auto iter = dbusValToValMap.find(propValue); |
| if (iter != dbusValToValMap.end()) |
| { |
| currentValues.push_back(iter->second); |
| } |
| |
| return currentValues; |
| } |
| |
| int setAttrValue(const AttrName& attrName, |
| const pldm_bios_attr_val_table_entry* attrValueEntry, |
| const pldm_bios_attr_table_entry* attrEntry, |
| const BIOSStringTable& stringTable) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| if (dBusMap == std::nullopt) |
| { |
| return PLDM_SUCCESS; |
| } |
| |
| uint8_t pvNum = pldm_bios_table_attr_entry_enum_decode_pv_num(attrEntry); |
| std::vector<uint16_t> pvHdls(pvNum, 0); |
| pldm_bios_table_attr_entry_enum_decode_pv_hdls(attrEntry, pvHdls.data(), |
| pvNum); |
| |
| uint8_t defNum = |
| pldm_bios_table_attr_value_entry_enum_decode_number(attrValueEntry); |
| |
| assert(defNum == 1); |
| |
| std::vector<uint8_t> currHdls(1, 0); |
| pldm_bios_table_attr_value_entry_enum_decode_handles( |
| attrValueEntry, currHdls.data(), currHdls.size()); |
| |
| auto valueString = stringTable.findString(pvHdls[currHdls[0]]); |
| |
| const auto& dbusValToValMap = internal::dbusValToValMaps.at(attrName); |
| |
| auto it = std::find_if(dbusValToValMap.begin(), dbusValToValMap.end(), |
| [&valueString](const auto& typePair) { |
| return typePair.second == valueString; |
| }); |
| if (it == dbusValToValMap.end()) |
| { |
| std::cerr << "Invalid Enum Value\n"; |
| return PLDM_ERROR; |
| } |
| |
| pldm::utils::DBusHandler().setDbusProperty(dBusMap.value(), it->first); |
| |
| return PLDM_SUCCESS; |
| } |
| |
| } // namespace bios_enum |
| |
| namespace bios_string |
| { |
| |
| /** @brief BIOS string types |
| */ |
| enum BIOSStringEncoding |
| { |
| UNKNOWN = 0x00, |
| ASCII = 0x01, |
| HEX = 0x02, |
| UTF_8 = 0x03, |
| UTF_16LE = 0x04, |
| UTF_16BE = 0x05, |
| VENDOR_SPECIFIC = 0xFF |
| }; |
| |
| const std::map<std::string, uint8_t> strTypeMap{ |
| {"Unknown", UNKNOWN}, |
| {"ASCII", ASCII}, |
| {"Hex", HEX}, |
| {"UTF-8", UTF_8}, |
| {"UTF-16LE", UTF_16LE}, |
| {"UTF-16LE", UTF_16LE}, |
| {"Vendor Specific", VENDOR_SPECIFIC}}; |
| |
| namespace internal |
| { |
| |
| /** @brief Map containing the possible and the default values for the BIOS |
| * string type attributes. |
| */ |
| AttrValuesMap valueMap; |
| |
| } // namespace internal |
| |
| int setup(const Json& jsonEntry) |
| { |
| |
| std::string attr = jsonEntry.value("attribute_name", ""); |
| // Transfer string type from string to enum |
| std::string strTypeTmp = jsonEntry.value("string_type", "Unknown"); |
| auto iter = strTypeMap.find(strTypeTmp); |
| if (iter == strTypeMap.end()) |
| { |
| std::cerr << "Wrong string type, STRING_TYPE=" << strTypeTmp.c_str() |
| << " ATTRIBUTE_NAME=" << attr.c_str() << "\n"; |
| return -1; |
| } |
| uint8_t strType = iter->second; |
| |
| uint16_t minStrLen = jsonEntry.value("minimum_string_length", 0); |
| uint16_t maxStrLen = jsonEntry.value("maximum_string_length", 0); |
| uint16_t defaultStrLen = jsonEntry.value("default_string_length", 0); |
| std::string defaultStr = jsonEntry.value("default_string", ""); |
| |
| pldm_bios_table_attr_entry_string_info info = { |
| 0, /* name handle */ |
| false, /* read only */ |
| strType, minStrLen, maxStrLen, defaultStrLen, defaultStr.data(), |
| }; |
| |
| const char* errmsg; |
| auto rc = pldm_bios_table_attr_entry_string_info_check(&info, &errmsg); |
| if (rc != PLDM_SUCCESS) |
| { |
| std::cerr << "Wrong filed for string attribute, ATTRIBUTE_NAME=" |
| << attr.c_str() << " ERRMSG=" << errmsg |
| << " MINIMUM_STRING_LENGTH=" << minStrLen |
| << " MAXIMUM_STRING_LENGTH=" << maxStrLen |
| << " DEFAULT_STRING_LENGTH=" << defaultStrLen |
| << " DEFAULT_STRING=" << defaultStr.data() << "\n"; |
| return -1; |
| } |
| |
| // Defaulting all the types of attributes to BIOSString |
| internal::valueMap.emplace( |
| std::move(attr), |
| std::make_tuple(jsonEntry.count("dbus") == 0, strType, minStrLen, |
| maxStrLen, defaultStrLen, std::move(defaultStr))); |
| |
| return 0; |
| } |
| |
| const AttrValuesMap& getValues() |
| { |
| return internal::valueMap; |
| } |
| |
| std::string getAttrValue(const AttrName& attrName) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| std::variant<std::string> propValue; |
| |
| if (dBusMap == std::nullopt) |
| { // return default string |
| const auto& valueEntry = internal::valueMap.at(attrName); |
| return std::get<DefaultStr>(valueEntry); |
| } |
| |
| return pldm::utils::DBusHandler().getDbusProperty<std::string>( |
| dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(), |
| dBusMap->interface.c_str()); |
| } |
| |
| std::string stringToUtf8(BIOSStringEncoding stringType, |
| const std::vector<uint8_t>& data) |
| { |
| switch (stringType) |
| { |
| case ASCII: |
| case UTF_8: |
| case HEX: |
| return std::string(data.begin(), data.end()); |
| case UTF_16BE: |
| case UTF_16LE: // TODO |
| return std::string(data.begin(), data.end()); |
| case VENDOR_SPECIFIC: |
| throw std::invalid_argument("Vendor Specific is unsupported"); |
| case UNKNOWN: |
| throw std::invalid_argument("Unknown String Type"); |
| } |
| throw std::invalid_argument("String Type Error"); |
| } |
| |
| int setAttrValue(const AttrName& attrName, |
| const pldm_bios_attr_val_table_entry* attrValueEntry, |
| const pldm_bios_attr_table_entry* attrEntry, |
| const BIOSStringTable&) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| if (dBusMap == std::nullopt) |
| { |
| return PLDM_SUCCESS; |
| } |
| |
| auto stringType = |
| pldm_bios_table_attr_entry_string_decode_string_type(attrEntry); |
| |
| variable_field currentString{}; |
| pldm_bios_table_attr_value_entry_string_decode_string(attrValueEntry, |
| ¤tString); |
| std::vector<uint8_t> data(currentString.ptr, |
| currentString.ptr + currentString.length); |
| |
| PropertyValue value = |
| stringToUtf8(static_cast<BIOSStringEncoding>(stringType), data); |
| pldm::utils::DBusHandler().setDbusProperty(dBusMap.value(), value); |
| |
| return PLDM_SUCCESS; |
| } |
| |
| } // namespace bios_string |
| |
| namespace bios_integer |
| { |
| |
| AttrValuesMap valueMap; |
| |
| int setup(const Json& jsonEntry) |
| { |
| |
| std::string attr = jsonEntry.value("attribute_name", ""); |
| // Transfer string type from string to enum |
| |
| uint64_t lowerBound = jsonEntry.value("lower_bound", 0); |
| uint64_t upperBound = jsonEntry.value("upper_bound", 0); |
| uint32_t scalarIncrement = jsonEntry.value("scalar_increment", 1); |
| uint64_t defaultValue = jsonEntry.value("default_value", 0); |
| pldm_bios_table_attr_entry_integer_info info = { |
| 0, /* name handle*/ |
| false, /* read only */ |
| lowerBound, upperBound, scalarIncrement, defaultValue, |
| }; |
| const char* errmsg = nullptr; |
| auto rc = pldm_bios_table_attr_entry_integer_info_check(&info, &errmsg); |
| if (rc != PLDM_SUCCESS) |
| { |
| std::cerr << "Wrong filed for integer attribute, ATTRIBUTE_NAME=" |
| << attr.c_str() << " ERRMSG=" << errmsg |
| << " LOWER_BOUND=" << lowerBound |
| << " UPPER_BOUND=" << upperBound |
| << " DEFAULT_VALUE=" << defaultValue |
| << " SCALAR_INCREMENT=" << scalarIncrement << "\n"; |
| return -1; |
| } |
| |
| valueMap.emplace(std::move(attr), |
| std::make_tuple(jsonEntry.count("dbus") == 0, lowerBound, |
| upperBound, scalarIncrement, |
| defaultValue)); |
| |
| return 0; |
| } |
| |
| const AttrValuesMap& getValues() |
| { |
| return valueMap; |
| } |
| |
| uint64_t getAttrValue(const AttrName& attrName) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| std::variant<std::string> propValue; |
| |
| if (dBusMap == std::nullopt) |
| { // return default string |
| const auto& valueEntry = valueMap.at(attrName); |
| return std::get<AttrDefaultValue>(valueEntry); |
| } |
| |
| return pldm::utils::DBusHandler().getDbusProperty<uint64_t>( |
| dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(), |
| dBusMap->interface.c_str()); |
| } |
| |
| int setAttrValue(const AttrName& attrName, |
| const pldm_bios_attr_val_table_entry* attrValueEntry, |
| const pldm_bios_attr_table_entry*, const BIOSStringTable&) |
| { |
| const auto& dBusMap = BIOSAttrLookup.at(attrName); |
| if (dBusMap == std::nullopt) |
| { |
| return PLDM_SUCCESS; |
| } |
| |
| uint64_t currentValue = |
| pldm_bios_table_attr_value_entry_integer_decode_cv(attrValueEntry); |
| |
| PropertyValue value = static_cast<uint64_t>(currentValue); |
| pldm::utils::DBusHandler().setDbusProperty(dBusMap.value(), value); |
| |
| return PLDM_SUCCESS; |
| } |
| |
| } // namespace bios_integer |
| |
| const std::map<BIOSJsonName, BIOSStringHandler> BIOSStringHandlers = { |
| {bIOSEnumJson, bios_enum::setupBIOSStrings}, |
| }; |
| |
| const std::map<BIOSJsonName, typeHandler> BIOSTypeHandlers = { |
| {bIOSEnumJson, bios_enum::setup}, |
| {bIOSStrJson, bios_string::setup}, |
| {bIOSIntegerJson, bios_integer::setup}, |
| }; |
| |
| void setupBIOSStrings(const BIOSJsonName& jsonName, const Json& entry, |
| Strings& strings) |
| { |
| strings.emplace_back(entry.value("attribute_name", "")); |
| auto iter = BIOSStringHandlers.find(jsonName); |
| if (iter != BIOSStringHandlers.end()) |
| { |
| iter->second(entry, strings); |
| } |
| } |
| |
| void setupBIOSAttrLookup(const Json& jsonEntry, AttrLookup& lookup) |
| { |
| std::optional<DBusMapping> dBusMap; |
| std::string attrName = jsonEntry.value("attribute_name", ""); |
| |
| if (jsonEntry.count("dbus") != 0) |
| { |
| auto dBusEntry = jsonEntry.value("dbus", emptyJson); |
| std::string objectPath = dBusEntry.value("object_path", ""); |
| std::string interface = dBusEntry.value("interface", ""); |
| std::string propertyName = dBusEntry.value("property_name", ""); |
| std::string propertyType = dBusEntry.value("property_type", ""); |
| if (!objectPath.empty() && !interface.empty() && |
| !propertyName.empty() && |
| (SupportedDbusPropertyTypes.find(propertyType) != |
| SupportedDbusPropertyTypes.end())) |
| { |
| dBusMap = std::optional<DBusMapping>( |
| {objectPath, interface, propertyName, propertyType}); |
| } |
| else |
| { |
| std::cerr << "Invalid dbus config, OBJPATH=" |
| << dBusMap->objectPath.c_str() |
| << " INTERFACE=" << dBusMap->interface.c_str() |
| << " PROPERTY_NAME=" << dBusMap->propertyName.c_str() |
| << " PROPERTY_TYPE=" << dBusMap->propertyType.c_str() |
| << "\n"; |
| } |
| } |
| lookup.emplace(attrName, dBusMap); |
| } |
| |
| int setupBIOSType(const BIOSJsonName& jsonName, const Json& entry) |
| { |
| auto iter = BIOSTypeHandlers.find(jsonName); |
| if (iter != BIOSTypeHandlers.end()) |
| { |
| iter->second(entry); |
| } |
| return 0; |
| } |
| |
| const std::vector<BIOSJsonName> BIOSConfigFiles = {bIOSEnumJson, bIOSStrJson, |
| bIOSIntegerJson}; |
| |
| int setupConfig(const char* dirPath) |
| { |
| if (!BIOSStrings.empty() && !BIOSAttrLookup.empty()) |
| { |
| return 0; |
| } |
| |
| fs::path dir(dirPath); |
| if (!fs::exists(dir) || fs::is_empty(dir)) |
| { |
| std::cerr << "BIOS config directory does not exist or empty, DIR=" |
| << dirPath << "\n"; |
| return -1; |
| } |
| for (auto jsonName : BIOSConfigFiles) |
| { |
| Json json; |
| if (parseBIOSJsonFile(dir, jsonName, json) < 0) |
| { |
| continue; |
| } |
| auto entries = json.value("entries", emptyJsonList); |
| for (auto& entry : entries) |
| { |
| setupBIOSStrings(jsonName, entry, BIOSStrings); |
| setupBIOSAttrLookup(entry, BIOSAttrLookup); |
| setupBIOSType(jsonName, entry); |
| } |
| } |
| if (BIOSStrings.empty()) |
| { // means there is no attribute |
| std::cerr << "No attribute is found in the config directory, DIR=" |
| << dirPath << "\n"; |
| return -1; |
| } |
| return 0; |
| } |
| |
| using setAttrValueHandler = std::function<int( |
| const AttrName&, const pldm_bios_attr_val_table_entry*, |
| const pldm_bios_attr_table_entry*, const BIOSStringTable&)>; |
| |
| const std::map<AttrType, setAttrValueHandler> SetAttrValueMap{{ |
| {PLDM_BIOS_STRING, bios_string::setAttrValue}, |
| {PLDM_BIOS_STRING_READ_ONLY, bios_string::setAttrValue}, |
| {PLDM_BIOS_ENUMERATION, bios_enum::setAttrValue}, |
| {PLDM_BIOS_ENUMERATION_READ_ONLY, bios_enum::setAttrValue}, |
| {PLDM_BIOS_INTEGER, bios_integer::setAttrValue}, |
| {PLDM_BIOS_INTEGER_READ_ONLY, bios_integer::setAttrValue}, |
| |
| }}; |
| |
| int setAttributeValueOnDbus(const variable_field* attributeData, |
| const BIOSTable& biosAttributeTable, |
| const BIOSStringTable& stringTable) |
| { |
| Table attributeTable; |
| biosAttributeTable.load(attributeTable); |
| auto attrValueEntry = |
| reinterpret_cast<const pldm_bios_attr_val_table_entry*>( |
| attributeData->ptr); |
| |
| auto attrType = |
| pldm_bios_table_attr_value_entry_decode_attribute_type(attrValueEntry); |
| auto attrHandle = pldm_bios_table_attr_value_entry_decode_attribute_handle( |
| attrValueEntry); |
| |
| auto attrEntry = pldm_bios_table_attr_find_by_handle( |
| attributeTable.data(), attributeTable.size(), attrHandle); |
| |
| assert(attrEntry != nullptr); |
| |
| auto attrNameHandle = |
| pldm_bios_table_attr_entry_decode_string_handle(attrEntry); |
| |
| auto attrName = stringTable.findString(attrNameHandle); |
| |
| try |
| { |
| auto rc = SetAttrValueMap.at(attrType)(attrName, attrValueEntry, |
| attrEntry, stringTable); |
| return rc; |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << "setAttributeValueOnDbus Error: " << e.what() << std::endl; |
| return PLDM_ERROR; |
| } |
| } |
| |
| } // namespace bios_parser |