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