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 std::cerr << "Parsing FRU Dbus Lookup Map config file failed, FILE=" 54 << masterJsonPath; 55 std::abort(); 56 } 57 std::map<Interface, EntityType> defIntfToEntityType; 58 auto dbusMap = data.value("FruDBusLookupMap", emptyJson); 59 for (const auto& element : dbusMap.items()) 60 61 { 62 try 63 { 64 defIntfToEntityType[static_cast<Interface>(element.key())] = 65 static_cast<EntityType>(element.value()); 66 } 67 catch (const std::exception& e) 68 { 69 std::cerr << "FRU DBus lookup map format error\n"; 70 throw InternalFailure(); 71 } 72 } 73 74 Interfaces interfaces{}; 75 for (auto [intf, entityType] : defIntfToEntityType) 76 { 77 intfToEntityType[intf] = entityType; 78 interfaces.emplace(intf); 79 } 80 81 lookupInfo.emplace(service, rootPath, std::move(interfaces)); 82 } 83 84 void FruParser::setupDefaultFruRecordMap() 85 { 86 const FruRecordInfo generalRecordInfo = { 87 1, // generalRecordType 88 1, // encodingTypeASCII 89 { 90 // DSP0257 Table 5 General FRU Record Field Type Definitions 91 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Model", "string", 92 2}, 93 {"xyz.openbmc_project.Inventory.Decorator.Asset", "PartNumber", 94 "string", 3}, 95 {"xyz.openbmc_project.Inventory.Decorator.Asset", "SerialNumber", 96 "string", 4}, 97 {"xyz.openbmc_project.Inventory.Decorator.Asset", "Manufacturer", 98 "string", 5}, 99 {"xyz.openbmc_project.Inventory.Item", "PrettyName", "string", 8}, 100 {"xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 101 "string", 11}, 102 {"xyz.openbmc_project.Inventory.Decorator.Revision", "Version", 103 "string", 10}, 104 }}; 105 106 for (auto [intf, entityType] : intfToEntityType) 107 { 108 recordMap[intf] = {generalRecordInfo}; 109 } 110 } 111 112 void FruParser::setupFruRecordMap(const std::string& dirPath) 113 { 114 for (auto& file : fs::directory_iterator(dirPath)) 115 { 116 auto fileName = file.path().filename().string(); 117 std::ifstream jsonFile(file.path()); 118 auto data = Json::parse(jsonFile, nullptr, false); 119 if (data.is_discarded()) 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