xref: /openbmc/pldm/libpldmresponder/fru_parser.cpp (revision d4a14d3ae2d07c214cee0ce77b1a0f002abb3ab2)
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