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