1 #include "fru_parser.hpp" 2 3 #include <nlohmann/json.hpp> 4 #include <xyz/openbmc_project/Common/error.hpp> 5 6 #include <filesystem> 7 #include <fstream> 8 #include <iostream> 9 10 namespace pldm 11 { 12 13 namespace responder 14 { 15 16 namespace fru_parser 17 { 18 19 using Json = nlohmann::json; 20 using InternalFailure = 21 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 22 23 const Json emptyJson{}; 24 const std::vector<Json> emptyJsonList{}; 25 const std::vector<std::string> emptyStringVec{}; 26 27 FruParser::FruParser(const std::string& dirPath, 28 const fs::path& fruMasterJsonPath) 29 { 30 if (fs::exists(fruMasterJsonPath)) 31 { 32 setupDefaultDBusLookup(fruMasterJsonPath); 33 } 34 setupDefaultFruRecordMap(); 35 36 fs::path dir(dirPath); 37 if (fs::exists(dir) && !fs::is_empty(dir)) 38 { 39 setupFruRecordMap(dirPath); 40 } 41 } 42 43 void FruParser::setupDefaultDBusLookup(const fs::path& masterJsonPath) 44 { 45 constexpr auto service = "xyz.openbmc_project.Inventory.Manager"; 46 constexpr auto rootPath = "/xyz/openbmc_project/inventory"; 47 std::ifstream jsonFile(masterJsonPath); 48 auto data = Json::parse(jsonFile, nullptr, false); 49 if (data.is_discarded()) 50 { 51 52 std::cerr << "Parsing FRU Dbus Lookup Map config file failed, FILE=" 53 << masterJsonPath; 54 std::abort(); 55 } 56 std::map<Interface, EntityType> defIntfToEntityType; 57 auto dbusMap = data.value("FruDBusLookupMap", emptyJson); 58 for (const auto& element : dbusMap.items()) 59 60 { 61 try 62 { 63 defIntfToEntityType[static_cast<Interface>(element.key())] = 64 static_cast<EntityType>(element.value()); 65 } 66 catch (const std::exception& e) 67 { 68 std::cerr << "FRU DBus lookup map format error\n"; 69 throw InternalFailure(); 70 } 71 } 72 73 Interfaces interfaces{}; 74 for (auto [intf, entityType] : defIntfToEntityType) 75 { 76 intfToEntityType[intf] = entityType; 77 interfaces.emplace(intf); 78 } 79 80 lookupInfo.emplace(service, rootPath, std::move(interfaces)); 81 } 82 83 void FruParser::setupDefaultFruRecordMap() 84 { 85 const FruRecordInfo generalRecordInfo = { 86 1, // generalRecordType 87 1, // encodingTypeASCII 88 { 89 // DSP0257 Table 5 General FRU Record Field Type Definitions 90 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Model", "string", 91 2}, 92 {"xyz.openbmc_project.Inventory.Decorator.Asset", "PartNumber", 93 "string", 3}, 94 {"xyz.openbmc_project.Inventory.Decorator.Asset", "SerialNumber", 95 "string", 4}, 96 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Manufacturer", 97 "string", 5}, 98 {"xyz.openbmc_project.Inventory.Item", "PrettyName", "string", 8}, 99 {"xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 100 "string", 11}, 101 {"xyz.openbmc_project.Inventory.Decorator.Revision", "Version", 102 "string", 10}, 103 }}; 104 105 for (auto [intf, entityType] : intfToEntityType) 106 { 107 recordMap[intf] = {generalRecordInfo}; 108 } 109 } 110 111 void FruParser::setupFruRecordMap(const std::string& dirPath) 112 { 113 for (auto& file : fs::directory_iterator(dirPath)) 114 { 115 auto fileName = file.path().filename().string(); 116 std::ifstream jsonFile(file.path()); 117 auto data = Json::parse(jsonFile, nullptr, false); 118 if (data.is_discarded()) 119 { 120 121 std::cerr << "Parsing FRU config file failed, FILE=" << file.path(); 122 throw InternalFailure(); 123 } 124 125 try 126 { 127 auto record = data.value("record_details", emptyJson); 128 auto recordType = 129 static_cast<uint8_t>(record.value("fru_record_type", 0)); 130 auto encType = 131 static_cast<uint8_t>(record.value("fru_encoding_type", 0)); 132 auto dbusIntfName = record.value("dbus_interface_name", ""); 133 auto entries = data.value("fru_fields", emptyJsonList); 134 std::vector<FieldInfo> fieldInfo; 135 136 for (const auto& entry : entries) 137 { 138 auto fieldType = 139 static_cast<uint8_t>(entry.value("fru_field_type", 0)); 140 auto dbus = entry.value("dbus", emptyJson); 141 auto interface = dbus.value("interface", ""); 142 auto property = dbus.value("property_name", ""); 143 auto propType = dbus.value("property_type", ""); 144 fieldInfo.emplace_back( 145 std::make_tuple(std::move(interface), std::move(property), 146 std::move(propType), std::move(fieldType))); 147 } 148 149 FruRecordInfo fruInfo; 150 fruInfo = 151 std::make_tuple(recordType, encType, std::move(fieldInfo)); 152 153 auto search = recordMap.find(dbusIntfName); 154 155 // PLDM FRU can have multiple records for the same FRU like General 156 // FRU record and multiple OEM FRU records. If the FRU item 157 // interface name is already in the map, that indicates a record 158 // info is already added for the FRU, so append the new record info 159 // to the same data. 160 if (search != recordMap.end()) 161 { 162 search->second.emplace_back(std::move(fruInfo)); 163 } 164 else 165 { 166 FruRecordInfos recordInfos{fruInfo}; 167 recordMap.emplace(dbusIntfName, recordInfos); 168 } 169 } 170 catch (const std::exception& e) 171 { 172 continue; 173 } 174 } 175 } 176 177 } // namespace fru_parser 178 179 } // namespace responder 180 181 } // namespace pldm 182