xref: /openbmc/pldm/libpldmresponder/fru.cpp (revision dd04990d)
1 #include "fru.hpp"
2 
3 #include "libpldm/utils.h"
4 
5 #include "common/utils.hpp"
6 
7 #include <systemd/sd-journal.h>
8 
9 #include <sdbusplus/bus.hpp>
10 
11 #include <iostream>
12 #include <set>
13 
14 namespace pldm
15 {
16 
17 namespace responder
18 {
19 
20 void FruImpl::buildFRUTable()
21 {
22 
23     if (isBuilt)
24     {
25         return;
26     }
27 
28     fru_parser::DBusLookupInfo dbusInfo;
29     // Read the all the inventory D-Bus objects
30     auto& bus = pldm::utils::DBusHandler::getBus();
31     dbus::ObjectValueTree objects;
32 
33     try
34     {
35         dbusInfo = parser.inventoryLookup();
36         auto method = bus.new_method_call(
37             std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
38             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
39         auto reply = bus.call(method);
40         reply.read(objects);
41     }
42     catch (const std::exception& e)
43     {
44         std::cerr << "Look up of inventory objects failed and PLDM FRU table "
45                      "creation failed";
46         return;
47     }
48 
49     auto itemIntfsLookup = std::get<2>(dbusInfo);
50 
51     for (const auto& object : objects)
52     {
53         const auto& interfaces = object.second;
54 
55         for (const auto& interface : interfaces)
56         {
57             if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end())
58             {
59                 // An exception will be thrown by getRecordInfo, if the item
60                 // D-Bus interface name specified in FRU_Master.json does
61                 // not have corresponding config jsons
62                 try
63                 {
64                     pldm_entity entity{};
65                     entity.entity_type = parser.getEntityType(interface.first);
66                     pldm_entity_node* parent = nullptr;
67                     auto parentObj = pldm::utils::findParent(object.first.str);
68                     // To add a FRU to the entity association tree, we need to
69                     // determine if the FRU has a parent (D-Bus object). For eg
70                     // /system/backplane's parent is /system. /system has no
71                     // parent. Some D-Bus pathnames might just be namespaces
72                     // (not D-Bus objects), so we need to iterate upwards until
73                     // a parent is found, or we reach the root ("/").
74                     // Parents are always added first before children in the
75                     // entity association tree. We're relying on the fact that
76                     // the std::map containing object paths from the
77                     // GetManagedObjects call will have a sorted pathname list.
78                     do
79                     {
80                         auto iter = objToEntityNode.find(parentObj);
81                         if (iter != objToEntityNode.end())
82                         {
83                             parent = iter->second;
84                             break;
85                         }
86                         parentObj = pldm::utils::findParent(parentObj);
87                     } while (parentObj != "/");
88 
89                     auto node = pldm_entity_association_tree_add(
90                         entityTree, &entity, parent,
91                         PLDM_ENTITY_ASSOCIAION_PHYSICAL);
92                     objToEntityNode[object.first.str] = node;
93 
94                     auto recordInfos = parser.getRecordInfo(interface.first);
95                     populateRecords(interfaces, recordInfos, entity);
96 
97                     associatedEntityMap.emplace(object.first, entity);
98                     break;
99                 }
100                 catch (const std::exception& e)
101                 {
102                     std::cout << "Config JSONs missing for the item "
103                                  "interface type, interface = "
104                               << interface.first << "\n";
105                     break;
106                 }
107             }
108         }
109     }
110 
111     pldm_entity_association_pdr_add(entityTree, pdrRepo, false);
112 
113     if (table.size())
114     {
115         padBytes = utils::getNumPadBytes(table.size());
116         table.resize(table.size() + padBytes, 0);
117 
118         // Calculate the checksum
119         checksum = crc32(table.data(), table.size());
120     }
121     isBuilt = true;
122 }
123 
124 void FruImpl::populateRecords(
125     const pldm::responder::dbus::InterfaceMap& interfaces,
126     const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity)
127 {
128     // recordSetIdentifier for the FRU will be set when the first record gets
129     // added for the FRU
130     uint16_t recordSetIdentifier = 0;
131     auto numRecsCount = numRecs;
132 
133     for (auto const& [recType, encType, fieldInfos] : recordInfos)
134     {
135         std::vector<uint8_t> tlvs;
136         uint8_t numFRUFields = 0;
137         for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos)
138         {
139             try
140             {
141                 auto propValue = interfaces.at(intf).at(prop);
142                 if (propType == "bytearray")
143                 {
144                     auto byteArray = std::get<std::vector<uint8_t>>(propValue);
145                     if (!byteArray.size())
146                     {
147                         continue;
148                     }
149 
150                     numFRUFields++;
151                     tlvs.emplace_back(fieldTypeNum);
152                     tlvs.emplace_back(byteArray.size());
153                     std::move(std::begin(byteArray), std::end(byteArray),
154                               std::back_inserter(tlvs));
155                 }
156                 else if (propType == "string")
157                 {
158                     auto str = std::get<std::string>(propValue);
159                     if (!str.size())
160                     {
161                         continue;
162                     }
163 
164                     numFRUFields++;
165                     tlvs.emplace_back(fieldTypeNum);
166                     tlvs.emplace_back(str.size());
167                     std::move(std::begin(str), std::end(str),
168                               std::back_inserter(tlvs));
169                 }
170             }
171             catch (const std::out_of_range& e)
172             {
173                 continue;
174             }
175         }
176 
177         if (tlvs.size())
178         {
179             if (numRecs == numRecsCount)
180             {
181                 recordSetIdentifier = nextRSI();
182                 pldm_pdr_add_fru_record_set(
183                     pdrRepo, 0, recordSetIdentifier, entity.entity_type,
184                     entity.entity_instance_num, entity.entity_container_id);
185             }
186             auto curSize = table.size();
187             table.resize(curSize + recHeaderSize + tlvs.size());
188             encode_fru_record(table.data(), table.size(), &curSize,
189                               recordSetIdentifier, recType, numFRUFields,
190                               encType, tlvs.data(), tlvs.size());
191             numRecs++;
192         }
193     }
194 }
195 
196 void FruImpl::getFRUTable(Response& response)
197 {
198     auto hdrSize = response.size();
199 
200     response.resize(hdrSize + table.size() + sizeof(checksum), 0);
201     std::copy(table.begin(), table.end(), response.begin() + hdrSize);
202 
203     // Copy the checksum to response data
204     auto iter = response.begin() + hdrSize + table.size();
205     std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
206                 iter);
207 }
208 
209 int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData,
210                                   uint16_t /* fruTableHandle */,
211                                   uint16_t recordSetIdentifer,
212                                   uint8_t recordType, uint8_t fieldType)
213 {
214     // FRU table is built lazily, build if not done.
215     buildFRUTable();
216 
217     /* 7 is sizeof(checksum,4) + padBytesMax(3)
218      * We can not know size of the record table got by options in advance, but
219      * it must be less than the source table. So it's safe to use sizeof the
220      * source table + 7 as the buffer length
221      */
222     size_t recordTableSize = table.size() - padBytes + 7;
223     fruData.resize(recordTableSize, 0);
224 
225     get_fru_record_by_option(table.data(), table.size() - padBytes,
226                              fruData.data(), &recordTableSize,
227                              recordSetIdentifer, recordType, fieldType);
228 
229     if (recordTableSize == 0)
230     {
231         return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
232     }
233 
234     auto pads = utils::getNumPadBytes(recordTableSize);
235     auto sum = crc32(fruData.data(), recordTableSize + pads);
236 
237     auto iter = fruData.begin() + recordTableSize + pads;
238     std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
239                 iter);
240     fruData.resize(recordTableSize + pads + sizeof(sum));
241 
242     return PLDM_SUCCESS;
243 }
244 
245 namespace fru
246 {
247 
248 Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
249                                             size_t /*payloadLength*/)
250 {
251     // FRU table is built lazily, build if not done.
252     buildFRUTable();
253 
254     constexpr uint8_t major = 0x01;
255     constexpr uint8_t minor = 0x00;
256     constexpr uint32_t maxSize = 0xFFFFFFFF;
257 
258     Response response(sizeof(pldm_msg_hdr) +
259                           PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES,
260                       0);
261     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
262 
263     auto rc = encode_get_fru_record_table_metadata_resp(
264         request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize,
265         impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(),
266         responsePtr);
267     if (rc != PLDM_SUCCESS)
268     {
269         return ccOnlyResponse(request, rc);
270     }
271 
272     return response;
273 }
274 
275 Response Handler::getFRURecordTable(const pldm_msg* request,
276                                     size_t payloadLength)
277 {
278     // FRU table is built lazily, build if not done.
279     buildFRUTable();
280 
281     if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
282     {
283         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
284     }
285 
286     Response response(
287         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0);
288     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
289 
290     auto rc =
291         encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
292                                          0, PLDM_START_AND_END, responsePtr);
293     if (rc != PLDM_SUCCESS)
294     {
295         return ccOnlyResponse(request, rc);
296     }
297 
298     impl.getFRUTable(response);
299 
300     return response;
301 }
302 
303 Response Handler::getFRURecordByOption(const pldm_msg* request,
304                                        size_t payloadLength)
305 {
306     if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
307     {
308         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
309     }
310 
311     uint32_t retDataTransferHandle{};
312     uint16_t retFruTableHandle{};
313     uint16_t retRecordSetIdentifier{};
314     uint8_t retRecordType{};
315     uint8_t retFieldType{};
316     uint8_t retTransferOpFlag{};
317 
318     auto rc = decode_get_fru_record_by_option_req(
319         request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
320         &retRecordSetIdentifier, &retRecordType, &retFieldType,
321         &retTransferOpFlag);
322 
323     if (rc != PLDM_SUCCESS)
324     {
325         return ccOnlyResponse(request, rc);
326     }
327 
328     std::vector<uint8_t> fruData;
329     rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
330                                    retRecordSetIdentifier, retRecordType,
331                                    retFieldType);
332     if (rc != PLDM_SUCCESS)
333     {
334         return ccOnlyResponse(request, rc);
335     }
336 
337     auto respPayloadLength =
338         PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size();
339     Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
340     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
341 
342     rc = encode_get_fru_record_by_option_resp(
343         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
344         fruData.data(), fruData.size(), responsePtr, respPayloadLength);
345 
346     if (rc != PLDM_SUCCESS)
347     {
348         return ccOnlyResponse(request, rc);
349     }
350 
351     return response;
352 }
353 
354 } // namespace fru
355 
356 } // namespace responder
357 
358 } // namespace pldm
359