xref: /openbmc/pldm/platform-mc/terminus.cpp (revision 8edcd7efefc3242f9136e65caed880d902d393a5)
1 #include "terminus.hpp"
2 
3 #include "dbus_impl_fru.hpp"
4 #include "terminus_manager.hpp"
5 
6 #include <libpldm/platform.h>
7 
8 #include <common/utils.hpp>
9 
10 #include <ranges>
11 
12 namespace pldm
13 {
14 namespace platform_mc
15 {
16 
Terminus(pldm_tid_t tid,uint64_t supportedTypes,sdeventplus::Event & event)17 Terminus::Terminus(pldm_tid_t tid, uint64_t supportedTypes,
18                    sdeventplus::Event& event) :
19     initialized(false), maxBufferSize(PLDM_PLATFORM_EVENT_MSG_MAX_BUFFER_SIZE),
20     synchronyConfigurationSupported(0), pollEvent(false), tid(tid),
21     supportedTypes(supportedTypes), event(event)
22 {}
23 
doesSupportType(uint8_t type)24 bool Terminus::doesSupportType(uint8_t type)
25 {
26     return supportedTypes.test(type);
27 }
28 
doesSupportCommand(uint8_t type,uint8_t command)29 bool Terminus::doesSupportCommand(uint8_t type, uint8_t command)
30 {
31     if (!doesSupportType(type))
32     {
33         return false;
34     }
35 
36     try
37     {
38         const size_t idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (command / 8);
39         if (idx >= supportedCmds.size())
40         {
41             return false;
42         }
43 
44         if (supportedCmds[idx] & (1 << (command % 8)))
45         {
46             lg2::info(
47                 "PLDM type {TYPE} command {CMD} is supported by terminus {TID}",
48                 "TYPE", type, "CMD", command, "TID", getTid());
49             return true;
50         }
51     }
52     catch (const std::exception& e)
53     {
54         return false;
55     }
56 
57     return false;
58 }
59 
findTerminusName()60 std::optional<std::string_view> Terminus::findTerminusName()
61 {
62     auto it = std::find_if(
63         entityAuxiliaryNamesTbl.begin(), entityAuxiliaryNamesTbl.end(),
64         [](const std::shared_ptr<EntityAuxiliaryNames>& entityAuxiliaryNames) {
65             const auto& [key, entityNames] = *entityAuxiliaryNames;
66             /**
67              * There is only one Overall system container entity in one
68              * terminus. The entity auxiliary name PDR of that terminus with the
69              * that type of containerID will include terminus name.
70              */
71             return (
72                 entityAuxiliaryNames &&
73                 key.containerId == PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID &&
74                 entityNames.size());
75         });
76 
77     if (it != entityAuxiliaryNamesTbl.end())
78     {
79         const auto& [key, entityNames] = **it;
80         if (!entityNames.size())
81         {
82             return std::nullopt;
83         }
84         return entityNames[0].second;
85     }
86 
87     return std::nullopt;
88 }
89 
createInventoryPath(std::string tName)90 bool Terminus::createInventoryPath(std::string tName)
91 {
92     if (tName.empty())
93     {
94         return false;
95     }
96 
97     /* inventory object is created */
98     if (inventoryItemBoardInft)
99     {
100         return false;
101     }
102 
103     inventoryPath = "/xyz/openbmc_project/inventory/system/board/" + tName;
104     try
105     {
106         inventoryItemBoardInft =
107             std::make_unique<pldm::dbus_api::PldmEntityReq>(
108                 utils::DBusHandler::getBus(), inventoryPath.c_str());
109         return true;
110     }
111     catch (const sdbusplus::exception_t& e)
112     {
113         lg2::error(
114             "Failed to create Inventory Board interface for device {PATH}",
115             "PATH", inventoryPath);
116     }
117 
118     return false;
119 }
120 
parseTerminusPDRs()121 void Terminus::parseTerminusPDRs()
122 {
123     for (auto& pdr : pdrs)
124     {
125         auto pdrHdr = new (pdr.data()) pldm_pdr_hdr;
126         switch (pdrHdr->type)
127         {
128             case PLDM_SENSOR_AUXILIARY_NAMES_PDR:
129             {
130                 auto sensorAuxNames = parseSensorAuxiliaryNamesPDR(pdr);
131                 if (!sensorAuxNames)
132                 {
133                     lg2::error(
134                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
135                         "TYPE", pdrHdr->type, "HANDLE",
136                         static_cast<uint32_t>(pdrHdr->record_handle));
137                     continue;
138                 }
139                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
140                 break;
141             }
142             case PLDM_NUMERIC_SENSOR_PDR:
143             {
144                 auto parsedPdr = parseNumericSensorPDR(pdr);
145                 if (!parsedPdr)
146                 {
147                     lg2::error(
148                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
149                         "TYPE", pdrHdr->type, "HANDLE",
150                         static_cast<uint32_t>(pdrHdr->record_handle));
151                     continue;
152                 }
153                 numericSensorPdrs.emplace_back(std::move(parsedPdr));
154                 break;
155             }
156             case PLDM_COMPACT_NUMERIC_SENSOR_PDR:
157             {
158                 auto parsedPdr = parseCompactNumericSensorPDR(pdr);
159                 if (!parsedPdr)
160                 {
161                     lg2::error(
162                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
163                         "TYPE", pdrHdr->type, "HANDLE",
164                         static_cast<uint32_t>(pdrHdr->record_handle));
165                     continue;
166                 }
167                 auto sensorAuxNames = parseCompactNumericSensorNames(pdr);
168                 if (!sensorAuxNames)
169                 {
170                     lg2::error(
171                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
172                         "TYPE", pdrHdr->type, "HANDLE",
173                         static_cast<uint32_t>(pdrHdr->record_handle));
174                     continue;
175                 }
176                 compactNumericSensorPdrs.emplace_back(std::move(parsedPdr));
177                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
178                 break;
179             }
180             case PLDM_ENTITY_AUXILIARY_NAMES_PDR:
181             {
182                 auto entityNames = parseEntityAuxiliaryNamesPDR(pdr);
183                 if (!entityNames)
184                 {
185                     lg2::error(
186                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
187                         "TYPE", pdrHdr->type, "HANDLE",
188                         static_cast<uint32_t>(pdrHdr->record_handle));
189                     continue;
190                 }
191                 entityAuxiliaryNamesTbl.emplace_back(std::move(entityNames));
192                 break;
193             }
194             default:
195             {
196                 lg2::error("Unsupported PDR with type {TYPE} handle {HANDLE}",
197                            "TYPE", pdrHdr->type, "HANDLE",
198                            static_cast<uint32_t>(pdrHdr->record_handle));
199                 break;
200             }
201         }
202     }
203 
204     auto tName = findTerminusName();
205     if (tName && !tName.value().empty())
206     {
207         lg2::info("Terminus {TID} has Auxiliary Name {NAME}.", "TID", tid,
208                   "NAME", tName.value());
209         terminusName = static_cast<std::string>(tName.value());
210     }
211 
212     if (terminusName.empty())
213     {
214         terminusName = std::format("Terminus_{}", tid);
215     }
216 
217     if (createInventoryPath(terminusName))
218     {
219         lg2::info("Terminus ID {TID}: Created Inventory path {PATH}.", "TID",
220                   tid, "PATH", inventoryPath);
221     }
222 
223     addNextSensorFromPDRs();
224 }
225 
addNextSensorFromPDRs()226 void Terminus::addNextSensorFromPDRs()
227 {
228     sensorCreationEvent.reset();
229 
230     if (terminusName.empty())
231     {
232         lg2::error(
233             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
234             "TID", tid);
235         return;
236     }
237 
238     auto pdrIt = sensorPdrIt;
239 
240     if (pdrIt < numericSensorPdrs.size())
241     {
242         const auto& pdr = numericSensorPdrs[pdrIt];
243         // Defer adding the next Numeric Sensor
244         sensorCreationEvent = std::make_unique<sdeventplus::source::Defer>(
245             event,
246             std::bind(std::mem_fn(&Terminus::addNumericSensor), this, pdr));
247     }
248     else if (pdrIt < numericSensorPdrs.size() + compactNumericSensorPdrs.size())
249     {
250         pdrIt -= numericSensorPdrs.size();
251         const auto& pdr = compactNumericSensorPdrs[pdrIt];
252         // Defer adding the next Compact Numeric Sensor
253         sensorCreationEvent = std::make_unique<sdeventplus::source::Defer>(
254             event, std::bind(std::mem_fn(&Terminus::addCompactNumericSensor),
255                              this, pdr));
256     }
257     else
258     {
259         sensorPdrIt = 0;
260         return;
261     }
262 
263     // Move the iteration to the next sensor PDR
264     sensorPdrIt++;
265 }
266 
getSensorAuxiliaryNames(SensorID id)267 std::shared_ptr<SensorAuxiliaryNames> Terminus::getSensorAuxiliaryNames(
268     SensorID id)
269 {
270     auto it = std::find_if(
271         sensorAuxiliaryNamesTbl.begin(), sensorAuxiliaryNamesTbl.end(),
272         [id](
273             const std::shared_ptr<SensorAuxiliaryNames>& sensorAuxiliaryNames) {
274             const auto& [sensorId, sensorCnt, sensorNames] =
275                 *sensorAuxiliaryNames;
276             return sensorId == id;
277         });
278 
279     if (it != sensorAuxiliaryNamesTbl.end())
280     {
281         return *it;
282     }
283     return nullptr;
284 };
285 
parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)286 std::shared_ptr<SensorAuxiliaryNames> Terminus::parseSensorAuxiliaryNamesPDR(
287     const std::vector<uint8_t>& pdrData)
288 {
289     constexpr uint8_t nullTerminator = 0;
290     auto pdr = reinterpret_cast<const struct pldm_sensor_auxiliary_names_pdr*>(
291         pdrData.data());
292     const uint8_t* ptr = pdr->names;
293     std::vector<AuxiliaryNames> sensorAuxNames{};
294     char16_t alignedBuffer[PLDM_STR_UTF_16_MAX_LEN];
295     for ([[maybe_unused]] const auto& sensor :
296          std::views::iota(0, static_cast<int>(pdr->sensor_count)))
297     {
298         const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
299         ptr += sizeof(uint8_t);
300         AuxiliaryNames nameStrings{};
301         for ([[maybe_unused]] const auto& count :
302              std::views::iota(0, static_cast<int>(nameStringCount)))
303         {
304             std::string_view nameLanguageTag(
305                 reinterpret_cast<const char*>(ptr));
306             ptr += nameLanguageTag.size() + sizeof(nullTerminator);
307 
308             int u16NameStringLen = 0;
309             for (int i = 0; ptr[i] != 0 || ptr[i + 1] != 0; i += 2)
310             {
311                 u16NameStringLen++;
312             }
313             /* include terminator */
314             u16NameStringLen++;
315 
316             std::fill(std::begin(alignedBuffer), std::end(alignedBuffer), 0);
317             if (u16NameStringLen > PLDM_STR_UTF_16_MAX_LEN)
318             {
319                 lg2::error("Sensor name too long.");
320                 return nullptr;
321             }
322             memcpy(alignedBuffer, ptr, u16NameStringLen * sizeof(uint16_t));
323             std::u16string u16NameString(alignedBuffer, u16NameStringLen);
324             ptr += u16NameString.size() * sizeof(uint16_t);
325             std::transform(u16NameString.cbegin(), u16NameString.cend(),
326                            u16NameString.begin(),
327                            [](uint16_t utf16) { return be16toh(utf16); });
328 #pragma GCC diagnostic push
329 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
330             std::string nameString{};
331             try
332             {
333                 nameString =
334                     std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
335                                          char16_t>{}
336                         .to_bytes(u16NameString);
337             }
338             catch (const std::range_error& e)
339             {
340                 lg2::error(
341                     "Exception while converting UTF-16 to UTF-8 for sensor auxiliary name: {ERROR}, Skipping this name.",
342                     "ERROR", e.what());
343                 continue;
344             }
345 #pragma GCC diagnostic pop
346             nameStrings.emplace_back(std::make_pair(
347                 nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
348         }
349         sensorAuxNames.emplace_back(std::move(nameStrings));
350     }
351     return std::make_shared<SensorAuxiliaryNames>(
352         pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
353 }
354 
parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)355 std::shared_ptr<EntityAuxiliaryNames> Terminus::parseEntityAuxiliaryNamesPDR(
356     const std::vector<uint8_t>& pdrData)
357 {
358     auto names_offset = sizeof(struct pldm_pdr_hdr) +
359                         PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
360     auto names_size = pdrData.size() - names_offset;
361 
362     size_t decodedPdrSize =
363         sizeof(struct pldm_entity_auxiliary_names_pdr) + names_size;
364     auto vPdr = std::vector<char>(decodedPdrSize);
365     auto decodedPdr = new (vPdr.data()) pldm_entity_auxiliary_names_pdr;
366 
367     auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
368                                                 decodedPdr, decodedPdrSize);
369 
370     if (rc)
371     {
372         lg2::error(
373             "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
374             "RC", rc);
375         return nullptr;
376     }
377 
378     auto vNames =
379         std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
380     decodedPdr->names = vNames.data();
381 
382     rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
383     if (rc)
384     {
385         lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
386                    rc);
387         return nullptr;
388     }
389 
390     AuxiliaryNames nameStrings{};
391     for (const auto& count :
392          std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
393     {
394         std::string_view nameLanguageTag =
395             static_cast<std::string_view>(decodedPdr->names[count].tag);
396         const size_t u16NameStringLen =
397             std::char_traits<char16_t>::length(decodedPdr->names[count].name);
398         std::u16string u16NameString(decodedPdr->names[count].name,
399                                      u16NameStringLen);
400         std::transform(u16NameString.cbegin(), u16NameString.cend(),
401                        u16NameString.begin(),
402                        [](uint16_t utf16) { return be16toh(utf16); });
403 #pragma GCC diagnostic push
404 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
405         std::string nameString{};
406         try
407         {
408             nameString = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
409                                               char16_t>{}
410                              .to_bytes(u16NameString);
411         }
412         catch (const std::range_error& e)
413         {
414             lg2::error(
415                 "Exception while converting UTF-16 to UTF-8 for entity auxiliary name: {ERROR}, Skipping this name.",
416                 "ERROR", e.what());
417             continue;
418         }
419 #pragma GCC diagnostic pop
420         nameStrings.emplace_back(std::make_pair(
421             nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
422     }
423 
424     EntityKey key{decodedPdr->container.entity_type,
425                   decodedPdr->container.entity_instance_num,
426                   decodedPdr->container.entity_container_id};
427 
428     return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
429 }
430 
parseNumericSensorPDR(const std::vector<uint8_t> & pdr)431 std::shared_ptr<pldm_numeric_sensor_value_pdr> Terminus::parseNumericSensorPDR(
432     const std::vector<uint8_t>& pdr)
433 {
434     const uint8_t* ptr = pdr.data();
435     auto parsedPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
436     auto rc = decode_numeric_sensor_pdr_data(ptr, pdr.size(), parsedPdr.get());
437     if (rc)
438     {
439         return nullptr;
440     }
441     return parsedPdr;
442 }
443 
addNumericSensor(const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)444 void Terminus::addNumericSensor(
445     const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)
446 {
447     if (!pdr)
448     {
449         lg2::error(
450             "Terminus ID {TID}: Skip adding Numeric Sensor - invalid pointer to PDR.",
451             "TID", tid);
452         addNextSensorFromPDRs();
453     }
454 
455     auto sensorId = pdr->sensor_id;
456     auto sensorNames = getSensorNames(sensorId);
457 
458     if (sensorNames.empty())
459     {
460         lg2::error(
461             "Terminus ID {TID}: Failed to get name for Numeric Sensor {SID}",
462             "TID", tid, "SID", sensorId);
463         addNextSensorFromPDRs();
464     }
465 
466     std::string sensorName = sensorNames.front();
467 
468     try
469     {
470         auto sensor = std::make_shared<NumericSensor>(
471             tid, true, pdr, sensorName, inventoryPath);
472         lg2::info("Created NumericSensor {NAME}", "NAME", sensorName);
473         numericSensors.emplace_back(sensor);
474     }
475     catch (const sdbusplus::exception_t& e)
476     {
477         lg2::error(
478             "Failed to create NumericSensor. error - {ERROR} sensorname - {NAME}",
479             "ERROR", e, "NAME", sensorName);
480     }
481 
482     addNextSensorFromPDRs();
483 }
484 
parseCompactNumericSensorNames(const std::vector<uint8_t> & sPdr)485 std::shared_ptr<SensorAuxiliaryNames> Terminus::parseCompactNumericSensorNames(
486     const std::vector<uint8_t>& sPdr)
487 {
488     std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
489         sensorAuxNames{};
490     AuxiliaryNames nameStrings{};
491 
492     auto pdr =
493         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
494 
495     if (sPdr.size() <
496         (sizeof(pldm_compact_numeric_sensor_pdr) - sizeof(uint8_t)))
497     {
498         return nullptr;
499     }
500 
501     if (!pdr->sensor_name_length ||
502         (sPdr.size() < (sizeof(pldm_compact_numeric_sensor_pdr) -
503                         sizeof(uint8_t) + pdr->sensor_name_length)))
504     {
505         return nullptr;
506     }
507 
508     std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
509                            pdr->sensor_name_length);
510     nameStrings.emplace_back(
511         std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
512     sensorAuxNames.emplace_back(std::move(nameStrings));
513 
514     return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
515                                                   std::move(sensorAuxNames));
516 }
517 
518 std::shared_ptr<pldm_compact_numeric_sensor_pdr>
parseCompactNumericSensorPDR(const std::vector<uint8_t> & sPdr)519     Terminus::parseCompactNumericSensorPDR(const std::vector<uint8_t>& sPdr)
520 {
521     auto pdr =
522         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
523     if (sPdr.size() < sizeof(pldm_compact_numeric_sensor_pdr))
524     {
525         // Handle error: input data too small to contain valid pdr
526         return nullptr;
527     }
528     auto parsedPdr = std::make_shared<pldm_compact_numeric_sensor_pdr>();
529 
530     parsedPdr->hdr = pdr->hdr;
531     parsedPdr->terminus_handle = pdr->terminus_handle;
532     parsedPdr->sensor_id = pdr->sensor_id;
533     parsedPdr->entity_type = pdr->entity_type;
534     parsedPdr->entity_instance = pdr->entity_instance;
535     parsedPdr->container_id = pdr->container_id;
536     parsedPdr->sensor_name_length = pdr->sensor_name_length;
537     parsedPdr->base_unit = pdr->base_unit;
538     parsedPdr->unit_modifier = pdr->unit_modifier;
539     parsedPdr->occurrence_rate = pdr->occurrence_rate;
540     parsedPdr->range_field_support = pdr->range_field_support;
541     parsedPdr->warning_high = pdr->warning_high;
542     parsedPdr->warning_low = pdr->warning_low;
543     parsedPdr->critical_high = pdr->critical_high;
544     parsedPdr->critical_low = pdr->critical_low;
545     parsedPdr->fatal_high = pdr->fatal_high;
546     parsedPdr->fatal_low = pdr->fatal_low;
547     return parsedPdr;
548 }
549 
addCompactNumericSensor(const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)550 void Terminus::addCompactNumericSensor(
551     const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)
552 {
553     if (!pdr)
554     {
555         lg2::error(
556             "Terminus ID {TID}: Skip adding Compact Numeric Sensor - invalid pointer to PDR.",
557             "TID", tid);
558         addNextSensorFromPDRs();
559     }
560 
561     auto sensorId = pdr->sensor_id;
562     auto sensorNames = getSensorNames(sensorId);
563 
564     if (sensorNames.empty())
565     {
566         lg2::error(
567             "Terminus ID {TID}: Failed to get name for Compact Numeric Sensor {SID}",
568             "TID", tid, "SID", sensorId);
569         addNextSensorFromPDRs();
570     }
571 
572     std::string sensorName = sensorNames.front();
573 
574     try
575     {
576         auto sensor = std::make_shared<NumericSensor>(
577             tid, true, pdr, sensorName, inventoryPath);
578         lg2::info("Created Compact NumericSensor {NAME}", "NAME", sensorName);
579         numericSensors.emplace_back(sensor);
580     }
581     catch (const sdbusplus::exception_t& e)
582     {
583         lg2::error(
584             "Failed to create Compact NumericSensor. error - {ERROR} sensorname - {NAME}",
585             "ERROR", e, "NAME", sensorName);
586     }
587 
588     addNextSensorFromPDRs();
589 }
590 
getSensorObject(SensorID id)591 std::shared_ptr<NumericSensor> Terminus::getSensorObject(SensorID id)
592 {
593     if (terminusName.empty())
594     {
595         lg2::error(
596             "Terminus ID {TID}: DOES NOT have terminus name. No numeric sensor object.",
597             "TID", tid);
598         return nullptr;
599     }
600     if (!numericSensors.size())
601     {
602         lg2::error("Terminus ID {TID} name {NAME}: DOES NOT have sensor.",
603                    "TID", tid, "NAME", terminusName);
604         return nullptr;
605     }
606 
607     for (auto& sensor : numericSensors)
608     {
609         if (!sensor)
610         {
611             continue;
612         }
613 
614         if (sensor->sensorId == id)
615         {
616             return sensor;
617         }
618     }
619 
620     return nullptr;
621 }
622 
623 /** @brief Check if a pointer is go through end of table
624  *  @param[in] table - pointer to FRU record table
625  *  @param[in] p - pointer to each record of FRU record table
626  *  @param[in] tableSize - FRU table size
627  */
isTableEnd(const uint8_t * table,const uint8_t * p,const size_t tableSize)628 static bool isTableEnd(const uint8_t* table, const uint8_t* p,
629                        const size_t tableSize)
630 {
631     auto offset = p - table;
632     return (tableSize - offset) < sizeof(struct pldm_fru_record_data_format);
633 }
634 
updateInventoryWithFru(const uint8_t * fruData,const size_t fruLen)635 void Terminus::updateInventoryWithFru(const uint8_t* fruData,
636                                       const size_t fruLen)
637 {
638     auto tmp = getTerminusName();
639     if (!tmp || tmp.value().empty())
640     {
641         lg2::error(
642             "Terminus ID {TID}: Failed to update Inventory with Fru Data - error : Terminus name is empty.",
643             "TID", tid);
644         return;
645     }
646 
647     if (createInventoryPath(static_cast<std::string>(tmp.value())))
648     {
649         lg2::info("Terminus ID {TID}: Created Inventory path.", "TID", tid);
650     }
651 
652     auto ptr = fruData;
653     while (!isTableEnd(fruData, ptr, fruLen))
654     {
655         auto record = reinterpret_cast<const pldm_fru_record_data_format*>(ptr);
656         ptr += sizeof(pldm_fru_record_data_format) -
657                sizeof(pldm_fru_record_tlv);
658 
659         if (!record->num_fru_fields)
660         {
661             lg2::error(
662                 "Invalid number of fields {NUM} of Record ID Type {TYPE} of terminus {TID}",
663                 "NUM", record->num_fru_fields, "TYPE", record->record_type,
664                 "TID", tid);
665             return;
666         }
667 
668         if (record->record_type != PLDM_FRU_RECORD_TYPE_GENERAL)
669         {
670             lg2::error(
671                 "Does not support Fru Record ID Type {TYPE} of terminus {TID}",
672                 "TYPE", record->record_type, "TID", tid);
673 
674             for ([[maybe_unused]] const auto& idx :
675                  std::views::iota(0, static_cast<int>(record->num_fru_fields)))
676             {
677                 auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
678                 ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
679             }
680             continue;
681         }
682         /* FRU General record type */
683         for ([[maybe_unused]] const auto& idx :
684              std::views::iota(0, static_cast<int>(record->num_fru_fields)))
685         {
686             auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
687             std::string fruField{};
688             if (tlv->type != PLDM_FRU_FIELD_TYPE_IANA)
689             {
690                 auto strOptional =
691                     pldm::utils::fruFieldValuestring(tlv->value, tlv->length);
692                 if (!strOptional)
693                 {
694                     ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
695                     continue;
696                 }
697                 fruField = strOptional.value();
698 
699                 if (fruField.empty())
700                 {
701                     ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
702                     continue;
703                 }
704             }
705 
706             switch (tlv->type)
707             {
708                 case PLDM_FRU_FIELD_TYPE_MODEL:
709                     inventoryItemBoardInft->model(fruField);
710                     break;
711                 case PLDM_FRU_FIELD_TYPE_PN:
712                     inventoryItemBoardInft->partNumber(fruField);
713                     break;
714                 case PLDM_FRU_FIELD_TYPE_SN:
715                     inventoryItemBoardInft->serialNumber(fruField);
716                     break;
717                 case PLDM_FRU_FIELD_TYPE_MANUFAC:
718                     inventoryItemBoardInft->manufacturer(fruField);
719                     break;
720                 case PLDM_FRU_FIELD_TYPE_NAME:
721                     inventoryItemBoardInft->names({fruField});
722                     break;
723                 case PLDM_FRU_FIELD_TYPE_VERSION:
724                     inventoryItemBoardInft->version(fruField);
725                     break;
726                 case PLDM_FRU_FIELD_TYPE_ASSET_TAG:
727                     inventoryItemBoardInft->assetTag(fruField);
728                     break;
729                 case PLDM_FRU_FIELD_TYPE_VENDOR:
730                 case PLDM_FRU_FIELD_TYPE_CHASSIS:
731                 case PLDM_FRU_FIELD_TYPE_SKU:
732                 case PLDM_FRU_FIELD_TYPE_DESC:
733                 case PLDM_FRU_FIELD_TYPE_EC_LVL:
734                 case PLDM_FRU_FIELD_TYPE_OTHER:
735                     break;
736                 case PLDM_FRU_FIELD_TYPE_IANA:
737                     auto iana =
738                         pldm::utils::fruFieldParserU32(tlv->value, tlv->length);
739                     if (!iana)
740                     {
741                         ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
742                         continue;
743                     }
744                     break;
745             }
746             ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
747         }
748     }
749 }
750 
getSensorNames(const SensorID & sensorId)751 std::vector<std::string> Terminus::getSensorNames(const SensorID& sensorId)
752 {
753     std::vector<std::string> sensorNames;
754     std::string defaultName =
755         std::format("{}_Sensor_{}", terminusName, unsigned(sensorId));
756     // To ensure there's always a default name at offset 0
757     sensorNames.emplace_back(defaultName);
758 
759     auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
760     if (!sensorAuxiliaryNames)
761     {
762         return sensorNames;
763     }
764 
765     const auto& [id, sensorCount, nameMap] = *sensorAuxiliaryNames;
766     for (const unsigned int& i :
767          std::views::iota(0, static_cast<int>(sensorCount)))
768     {
769         auto sensorName = defaultName;
770         if (i > 0)
771         {
772             // Sensor name at offset 0 will be the default name
773             sensorName += "_" + std::to_string(i);
774         }
775 
776         for (const auto& [languageTag, name] : nameMap[i])
777         {
778             if (languageTag == "en" && !name.empty())
779             {
780                 sensorName = std::format("{}_{}", terminusName, name);
781             }
782         }
783 
784         if (i >= sensorNames.size())
785         {
786             sensorNames.emplace_back(sensorName);
787         }
788         else
789         {
790             sensorNames[i] = sensorName;
791         }
792     }
793 
794     return sensorNames;
795 }
796 
797 } // namespace platform_mc
798 } // namespace pldm
799