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