xref: /openbmc/pldm/libpldmresponder/fru.cpp (revision 28ba36aa9ecc3b4e8f2c2f8b2821dd8ec8c83d78)
1 #include "fru.hpp"
2 
3 #include "common/utils.hpp"
4 #include "libpldmresponder/platform.hpp"
5 
6 #include <libpldm/entity.h>
7 #include <libpldm/utils.h>
8 #include <systemd/sd-journal.h>
9 
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdbusplus/bus.hpp>
12 #include <xyz/openbmc_project/Association/common.hpp>
13 #include <xyz/openbmc_project/Software/Version/client.hpp>
14 
15 #include <optional>
16 #include <set>
17 #include <stack>
18 
19 PHOSPHOR_LOG2_USING;
20 
21 using SoftwareVersion =
22     sdbusplus::common::xyz::openbmc_project::software::Version;
23 using Association = sdbusplus::common::xyz::openbmc_project::Association;
24 
25 namespace pldm
26 {
27 namespace responder
28 {
29 
30 constexpr auto root = "/xyz/openbmc_project/inventory/";
31 
getEntityByObjectPath(const dbus::InterfaceMap & intfMaps)32 std::optional<pldm_entity> FruImpl::getEntityByObjectPath(
33     const dbus::InterfaceMap& intfMaps)
34 {
35     for (const auto& intfMap : intfMaps)
36     {
37         try
38         {
39             pldm_entity entity{};
40             entity.entity_type = parser.getEntityType(intfMap.first);
41             return entity;
42         }
43         catch (const std::exception&)
44         {
45             continue;
46         }
47     }
48 
49     return std::nullopt;
50 }
51 
updateAssociationTree(const dbus::ObjectValueTree & objects,const std::string & path)52 void FruImpl::updateAssociationTree(const dbus::ObjectValueTree& objects,
53                                     const std::string& path)
54 {
55     if (path.find(root) == std::string::npos)
56     {
57         return;
58     }
59 
60     std::stack<std::string> tmpObjPaths{};
61     tmpObjPaths.emplace(path);
62 
63     auto obj = pldm::utils::findParent(path);
64     while ((obj + '/') != root)
65     {
66         tmpObjPaths.emplace(obj);
67         obj = pldm::utils::findParent(obj);
68     }
69 
70     std::stack<std::string> tmpObj = tmpObjPaths;
71     while (!tmpObj.empty())
72     {
73         std::string s = tmpObj.top();
74         tmpObj.pop();
75     }
76     // Update pldm entity to association tree
77     std::string prePath = tmpObjPaths.top();
78     while (!tmpObjPaths.empty())
79     {
80         std::string currPath = tmpObjPaths.top();
81         tmpObjPaths.pop();
82 
83         do
84         {
85             if (objToEntityNode.contains(currPath))
86             {
87                 pldm_entity node =
88                     pldm_entity_extract(objToEntityNode.at(currPath));
89                 if (pldm_entity_association_tree_find_with_locality(
90                         entityTree, &node, false))
91                 {
92                     break;
93                 }
94             }
95             else
96             {
97                 if (!objects.contains(currPath))
98                 {
99                     break;
100                 }
101 
102                 auto entityPtr = getEntityByObjectPath(objects.at(currPath));
103                 if (!entityPtr)
104                 {
105                     break;
106                 }
107 
108                 pldm_entity entity = *entityPtr;
109 
110                 for (const auto& it : objToEntityNode)
111                 {
112                     pldm_entity node = pldm_entity_extract(it.second);
113                     if (node.entity_type == entity.entity_type)
114                     {
115                         entity.entity_instance_num =
116                             node.entity_instance_num + 1;
117                         break;
118                     }
119                 }
120 
121                 if (currPath == prePath)
122                 {
123                     auto node = pldm_entity_association_tree_add_entity(
124                         entityTree, &entity, 0xFFFF, nullptr,
125                         PLDM_ENTITY_ASSOCIAION_PHYSICAL, false, true, 0xFFFF);
126                     objToEntityNode[currPath] = node;
127                 }
128                 else
129                 {
130                     if (objToEntityNode.contains(prePath))
131                     {
132                         auto node = pldm_entity_association_tree_add_entity(
133                             entityTree, &entity, 0xFFFF,
134                             objToEntityNode[prePath],
135                             PLDM_ENTITY_ASSOCIAION_PHYSICAL, false, true,
136                             0xFFFF);
137                         objToEntityNode[currPath] = node;
138                     }
139                 }
140             }
141         } while (0);
142 
143         prePath = currPath;
144     }
145 }
146 
buildFRUTable()147 void FruImpl::buildFRUTable()
148 {
149     if (isBuilt)
150     {
151         return;
152     }
153 
154     fru_parser::DBusLookupInfo dbusInfo;
155 
156     try
157     {
158         dbusInfo = parser.inventoryLookup();
159         objects = pldm::utils::DBusHandler::getInventoryObjects<
160             pldm::utils::DBusHandler>();
161     }
162     catch (const std::exception& e)
163     {
164         error(
165             "Failed to build FRU table due to inventory lookup, error - {ERROR}",
166             "ERROR", e);
167         return;
168     }
169 
170     auto itemIntfsLookup = std::get<2>(dbusInfo);
171 
172     for (const auto& object : objects)
173     {
174         const auto& interfaces = object.second;
175         for (const auto& interface : interfaces)
176         {
177             if (itemIntfsLookup.contains(interface.first))
178             {
179                 // checking fru present property is available or not.
180                 if (!pldm::utils::checkForFruPresence(object.first.str))
181                 {
182                     continue;
183                 }
184 
185                 // An exception will be thrown by getRecordInfo, if the item
186                 // D-Bus interface name specified in FRU_Master.json does
187                 // not have corresponding config jsons
188                 try
189                 {
190                     updateAssociationTree(objects, object.first.str);
191                     pldm_entity entity{};
192                     if (objToEntityNode.contains(object.first.str))
193                     {
194                         pldm_entity_node* node =
195                             objToEntityNode.at(object.first.str);
196 
197                         entity = pldm_entity_extract(node);
198                     }
199 
200                     auto recordInfos = parser.getRecordInfo(interface.first);
201                     populateRecords(interfaces, recordInfos, entity);
202 
203                     associatedEntityMap.emplace(object.first, entity);
204                     break;
205                 }
206                 catch (const std::exception& e)
207                 {
208                     error(
209                         "Config JSONs missing for the item '{INTERFACE}', error - {ERROR}",
210                         "INTERFACE", interface.first, "ERROR", e);
211                     break;
212                 }
213             }
214         }
215     }
216 
217     int rc = pldm_entity_association_pdr_add(entityTree, pdrRepo, false,
218                                              TERMINUS_HANDLE);
219     if (rc < 0)
220     {
221         // pldm_entity_assocation_pdr_add() assert()ed on failure
222         error("Failed to add PLDM entity association PDR, response code '{RC}'",
223               "RC", rc);
224         throw std::runtime_error("Failed to add PLDM entity association PDR");
225     }
226 
227     // save a copy of bmc's entity association tree
228     pldm_entity_association_tree_copy_root(entityTree, bmcEntityTree);
229 
230     isBuilt = true;
231 }
populatefwVersion()232 std::string FruImpl::populatefwVersion()
233 {
234     static constexpr auto fwFunctionalObjPath =
235         "/xyz/openbmc_project/software/functional";
236     auto& bus = pldm::utils::DBusHandler::getBus();
237     std::string currentBmcVersion;
238     try
239     {
240         auto method =
241             bus.new_method_call(pldm::utils::mapperService, fwFunctionalObjPath,
242                                 pldm::utils::dbusProperties, "Get");
243         method.append(Association::interface,
244                       Association::property_names::endpoints);
245         std::variant<std::vector<std::string>> paths;
246         auto reply = bus.call(method, dbusTimeout);
247         reply.read(paths);
248         auto fwRunningVersion = std::get<std::vector<std::string>>(paths)[0];
249         auto version = pldm::utils::DBusHandler().getDbusPropertyVariant(
250             fwRunningVersion.c_str(), SoftwareVersion::property_names::version,
251             SoftwareVersion::interface);
252         currentBmcVersion = std::get<std::string>(version);
253     }
254     catch (const std::exception& e)
255     {
256         error("Failed to make a d-bus call Association, error - {ERROR}",
257               "ERROR", e);
258         return {};
259     }
260     return currentBmcVersion;
261 }
populateRecords(const pldm::responder::dbus::InterfaceMap & interfaces,const fru_parser::FruRecordInfos & recordInfos,const pldm_entity & entity)262 void FruImpl::populateRecords(
263     const pldm::responder::dbus::InterfaceMap& interfaces,
264     const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity)
265 {
266     // recordSetIdentifier for the FRU will be set when the first record gets
267     // added for the FRU
268     uint16_t recordSetIdentifier = 0;
269     auto numRecsCount = numRecs;
270     static uint32_t bmc_record_handle = 0;
271 
272     for (const auto& [recType, encType, fieldInfos] : recordInfos)
273     {
274         std::vector<uint8_t> tlvs;
275         uint8_t numFRUFields = 0;
276         for (const auto& [intf, prop, propType, fieldTypeNum] : fieldInfos)
277         {
278             try
279             {
280                 pldm::responder::dbus::Value propValue;
281 
282                 // Assuming that 0 container Id is assigned to the System (as
283                 // that should be the top most container as per dbus hierarchy)
284                 if (entity.entity_container_id == 0 && prop == "Version")
285                 {
286                     propValue = populatefwVersion();
287                 }
288                 else
289                 {
290                     propValue = interfaces.at(intf).at(prop);
291                 }
292                 if (propType == "bytearray")
293                 {
294                     auto byteArray = std::get<std::vector<uint8_t>>(propValue);
295                     if (!byteArray.size())
296                     {
297                         continue;
298                     }
299 
300                     numFRUFields++;
301                     tlvs.emplace_back(fieldTypeNum);
302                     tlvs.emplace_back(byteArray.size());
303                     std::move(std::begin(byteArray), std::end(byteArray),
304                               std::back_inserter(tlvs));
305                 }
306                 else if (propType == "string")
307                 {
308                     auto str = std::get<std::string>(propValue);
309                     if (!str.size())
310                     {
311                         continue;
312                     }
313 
314                     numFRUFields++;
315                     tlvs.emplace_back(fieldTypeNum);
316                     tlvs.emplace_back(str.size());
317                     std::move(std::begin(str), std::end(str),
318                               std::back_inserter(tlvs));
319                 }
320             }
321             catch (const std::out_of_range&)
322             {
323                 continue;
324             }
325         }
326 
327         if (tlvs.size())
328         {
329             if (numRecs == numRecsCount)
330             {
331                 recordSetIdentifier = nextRSI();
332                 bmc_record_handle = nextRecordHandle();
333                 int rc = pldm_pdr_add_fru_record_set(
334                     pdrRepo, TERMINUS_HANDLE, recordSetIdentifier,
335                     entity.entity_type, entity.entity_instance_num,
336                     entity.entity_container_id, &bmc_record_handle);
337                 if (rc)
338                 {
339                     // pldm_pdr_add_fru_record_set() assert()ed on failure
340                     throw std::runtime_error(
341                         "Failed to add PDR FRU record set");
342                 }
343             }
344             auto curSize = table.size();
345             table.resize(curSize + recHeaderSize + tlvs.size());
346             encode_fru_record(table.data(), table.size(), &curSize,
347                               recordSetIdentifier, recType, numFRUFields,
348                               encType, tlvs.data(), tlvs.size());
349             numRecs++;
350         }
351     }
352 }
353 
deleteFRURecord(uint16_t rsi)354 void FruImpl::deleteFRURecord(uint16_t rsi)
355 {
356     std::vector<uint8_t> updatedFruTbl;
357     size_t pos = 0;
358 
359     while (pos < table.size())
360     {
361         // Ensure enough space for the record header
362         if ((table.size() - pos) < sizeof(struct pldm_fru_record_data_format))
363         {
364             // Log or handle corrupt/truncated record
365             error("Error: Incomplete FRU record header");
366             return;
367         }
368 
369         auto recordSetSrc =
370             reinterpret_cast<const struct pldm_fru_record_data_format*>(
371                 &table[pos]);
372 
373         size_t recordLen = sizeof(struct pldm_fru_record_data_format) -
374                            sizeof(struct pldm_fru_record_tlv);
375 
376         const struct pldm_fru_record_tlv* tlv = recordSetSrc->tlvs;
377 
378         for (uint8_t i = 0; i < recordSetSrc->num_fru_fields; ++i)
379         {
380             if ((table.size() - pos) < (recordLen + sizeof(*tlv)))
381             {
382                 error("Error: Incomplete TLV header");
383                 return;
384             }
385 
386             size_t len = sizeof(*tlv) - 1 + tlv->length;
387 
388             if ((table.size() - pos) < (recordLen + len))
389             {
390                 error("Error: Incomplete TLV data");
391                 return;
392             }
393 
394             recordLen += len;
395 
396             // Advance to next tlv
397             tlv = reinterpret_cast<const struct pldm_fru_record_tlv*>(
398                 reinterpret_cast<const uint8_t*>(tlv) + len);
399         }
400 
401         if ((le16toh(recordSetSrc->record_set_id) != rsi && rsi != 0))
402         {
403             std::copy(table.begin() + pos, table.begin() + pos + recordLen,
404                       std::back_inserter(updatedFruTbl));
405         }
406         else
407         {
408             // Deleted record
409             numRecs--;
410         }
411 
412         pos += recordLen;
413     }
414     // Replace the old table with the updated one
415     table = std::move(updatedFruTbl);
416 }
417 
removeIndividualFRU(const std::string & fruObjPath)418 void FruImpl::removeIndividualFRU(const std::string& fruObjPath)
419 {
420     uint16_t rsi = objectPathToRSIMap[fruObjPath];
421     if (!rsi)
422     {
423         info("No Pdrs to delete for the object path {PATH}", "PATH",
424              fruObjPath);
425         return;
426     }
427     pldm_entity removeEntity;
428     uint16_t terminusHdl{};
429     uint16_t entityType{};
430     uint16_t entityInsNum{};
431     uint16_t containerId{};
432     uint32_t updateRecordHdlBmc = 0;
433     uint32_t updateRecordHdlHost = 0;
434     uint32_t deleteRecordHdl = 0;
435     bool hasError = false;
436 
437     auto fruRecord = pldm_pdr_fru_record_set_find_by_rsi(
438         pdrRepo, rsi, &terminusHdl, &entityType, &entityInsNum, &containerId);
439 
440     if (fruRecord == nullptr)
441     {
442         error("No matching FRU record found for RSI {RSI}", "RSI", rsi);
443         hasError = true;
444         return;
445     }
446 
447     removeEntity = {entityType, entityInsNum, containerId};
448 
449     auto removeBmcEntityRc =
450         pldm_entity_association_pdr_remove_contained_entity(
451             pdrRepo, &removeEntity, false, &updateRecordHdlBmc);
452     if (removeBmcEntityRc)
453     {
454         hasError = true;
455         error(
456             "Failed to remove entity [Type={TYPE}, Instance={INS}, Container={CONT}] "
457             "from BMC PDR. RC = {RC}",
458             "TYPE", static_cast<unsigned>(removeEntity.entity_type), "INS",
459             static_cast<unsigned>(removeEntity.entity_instance_num), "CONT",
460             static_cast<unsigned>(removeEntity.entity_container_id), "RC",
461             static_cast<int>(removeBmcEntityRc));
462     }
463 
464     pldm::responder::pdr_utils::PdrEntry pdrEntry;
465     uint8_t* pdrData = nullptr;
466     auto record =
467         pldm_pdr_find_record(pdrRepo, updateRecordHdlBmc, &pdrData,
468                              &pdrEntry.size, &pdrEntry.handle.nextRecordHandle);
469     if (record)
470     {
471         info("Found BMC Record {REC}", "REC", updateRecordHdlBmc);
472     }
473     auto bmcEventDataOps =
474         record ? PLDM_RECORDS_MODIFIED : PLDM_RECORDS_DELETED;
475 
476     int removeHostEntityRc = -1;
477     uint8_t hostEventDataOps = 0;
478     if (!hasError)
479     {
480         removeHostEntityRc =
481             pldm_entity_association_pdr_remove_contained_entity(
482                 pdrRepo, &removeEntity, true, &updateRecordHdlHost);
483         if (removeHostEntityRc)
484         {
485             hasError = true;
486             error(
487                 "Failed to remove entity [Type={TYPE}, Instance={INS}, Container={CONT}] "
488                 "from Host PDR. RC = {RC}",
489                 "TYPE", static_cast<unsigned>(removeEntity.entity_type), "INS",
490                 static_cast<unsigned>(removeEntity.entity_instance_num), "CONT",
491                 static_cast<unsigned>(removeEntity.entity_container_id), "RC",
492                 static_cast<int>(removeHostEntityRc));
493         }
494 
495         record = pldm_pdr_find_record(pdrRepo, updateRecordHdlHost, &pdrData,
496                                       &pdrEntry.size,
497                                       &pdrEntry.handle.nextRecordHandle);
498         if (record)
499         {
500             info("Found Host Record {REC}", "REC", updateRecordHdlHost);
501         }
502 
503         hostEventDataOps = record ? PLDM_RECORDS_MODIFIED
504                                   : PLDM_RECORDS_DELETED;
505     }
506     if (hasError)
507     {
508         error("Partial failure occurred while removing FRU {FRU_OBJ_PATH}",
509               "FRU_OBJ_PATH", fruObjPath);
510         return;
511     }
512 
513     auto rc = pldm_pdr_remove_fru_record_set_by_rsi(pdrRepo, rsi, false,
514                                                     &deleteRecordHdl);
515     if (rc)
516     {
517         hasError = true;
518         error("Failed to remove FRU record set for RSI {RSI}. RC = {RC}", "RSI",
519               rsi, "RC", rc);
520     }
521 
522     if (!hasError)
523     {
524         auto rc =
525             pldm_entity_association_tree_delete_node(entityTree, &removeEntity);
526         if (rc)
527         {
528             hasError = true;
529             error("Failed to delete entity from association tree. RC = {RC}",
530                   "RC", rc);
531         }
532 
533         rc = pldm_entity_association_tree_delete_node(bmcEntityTree,
534                                                       &removeEntity);
535         if (rc)
536         {
537             hasError = true;
538             error(
539                 "Failed to delete entity from BMC association tree. RC = {RC}",
540                 "RC", rc);
541         }
542     }
543 
544     if (hasError)
545     {
546         error("Partial failure occurred while removing FRU {FRU_OBJ_PATH}",
547               "FRU_OBJ_PATH", fruObjPath);
548         return;
549     }
550 
551     objectPathToRSIMap.erase(fruObjPath);
552     objToEntityNode.erase(fruObjPath);
553     info(
554         "Removing Individual FRU [ {FRU_OBJ_PATH} ] with entityid [ {ENTITY_TYPE}, {ENTITY_NUM}, {ENTITY_ID} ]",
555         "FRU_OBJ_PATH", fruObjPath, "ENTITY_TYPE",
556         static_cast<unsigned>(removeEntity.entity_type), "ENTITY_NUM",
557         static_cast<unsigned>(removeEntity.entity_instance_num), "ENTITY_ID",
558         static_cast<unsigned>(removeEntity.entity_container_id));
559     associatedEntityMap.erase(fruObjPath);
560 
561     deleteFRURecord(rsi);
562 
563     std::vector<ChangeEntry> handlesTobeDeleted;
564     if (deleteRecordHdl != 0)
565     {
566         handlesTobeDeleted.push_back(deleteRecordHdl);
567     }
568 
569     std::vector<uint16_t> effecterIDs = pldm::utils::findEffecterIds(
570         pdrRepo, removeEntity.entity_type, removeEntity.entity_instance_num,
571         removeEntity.entity_container_id);
572 
573     for (const auto& ids : effecterIDs)
574     {
575         uint32_t delEffecterHdl = 0;
576         int rc = pldm_pdr_delete_by_effecter_id(pdrRepo, ids, false,
577                                                 &delEffecterHdl);
578 
579         if (rc != 0)
580         {
581             error("Failed to delete PDR by effecter ID {ID}. RC = {RC}", "ID",
582                   ids, "RC", rc);
583             continue;
584         }
585         effecterDbusObjMaps.erase(ids);
586         if (delEffecterHdl != 0)
587         {
588             handlesTobeDeleted.push_back(delEffecterHdl);
589         }
590     }
591     std::vector<uint16_t> sensorIDs = pldm::utils::findSensorIds(
592         pdrRepo, removeEntity.entity_type, removeEntity.entity_instance_num,
593         removeEntity.entity_container_id);
594 
595     for (const auto& ids : sensorIDs)
596     {
597         uint32_t delSensorHdl = 0;
598         int rc =
599             pldm_pdr_delete_by_sensor_id(pdrRepo, ids, false, &delSensorHdl);
600 
601         if (rc != 0)
602         {
603             error("Failed to delete PDR by sensor ID {ID}. RC = {RC}", "ID",
604                   ids, "RC", rc);
605             continue;
606         }
607         sensorDbusObjMaps.erase(ids);
608         if (delSensorHdl != 0)
609         {
610             handlesTobeDeleted.push_back(delSensorHdl);
611         }
612     }
613 
614     // need to send both remote and local records. Host keeps track of BMC
615     // only records
616     std::vector<ChangeEntry> handlesTobeModified;
617     if (removeBmcEntityRc == 0 && updateRecordHdlBmc != 0)
618     {
619         (bmcEventDataOps == PLDM_RECORDS_DELETED)
620             ? handlesTobeDeleted.push_back(updateRecordHdlBmc)
621             : handlesTobeModified.push_back(updateRecordHdlBmc);
622     }
623     if (removeHostEntityRc == 0 && updateRecordHdlHost != 0)
624     {
625         (hostEventDataOps == PLDM_RECORDS_DELETED)
626             ? handlesTobeDeleted.push_back(updateRecordHdlHost)
627             : handlesTobeModified.push_back(updateRecordHdlHost);
628     }
629     // Adapter PDRs can have deleted records
630     if (!handlesTobeDeleted.empty())
631     {
632         platformHandler->sendPDRRepositoryChgEventbyPDRHandles(
633             handlesTobeDeleted, std::vector<uint8_t>{PLDM_RECORDS_DELETED});
634     }
635     if (!handlesTobeModified.empty())
636     {
637         platformHandler->sendPDRRepositoryChgEventbyPDRHandles(
638             handlesTobeModified, std::vector<uint8_t>{PLDM_RECORDS_MODIFIED});
639     }
640 }
641 
tableResize()642 std::vector<uint8_t> FruImpl::tableResize()
643 {
644     std::vector<uint8_t> tempTable;
645 
646     if (table.size())
647     {
648         std::copy(table.begin(), table.end(), std::back_inserter(tempTable));
649         padBytes = pldm::utils::getNumPadBytes(table.size());
650         tempTable.resize(tempTable.size() + padBytes, 0);
651     }
652     return tempTable;
653 }
654 
getFRUTable(Response & response)655 void FruImpl::getFRUTable(Response& response)
656 {
657     auto hdrSize = response.size();
658     std::vector<uint8_t> tempTable;
659 
660     if (table.size())
661     {
662         tempTable = tableResize();
663         checksum = pldm_edac_crc32(tempTable.data(), tempTable.size());
664     }
665     response.resize(hdrSize + tempTable.size() + sizeof(checksum), 0);
666     std::copy(tempTable.begin(), tempTable.end(), response.begin() + hdrSize);
667 
668     // Copy the checksum to response data
669     auto iter = response.begin() + hdrSize + tempTable.size();
670     std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
671                 iter);
672 }
673 
getFRURecordTableMetadata()674 void FruImpl::getFRURecordTableMetadata()
675 {
676     std::vector<uint8_t> tempTable;
677     if (table.size())
678     {
679         tempTable = tableResize();
680         checksum = pldm_edac_crc32(tempTable.data(), tempTable.size());
681     }
682 }
683 
getFRURecordByOption(std::vector<uint8_t> & fruData,uint16_t,uint16_t recordSetIdentifer,uint8_t recordType,uint8_t fieldType)684 int FruImpl::getFRURecordByOption(
685     std::vector<uint8_t>& fruData, uint16_t /* fruTableHandle */,
686     uint16_t recordSetIdentifer, uint8_t recordType, uint8_t fieldType)
687 {
688     using sum = uint32_t;
689 
690     // FRU table is built lazily, build if not done.
691     buildFRUTable();
692 
693     /* 7 is sizeof(checksum,4) + padBytesMax(3)
694      * We can not know size of the record table got by options in advance, but
695      * it must be less than the source table. So it's safe to use sizeof the
696      * source table + 7 as the buffer length
697      */
698     size_t recordTableSize = table.size() - padBytes + 7;
699     fruData.resize(recordTableSize, 0);
700 
701     int rc = get_fru_record_by_option(
702         table.data(), table.size() - padBytes, fruData.data(), &recordTableSize,
703         recordSetIdentifer, recordType, fieldType);
704 
705     if (rc != PLDM_SUCCESS || recordTableSize == 0)
706     {
707         return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
708     }
709 
710     auto pads = pldm::utils::getNumPadBytes(recordTableSize);
711     pldm_edac_crc32(fruData.data(), recordTableSize + pads);
712 
713     auto iter = fruData.begin() + recordTableSize + pads;
714     std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
715                 iter);
716     fruData.resize(recordTableSize + pads + sizeof(sum));
717 
718     return PLDM_SUCCESS;
719 }
720 
setFRUTable(const std::vector<uint8_t> & fruData)721 int FruImpl::setFRUTable(const std::vector<uint8_t>& fruData)
722 {
723     auto record =
724         reinterpret_cast<const pldm_fru_record_data_format*>(fruData.data());
725     if (record)
726     {
727         if (oemFruHandler && record->record_type == PLDM_FRU_RECORD_TYPE_OEM)
728         {
729             auto rc = oemFruHandler->processOEMFRUTable(fruData);
730             if (!rc)
731             {
732                 return PLDM_SUCCESS;
733             }
734         }
735     }
736     return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
737 }
738 
addHotPlugRecord(pldm::responder::pdr_utils::PdrEntry pdrEntry)739 uint32_t FruImpl::addHotPlugRecord(
740     pldm::responder::pdr_utils::PdrEntry pdrEntry)
741 {
742     uint32_t lastHandle = 0;
743     uint32_t recordHandle = 0;
744 
745     if (oemPlatformHandler)
746     {
747         auto lastLocalRecord = oemPlatformHandler->fetchLastBMCRecord(pdrRepo);
748         lastHandle = pldm_pdr_get_record_handle(pdrRepo, lastLocalRecord);
749     }
750 
751     pdrEntry.handle.recordHandle = lastHandle + 1;
752     pldm_pdr_add(pdrRepo, pdrEntry.data, pdrEntry.size, false,
753                  pdrEntry.handle.recordHandle, &recordHandle);
754 
755     return recordHandle;
756 }
757 
758 namespace fru
759 {
getFRURecordTableMetadata(const pldm_msg * request,size_t)760 Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
761                                             size_t /*payloadLength*/)
762 {
763     // FRU table is built lazily, build if not done.
764     buildFRUTable();
765 
766     constexpr uint8_t major = 0x01;
767     constexpr uint8_t minor = 0x00;
768     constexpr uint32_t maxSize = 0xFFFFFFFF;
769 
770     Response response(sizeof(pldm_msg_hdr) +
771                           PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES,
772                       0);
773     auto responsePtr = new (response.data()) pldm_msg;
774 
775     impl.getFRURecordTableMetadata();
776 
777     auto rc = encode_get_fru_record_table_metadata_resp(
778         request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize,
779         impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(),
780         responsePtr);
781     if (rc != PLDM_SUCCESS)
782     {
783         return ccOnlyResponse(request, rc);
784     }
785 
786     return response;
787 }
788 
getFRURecordTable(const pldm_msg * request,size_t payloadLength)789 Response Handler::getFRURecordTable(const pldm_msg* request,
790                                     size_t payloadLength)
791 {
792     // FRU table is built lazily, build if not done.
793     buildFRUTable();
794 
795     if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
796     {
797         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
798     }
799 
800     Response response(
801         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0);
802     auto responsePtr = new (response.data()) pldm_msg;
803 
804     auto rc =
805         encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
806                                          0, PLDM_START_AND_END, responsePtr);
807     if (rc != PLDM_SUCCESS)
808     {
809         return ccOnlyResponse(request, rc);
810     }
811 
812     impl.getFRUTable(response);
813 
814     return response;
815 }
816 
getFRURecordByOption(const pldm_msg * request,size_t payloadLength)817 Response Handler::getFRURecordByOption(const pldm_msg* request,
818                                        size_t payloadLength)
819 {
820     if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
821     {
822         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
823     }
824 
825     uint32_t retDataTransferHandle{};
826     uint16_t retFruTableHandle{};
827     uint16_t retRecordSetIdentifier{};
828     uint8_t retRecordType{};
829     uint8_t retFieldType{};
830     uint8_t retTransferOpFlag{};
831 
832     auto rc = decode_get_fru_record_by_option_req(
833         request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
834         &retRecordSetIdentifier, &retRecordType, &retFieldType,
835         &retTransferOpFlag);
836 
837     if (rc != PLDM_SUCCESS)
838     {
839         return ccOnlyResponse(request, rc);
840     }
841 
842     std::vector<uint8_t> fruData;
843     rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
844                                    retRecordSetIdentifier, retRecordType,
845                                    retFieldType);
846     if (rc != PLDM_SUCCESS)
847     {
848         return ccOnlyResponse(request, rc);
849     }
850 
851     auto respPayloadLength =
852         PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size();
853     Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
854     auto responsePtr = new (response.data()) pldm_msg;
855 
856     rc = encode_get_fru_record_by_option_resp(
857         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
858         fruData.data(), fruData.size(), responsePtr, respPayloadLength);
859 
860     if (rc != PLDM_SUCCESS)
861     {
862         return ccOnlyResponse(request, rc);
863     }
864 
865     return response;
866 }
867 
setFRURecordTable(const pldm_msg * request,size_t payloadLength)868 Response Handler::setFRURecordTable(const pldm_msg* request,
869                                     size_t payloadLength)
870 {
871     uint32_t transferHandle{};
872     uint8_t transferOpFlag{};
873     struct variable_field fruData;
874 
875     auto rc = decode_set_fru_record_table_req(
876         request, payloadLength, &transferHandle, &transferOpFlag, &fruData);
877 
878     if (rc != PLDM_SUCCESS)
879     {
880         return ccOnlyResponse(request, rc);
881     }
882 
883     Table table(fruData.ptr, fruData.ptr + fruData.length);
884     rc = impl.setFRUTable(table);
885     if (rc != PLDM_SUCCESS)
886     {
887         return ccOnlyResponse(request, rc);
888     }
889 
890     Response response(
891         sizeof(pldm_msg_hdr) + PLDM_SET_FRU_RECORD_TABLE_RESP_BYTES);
892     struct pldm_msg* responsePtr = new (response.data()) pldm_msg;
893 
894     rc = encode_set_fru_record_table_resp(
895         request->hdr.instance_id, PLDM_SUCCESS, 0 /* nextDataTransferHandle */,
896         response.size() - sizeof(pldm_msg_hdr), responsePtr);
897 
898     if (rc != PLDM_SUCCESS)
899     {
900         return ccOnlyResponse(request, rc);
901     }
902 
903     return response;
904 }
905 
906 } // namespace fru
907 
908 } // namespace responder
909 
910 } // namespace pldm
911