xref: /openbmc/pldm/libpldmresponder/fru.cpp (revision 1f4df219)
1 #include "fru.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <systemd/sd-journal.h>
6 
7 #include <boost/crc.hpp>
8 #include <iostream>
9 #include <sdbusplus/bus.hpp>
10 #include <set>
11 
12 namespace pldm
13 {
14 
15 namespace responder
16 {
17 
18 FruImpl::FruImpl(const std::string& configPath)
19 {
20     fru_parser::FruParser handle(configPath);
21 
22     fru_parser::DBusLookupInfo dbusInfo;
23     // Read the all the inventory D-Bus objects
24     auto& bus = pldm::utils::DBusHandler::getBus();
25     dbus::ObjectValueTree objects;
26 
27     try
28     {
29         dbusInfo = handle.inventoryLookup();
30         auto method = bus.new_method_call(
31             std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
32             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
33         auto reply = bus.call(method);
34         reply.read(objects);
35     }
36     catch (const std::exception& e)
37     {
38         std::cerr << "Look up of inventory objects failed and PLDM FRU table "
39                      "creation failed";
40         return;
41     }
42 
43     // Populate all the interested Item types to a map for easy lookup
44     std::set<dbus::Interface> itemIntfsLookup;
45     auto itemIntfs = std::get<2>(dbusInfo);
46     std::transform(std::begin(itemIntfs), std::end(itemIntfs),
47                    std::inserter(itemIntfsLookup, itemIntfsLookup.end()),
48                    [](dbus::Interface intf) { return intf; });
49 
50     for (const auto& object : objects)
51     {
52         const auto& interfaces = object.second;
53 
54         for (const auto& interface : interfaces)
55         {
56             if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end())
57             {
58                 // An exception will be thrown by getRecordInfo, if the item
59                 // D-Bus interface name specified in FRU_Master.json does
60                 // not have corresponding config jsons
61                 try
62                 {
63                     auto recordInfos = handle.getRecordInfo(interface.first);
64                     populateRecords(interfaces, recordInfos);
65                 }
66                 catch (const std::exception& e)
67                 {
68                     std::cout << "Config JSONs missing for the item "
69                                  "interface type, interface = "
70                               << interface.first << "\n";
71                     break;
72                 }
73             }
74         }
75     }
76 
77     if (table.size())
78     {
79         padBytes = utils::getNumPadBytes(table.size());
80         table.resize(table.size() + padBytes, 0);
81 
82         // Calculate the checksum
83         boost::crc_32_type result;
84         result.process_bytes(table.data(), table.size());
85         checksum = result.checksum();
86     }
87 }
88 
89 void FruImpl::populateRecords(
90     const pldm::responder::dbus::InterfaceMap& interfaces,
91     const fru_parser::FruRecordInfos& recordInfos)
92 {
93     // recordSetIdentifier for the FRU will be set when the first record gets
94     // added for the FRU
95     uint16_t recordSetIdentifier = 0;
96     auto numRecsCount = numRecs;
97 
98     for (auto const& [recType, encType, fieldInfos] : recordInfos)
99     {
100         std::vector<uint8_t> tlvs;
101         uint8_t numFRUFields = 0;
102         for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos)
103         {
104             try
105             {
106                 auto propValue = interfaces.at(intf).at(prop);
107                 if (propType == "bytearray")
108                 {
109                     auto byteArray = std::get<std::vector<uint8_t>>(propValue);
110                     if (!byteArray.size())
111                     {
112                         continue;
113                     }
114 
115                     numFRUFields++;
116                     tlvs.emplace_back(fieldTypeNum);
117                     tlvs.emplace_back(byteArray.size());
118                     std::move(std::begin(byteArray), std::end(byteArray),
119                               std::back_inserter(tlvs));
120                 }
121                 else if (propType == "string")
122                 {
123                     auto str = std::get<std::string>(propValue);
124                     if (!str.size())
125                     {
126                         continue;
127                     }
128 
129                     numFRUFields++;
130                     tlvs.emplace_back(fieldTypeNum);
131                     tlvs.emplace_back(str.size());
132                     std::move(std::begin(str), std::end(str),
133                               std::back_inserter(tlvs));
134                 }
135             }
136             catch (const std::out_of_range& e)
137             {
138                 std::cout << "Interface = " << intf
139                           << " , and Property = " << prop
140                           << " , in config json not present in D-Bus"
141                           << "\n";
142                 continue;
143             }
144         }
145 
146         if (tlvs.size())
147         {
148             if (numRecs == numRecsCount)
149             {
150                 recordSetIdentifier = nextRSI();
151             }
152             auto curSize = table.size();
153             table.resize(curSize + recHeaderSize + tlvs.size());
154             encode_fru_record(table.data(), table.size(), &curSize,
155                               recordSetIdentifier, recType, numFRUFields,
156                               encType, tlvs.data(), tlvs.size());
157             numRecs++;
158         }
159     }
160 }
161 
162 void FruImpl::getFRUTable(Response& response)
163 {
164     auto hdrSize = response.size();
165 
166     response.resize(hdrSize + table.size() + sizeof(checksum), 0);
167     std::copy(table.begin(), table.end(), response.begin() + hdrSize);
168 
169     // Copy the checksum to response data
170     auto iter = response.begin() + hdrSize + table.size();
171     std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
172                 iter);
173 }
174 
175 namespace fru
176 {
177 
178 Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
179                                             size_t /*payloadLength*/)
180 {
181     constexpr uint8_t major = 0x01;
182     constexpr uint8_t minor = 0x00;
183     constexpr uint32_t maxSize = 0xFFFFFFFF;
184 
185     Response response(sizeof(pldm_msg_hdr) +
186                           PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES,
187                       0);
188     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
189 
190     auto rc = encode_get_fru_record_table_metadata_resp(
191         request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize,
192         impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(),
193         responsePtr);
194     if (rc != PLDM_SUCCESS)
195     {
196         return ccOnlyResponse(request, rc);
197     }
198 
199     return response;
200 }
201 
202 Response Handler::getFRURecordTable(const pldm_msg* request,
203                                     size_t payloadLength)
204 {
205     if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
206     {
207         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
208     }
209 
210     Response response(
211         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0);
212     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
213 
214     auto rc =
215         encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
216                                          0, PLDM_START_AND_END, responsePtr);
217     if (rc != PLDM_SUCCESS)
218     {
219         return ccOnlyResponse(request, rc);
220     }
221 
222     impl.getFRUTable(response);
223 
224     return response;
225 }
226 
227 } // namespace fru
228 
229 } // namespace responder
230 
231 } // namespace pldm
232