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