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