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 constexpr auto fruMasterJson = "FRU_Master.json"; 28 29 FruParser::FruParser(const std::string& dirPath) 30 { 31 fs::path dir(dirPath); 32 if (!fs::exists(dir) || fs::is_empty(dir)) 33 { 34 setupDefaultDBusLookup(); 35 setupDefaultFruRecordMap(); 36 return; 37 } 38 39 fs::path masterFilePath = dir / fruMasterJson; 40 if (!fs::exists(masterFilePath)) 41 { 42 std::cerr << "FRU D-Bus lookup JSON does not exist, PATH=" 43 << masterFilePath << "\n"; 44 throw InternalFailure(); 45 } 46 47 setupDBusLookup(masterFilePath); 48 setupFruRecordMap(dirPath); 49 } 50 51 void FruParser::setupDefaultDBusLookup() 52 { 53 constexpr auto service = "xyz.openbmc_project.Inventory.Manager"; 54 constexpr auto rootPath = "/xyz/openbmc_project/inventory"; 55 56 // DSP0249 1.0.0 Table 15 Entity ID Codes 57 const std::map<Interface, EntityType> defIntfToEntityType = { 58 {"xyz.openbmc_project.Inventory.Item.Chassis", 45}, 59 {"xyz.openbmc_project.Inventory.Item.Board", 60}, 60 {"xyz.openbmc_project.Inventory.Item.Board.Motherboard", 64}, 61 {"xyz.openbmc_project.Inventory.Item.Panel", 69}, 62 {"xyz.openbmc_project.Inventory.Item.PowerSupply", 120}, 63 {"xyz.openbmc_project.Inventory.Item.Vrm", 123}, 64 {"xyz.openbmc_project.Inventory.Item.Cpu", 135}, 65 {"xyz.openbmc_project.Inventory.Item.Bmc", 137}, 66 {"xyz.openbmc_project.Inventory.Item.Dimm", 142}, 67 }; 68 69 Interfaces interfaces{}; 70 for (auto [intf, entityType] : defIntfToEntityType) 71 { 72 intfToEntityType[intf] = entityType; 73 interfaces.emplace(intf); 74 } 75 76 lookupInfo.emplace(service, rootPath, std::move(interfaces)); 77 } 78 79 void FruParser::setupDBusLookup(const fs::path& filePath) 80 { 81 std::ifstream jsonFile(filePath); 82 83 auto data = Json::parse(jsonFile, nullptr, false); 84 if (data.is_discarded()) 85 { 86 std::cerr << "Parsing FRU master config file failed, FILE=" << filePath; 87 throw InternalFailure(); 88 } 89 90 Service service = data.value("service", ""); 91 RootPath rootPath = data.value("root_path", ""); 92 auto entities = data.value("entities", emptyJsonList); 93 Interfaces interfaces{}; 94 EntityType entityType{}; 95 for (auto& entity : entities) 96 { 97 auto intf = entity.value("interface", ""); 98 intfToEntityType[intf] = 99 std::move(entity.value("entity_type", entityType)); 100 interfaces.emplace(std::move(intf)); 101 } 102 lookupInfo.emplace(std::make_tuple(std::move(service), std::move(rootPath), 103 std::move(interfaces))); 104 } 105 106 void FruParser::setupDefaultFruRecordMap() 107 { 108 const FruRecordInfo generalRecordInfo = { 109 1, // generalRecordType 110 1, // encodingTypeASCII 111 { 112 // DSP0257 Table 5 General FRU Record Field Type Definitions 113 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Model", "string", 114 2}, 115 {"xyz.openbmc_project.Inventory.Decorator.Asset", "PartNumber", 116 "string", 3}, 117 {"xyz.openbmc_project.Inventory.Decorator.Asset", "SerialNumber", 118 "string", 4}, 119 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Manufacturer", 120 "string", 5}, 121 {"xyz.openbmc_project.Inventory.Item", "PrettyName", "string", 8}, 122 {"xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 123 "string", 11}, 124 {"xyz.openbmc_project.Inventory.Decorator.Revision", "Version", 125 "string", 10}, 126 }}; 127 128 for (auto [intf, entityType] : intfToEntityType) 129 { 130 recordMap[intf] = {generalRecordInfo}; 131 } 132 } 133 134 void FruParser::setupFruRecordMap(const std::string& dirPath) 135 { 136 for (auto& file : fs::directory_iterator(dirPath)) 137 { 138 auto fileName = file.path().filename().string(); 139 if (fruMasterJson == fileName) 140 { 141 continue; 142 } 143 144 std::ifstream jsonFile(file.path()); 145 auto data = Json::parse(jsonFile, nullptr, false); 146 if (data.is_discarded()) 147 { 148 149 std::cerr << "Parsing FRU master config file failed, FILE=" 150 << file.path(); 151 throw InternalFailure(); 152 } 153 154 try 155 { 156 auto record = data.value("record_details", emptyJson); 157 auto recordType = 158 static_cast<uint8_t>(record.value("fru_record_type", 0)); 159 auto encType = 160 static_cast<uint8_t>(record.value("fru_encoding_type", 0)); 161 auto dbusIntfName = record.value("dbus_interface_name", ""); 162 auto entries = data.value("fru_fields", emptyJsonList); 163 std::vector<FieldInfo> fieldInfo; 164 165 for (const auto& entry : entries) 166 { 167 auto fieldType = 168 static_cast<uint8_t>(entry.value("fru_field_type", 0)); 169 auto dbus = entry.value("dbus", emptyJson); 170 auto interface = dbus.value("interface", ""); 171 auto property = dbus.value("property_name", ""); 172 auto propType = dbus.value("property_type", ""); 173 fieldInfo.emplace_back( 174 std::make_tuple(std::move(interface), std::move(property), 175 std::move(propType), std::move(fieldType))); 176 } 177 178 FruRecordInfo fruInfo; 179 fruInfo = 180 std::make_tuple(recordType, encType, std::move(fieldInfo)); 181 182 auto search = recordMap.find(dbusIntfName); 183 184 // PLDM FRU can have multiple records for the same FRU like General 185 // FRU record and multiple OEM FRU records. If the FRU item 186 // interface name is already in the map, that indicates a record 187 // info is already added for the FRU, so append the new record info 188 // to the same data. 189 if (search != recordMap.end()) 190 { 191 search->second.emplace_back(std::move(fruInfo)); 192 } 193 else 194 { 195 FruRecordInfos recordInfos{fruInfo}; 196 recordMap.emplace(dbusIntfName, recordInfos); 197 } 198 } 199 catch (const std::exception& e) 200 { 201 continue; 202 } 203 } 204 } 205 206 } // namespace fru_parser 207 208 } // namespace responder 209 210 } // namespace pldm 211