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