1 #include "fru_parser.hpp" 2 3 #include <nlohmann/json.hpp> 4 #include <phosphor-logging/lg2.hpp> 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <filesystem> 8 #include <fstream> 9 #include <iostream> 10 11 PHOSPHOR_LOG2_USING; 12 13 using namespace pldm::responder::dbus; 14 15 namespace pldm 16 { 17 namespace responder 18 { 19 namespace fru_parser 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 28 FruParser::FruParser(const std::string& dirPath, 29 const fs::path& fruMasterJsonPath) 30 { 31 if (fs::exists(fruMasterJsonPath)) 32 { 33 setupDefaultDBusLookup(fruMasterJsonPath); 34 } 35 setupDefaultFruRecordMap(); 36 37 fs::path dir(dirPath); 38 if (fs::exists(dir) && !fs::is_empty(dir)) 39 { 40 setupFruRecordMap(dirPath); 41 } 42 } 43 44 void FruParser::setupDefaultDBusLookup(const fs::path& masterJsonPath) 45 { 46 constexpr auto service = "xyz.openbmc_project.Inventory.Manager"; 47 constexpr auto rootPath = "/xyz/openbmc_project/inventory"; 48 std::ifstream jsonFile(masterJsonPath); 49 auto data = Json::parse(jsonFile, nullptr, false); 50 if (data.is_discarded()) 51 { 52 error( 53 "Parsing FRU Dbus Lookup Map config file failed, FILE={JSON_PATH}", 54 "JSON_PATH", masterJsonPath.c_str()); 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 error("FRU DBus lookup map format error"); 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 error("Parsing FRU config file failed, FILE={FILE_PATH}", 122 "FILE_PATH", file.path().c_str()); 123 throw InternalFailure(); 124 } 125 126 try 127 { 128 auto record = data.value("record_details", emptyJson); 129 auto recordType = 130 static_cast<uint8_t>(record.value("fru_record_type", 0)); 131 auto encType = 132 static_cast<uint8_t>(record.value("fru_encoding_type", 0)); 133 auto dbusIntfName = record.value("dbus_interface_name", ""); 134 auto entries = data.value("fru_fields", emptyJsonList); 135 std::vector<FieldInfo> fieldInfo; 136 137 for (const auto& entry : entries) 138 { 139 auto fieldType = 140 static_cast<uint8_t>(entry.value("fru_field_type", 0)); 141 auto dbus = entry.value("dbus", emptyJson); 142 auto interface = dbus.value("interface", ""); 143 auto property = dbus.value("property_name", ""); 144 auto propType = dbus.value("property_type", ""); 145 fieldInfo.emplace_back( 146 std::make_tuple(std::move(interface), std::move(property), 147 std::move(propType), std::move(fieldType))); 148 } 149 150 FruRecordInfo fruInfo; 151 fruInfo = std::make_tuple(recordType, encType, 152 std::move(fieldInfo)); 153 154 auto search = recordMap.find(dbusIntfName); 155 156 // PLDM FRU can have multiple records for the same FRU like General 157 // FRU record and multiple OEM FRU records. If the FRU item 158 // interface name is already in the map, that indicates a record 159 // info is already added for the FRU, so append the new record info 160 // to the same data. 161 if (search != recordMap.end()) 162 { 163 search->second.emplace_back(std::move(fruInfo)); 164 } 165 else 166 { 167 FruRecordInfos recordInfos{fruInfo}; 168 recordMap.emplace(dbusIntfName, recordInfos); 169 } 170 } 171 catch (const std::exception& e) 172 { 173 continue; 174 } 175 } 176 } 177 178 } // namespace fru_parser 179 180 } // namespace responder 181 182 } // namespace pldm 183