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