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