| #include "fru_parser.hpp" |
| |
| #include <nlohmann/json.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| |
| PHOSPHOR_LOG2_USING; |
| |
| using namespace pldm::responder::dbus; |
| |
| namespace pldm |
| { |
| namespace responder |
| { |
| namespace fru_parser |
| { |
| using Json = nlohmann::json; |
| using InternalFailure = |
| sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; |
| |
| const Json emptyJson{}; |
| const std::vector<Json> emptyJsonList{}; |
| const std::vector<std::string> emptyStringVec{}; |
| |
| FruParser::FruParser(const std::string& dirPath, |
| const fs::path& fruMasterJsonPath) |
| { |
| if (fs::exists(fruMasterJsonPath)) |
| { |
| setupDefaultDBusLookup(fruMasterJsonPath); |
| } |
| setupDefaultFruRecordMap(); |
| |
| fs::path dir(dirPath); |
| if (fs::exists(dir) && !fs::is_empty(dir)) |
| { |
| setupFruRecordMap(dirPath); |
| } |
| } |
| |
| void FruParser::setupDefaultDBusLookup(const fs::path& masterJsonPath) |
| { |
| constexpr auto service = "xyz.openbmc_project.Inventory.Manager"; |
| constexpr auto rootPath = "/xyz/openbmc_project/inventory"; |
| std::ifstream jsonFile(masterJsonPath); |
| auto data = Json::parse(jsonFile, nullptr, false); |
| if (data.is_discarded()) |
| { |
| error( |
| "Parsing FRU Dbus Lookup Map config file failed, FILE={JSON_PATH}", |
| "JSON_PATH", masterJsonPath.c_str()); |
| std::abort(); |
| } |
| std::map<Interface, EntityType> defIntfToEntityType; |
| auto dbusMap = data.value("FruDBusLookupMap", emptyJson); |
| for (const auto& element : dbusMap.items()) |
| |
| { |
| try |
| { |
| defIntfToEntityType[static_cast<Interface>(element.key())] = |
| static_cast<EntityType>(element.value()); |
| } |
| catch (const std::exception& e) |
| { |
| error("FRU DBus lookup map format error"); |
| throw InternalFailure(); |
| } |
| } |
| |
| Interfaces interfaces{}; |
| for (auto [intf, entityType] : defIntfToEntityType) |
| { |
| intfToEntityType[intf] = entityType; |
| interfaces.emplace(intf); |
| } |
| |
| lookupInfo.emplace(service, rootPath, std::move(interfaces)); |
| } |
| |
| void FruParser::setupDefaultFruRecordMap() |
| { |
| const FruRecordInfo generalRecordInfo = { |
| 1, // generalRecordType |
| 1, // encodingTypeASCII |
| { |
| // DSP0257 Table 5 General FRU Record Field Type Definitions |
| {"xyz.openbmc_project.Inventory.Decorator.Asset", "Model", "string", |
| 2}, |
| {"xyz.openbmc_project.Inventory.Decorator.Asset", "PartNumber", |
| "string", 3}, |
| {"xyz.openbmc_project.Inventory.Decorator.Asset", "SerialNumber", |
| "string", 4}, |
| {"xyz.openbmc_project.Inventory.Decorator.Asset", "Manufacturer", |
| "string", 5}, |
| {"xyz.openbmc_project.Inventory.Item", "PrettyName", "string", 8}, |
| {"xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", |
| "string", 11}, |
| {"xyz.openbmc_project.Inventory.Decorator.Revision", "Version", |
| "string", 10}, |
| }}; |
| |
| for (auto [intf, entityType] : intfToEntityType) |
| { |
| recordMap[intf] = {generalRecordInfo}; |
| } |
| } |
| |
| void FruParser::setupFruRecordMap(const std::string& dirPath) |
| { |
| for (auto& file : fs::directory_iterator(dirPath)) |
| { |
| auto fileName = file.path().filename().string(); |
| std::ifstream jsonFile(file.path()); |
| auto data = Json::parse(jsonFile, nullptr, false); |
| if (data.is_discarded()) |
| { |
| error("Parsing FRU config file failed, FILE={FILE_PATH}", |
| "FILE_PATH", file.path().c_str()); |
| throw InternalFailure(); |
| } |
| |
| try |
| { |
| auto record = data.value("record_details", emptyJson); |
| auto recordType = |
| static_cast<uint8_t>(record.value("fru_record_type", 0)); |
| auto encType = |
| static_cast<uint8_t>(record.value("fru_encoding_type", 0)); |
| auto dbusIntfName = record.value("dbus_interface_name", ""); |
| auto entries = data.value("fru_fields", emptyJsonList); |
| std::vector<FieldInfo> fieldInfo; |
| |
| for (const auto& entry : entries) |
| { |
| auto fieldType = |
| static_cast<uint8_t>(entry.value("fru_field_type", 0)); |
| auto dbus = entry.value("dbus", emptyJson); |
| auto interface = dbus.value("interface", ""); |
| auto property = dbus.value("property_name", ""); |
| auto propType = dbus.value("property_type", ""); |
| fieldInfo.emplace_back( |
| std::make_tuple(std::move(interface), std::move(property), |
| std::move(propType), std::move(fieldType))); |
| } |
| |
| FruRecordInfo fruInfo; |
| fruInfo = |
| std::make_tuple(recordType, encType, std::move(fieldInfo)); |
| |
| auto search = recordMap.find(dbusIntfName); |
| |
| // PLDM FRU can have multiple records for the same FRU like General |
| // FRU record and multiple OEM FRU records. If the FRU item |
| // interface name is already in the map, that indicates a record |
| // info is already added for the FRU, so append the new record info |
| // to the same data. |
| if (search != recordMap.end()) |
| { |
| search->second.emplace_back(std::move(fruInfo)); |
| } |
| else |
| { |
| FruRecordInfos recordInfos{fruInfo}; |
| recordMap.emplace(dbusIntfName, recordInfos); |
| } |
| } |
| catch (const std::exception& e) |
| { |
| continue; |
| } |
| } |
| } |
| |
| } // namespace fru_parser |
| |
| } // namespace responder |
| |
| } // namespace pldm |