xref: /openbmc/pldm/platform-mc/terminus.cpp (revision 8eedaa6c9649d5513bbf884f20b8a361143914b0)
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     else
212     {
213         terminusName = std::format("Terminus_{}", tid);
214     }
215 
216     if (terminusName.empty() &&
217         (numericSensorPdrs.size() || compactNumericSensorPdrs.size()))
218     {
219         lg2::error(
220             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
221             "TID", tid);
222         return;
223     }
224 
225     if (createInventoryPath(terminusName))
226     {
227         lg2::info("Terminus ID {TID}: Created Inventory path {PATH}.", "TID",
228                   tid, "PATH", inventoryPath);
229     }
230 
231     addNextSensorFromPDRs();
232 }
233 
addNextSensorFromPDRs()234 void Terminus::addNextSensorFromPDRs()
235 {
236     sensorCreationEvent.reset();
237 
238     if (terminusName.empty())
239     {
240         lg2::error(
241             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
242             "TID", tid);
243         return;
244     }
245 
246     auto pdrIt = sensorPdrIt;
247 
248     if (pdrIt < numericSensorPdrs.size())
249     {
250         const auto& pdr = numericSensorPdrs[pdrIt];
251         // Defer adding the next Numeric Sensor
252         sensorCreationEvent = std::make_unique<sdeventplus::source::Defer>(
253             event,
254             std::bind(std::mem_fn(&Terminus::addNumericSensor), this, pdr));
255     }
256     else if (pdrIt < numericSensorPdrs.size() + compactNumericSensorPdrs.size())
257     {
258         pdrIt -= numericSensorPdrs.size();
259         const auto& pdr = compactNumericSensorPdrs[pdrIt];
260         // Defer adding the next Compact Numeric Sensor
261         sensorCreationEvent = std::make_unique<sdeventplus::source::Defer>(
262             event, std::bind(std::mem_fn(&Terminus::addCompactNumericSensor),
263                              this, pdr));
264     }
265     else
266     {
267         sensorPdrIt = 0;
268         return;
269     }
270 
271     // Move the iteration to the next sensor PDR
272     sensorPdrIt++;
273 }
274 
getSensorAuxiliaryNames(SensorID id)275 std::shared_ptr<SensorAuxiliaryNames> Terminus::getSensorAuxiliaryNames(
276     SensorID id)
277 {
278     auto it = std::find_if(
279         sensorAuxiliaryNamesTbl.begin(), sensorAuxiliaryNamesTbl.end(),
280         [id](
281             const std::shared_ptr<SensorAuxiliaryNames>& sensorAuxiliaryNames) {
282             const auto& [sensorId, sensorCnt, sensorNames] =
283                 *sensorAuxiliaryNames;
284             return sensorId == id;
285         });
286 
287     if (it != sensorAuxiliaryNamesTbl.end())
288     {
289         return *it;
290     }
291     return nullptr;
292 };
293 
parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)294 std::shared_ptr<SensorAuxiliaryNames> Terminus::parseSensorAuxiliaryNamesPDR(
295     const std::vector<uint8_t>& pdrData)
296 {
297     constexpr uint8_t nullTerminator = 0;
298     auto pdr = reinterpret_cast<const struct pldm_sensor_auxiliary_names_pdr*>(
299         pdrData.data());
300     const uint8_t* ptr = pdr->names;
301     std::vector<AuxiliaryNames> sensorAuxNames{};
302     char16_t alignedBuffer[PLDM_STR_UTF_16_MAX_LEN];
303     for ([[maybe_unused]] const auto& sensor :
304          std::views::iota(0, static_cast<int>(pdr->sensor_count)))
305     {
306         const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
307         ptr += sizeof(uint8_t);
308         AuxiliaryNames nameStrings{};
309         for ([[maybe_unused]] const auto& count :
310              std::views::iota(0, static_cast<int>(nameStringCount)))
311         {
312             std::string_view nameLanguageTag(
313                 reinterpret_cast<const char*>(ptr));
314             ptr += nameLanguageTag.size() + sizeof(nullTerminator);
315 
316             int u16NameStringLen = 0;
317             for (int i = 0; ptr[i] != 0 || ptr[i + 1] != 0; i += 2)
318             {
319                 u16NameStringLen++;
320             }
321             /* include terminator */
322             u16NameStringLen++;
323 
324             std::fill(std::begin(alignedBuffer), std::end(alignedBuffer), 0);
325             if (u16NameStringLen > PLDM_STR_UTF_16_MAX_LEN)
326             {
327                 lg2::error("Sensor name too long.");
328                 return nullptr;
329             }
330             memcpy(alignedBuffer, ptr, u16NameStringLen * sizeof(uint16_t));
331             std::u16string u16NameString(alignedBuffer, u16NameStringLen);
332             ptr += u16NameString.size() * sizeof(uint16_t);
333             std::transform(u16NameString.cbegin(), u16NameString.cend(),
334                            u16NameString.begin(),
335                            [](uint16_t utf16) { return be16toh(utf16); });
336 #pragma GCC diagnostic push
337 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
338             std::string nameString =
339                 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
340                                      char16_t>{}
341                     .to_bytes(u16NameString);
342 #pragma GCC diagnostic pop
343             nameStrings.emplace_back(std::make_pair(
344                 nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
345         }
346         sensorAuxNames.emplace_back(std::move(nameStrings));
347     }
348     return std::make_shared<SensorAuxiliaryNames>(
349         pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
350 }
351 
parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)352 std::shared_ptr<EntityAuxiliaryNames> Terminus::parseEntityAuxiliaryNamesPDR(
353     const std::vector<uint8_t>& pdrData)
354 {
355     auto names_offset = sizeof(struct pldm_pdr_hdr) +
356                         PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
357     auto names_size = pdrData.size() - names_offset;
358 
359     size_t decodedPdrSize =
360         sizeof(struct pldm_entity_auxiliary_names_pdr) + names_size;
361     auto vPdr = std::vector<char>(decodedPdrSize);
362     auto decodedPdr = new (vPdr.data()) pldm_entity_auxiliary_names_pdr;
363 
364     auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
365                                                 decodedPdr, decodedPdrSize);
366 
367     if (rc)
368     {
369         lg2::error(
370             "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
371             "RC", rc);
372         return nullptr;
373     }
374 
375     auto vNames =
376         std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
377     decodedPdr->names = vNames.data();
378 
379     rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
380     if (rc)
381     {
382         lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
383                    rc);
384         return nullptr;
385     }
386 
387     AuxiliaryNames nameStrings{};
388     for (const auto& count :
389          std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
390     {
391         std::string_view nameLanguageTag =
392             static_cast<std::string_view>(decodedPdr->names[count].tag);
393         const size_t u16NameStringLen =
394             std::char_traits<char16_t>::length(decodedPdr->names[count].name);
395         std::u16string u16NameString(decodedPdr->names[count].name,
396                                      u16NameStringLen);
397         std::transform(u16NameString.cbegin(), u16NameString.cend(),
398                        u16NameString.begin(),
399                        [](uint16_t utf16) { return be16toh(utf16); });
400 #pragma GCC diagnostic push
401 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
402         std::string nameString =
403             std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
404                 .to_bytes(u16NameString);
405 #pragma GCC diagnostic pop
406         nameStrings.emplace_back(std::make_pair(
407             nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
408     }
409 
410     EntityKey key{decodedPdr->container.entity_type,
411                   decodedPdr->container.entity_instance_num,
412                   decodedPdr->container.entity_container_id};
413 
414     return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
415 }
416 
parseNumericSensorPDR(const std::vector<uint8_t> & pdr)417 std::shared_ptr<pldm_numeric_sensor_value_pdr> Terminus::parseNumericSensorPDR(
418     const std::vector<uint8_t>& pdr)
419 {
420     const uint8_t* ptr = pdr.data();
421     auto parsedPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
422     auto rc = decode_numeric_sensor_pdr_data(ptr, pdr.size(), parsedPdr.get());
423     if (rc)
424     {
425         return nullptr;
426     }
427     return parsedPdr;
428 }
429 
addNumericSensor(const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)430 void Terminus::addNumericSensor(
431     const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)
432 {
433     if (!pdr)
434     {
435         lg2::error(
436             "Terminus ID {TID}: Skip adding Numeric Sensor - invalid pointer to PDR.",
437             "TID", tid);
438         addNextSensorFromPDRs();
439     }
440 
441     auto sensorId = pdr->sensor_id;
442     auto sensorNames = getSensorNames(sensorId);
443 
444     if (sensorNames.empty())
445     {
446         lg2::error(
447             "Terminus ID {TID}: Failed to get name for Numeric Sensor {SID}",
448             "TID", tid, "SID", sensorId);
449         addNextSensorFromPDRs();
450     }
451 
452     std::string sensorName = sensorNames.front();
453 
454     try
455     {
456         auto sensor = std::make_shared<NumericSensor>(
457             tid, true, pdr, sensorName, inventoryPath);
458         lg2::info("Created NumericSensor {NAME}", "NAME", sensorName);
459         numericSensors.emplace_back(sensor);
460     }
461     catch (const sdbusplus::exception_t& e)
462     {
463         lg2::error(
464             "Failed to create NumericSensor. error - {ERROR} sensorname - {NAME}",
465             "ERROR", e, "NAME", sensorName);
466     }
467 
468     addNextSensorFromPDRs();
469 }
470 
parseCompactNumericSensorNames(const std::vector<uint8_t> & sPdr)471 std::shared_ptr<SensorAuxiliaryNames> Terminus::parseCompactNumericSensorNames(
472     const std::vector<uint8_t>& sPdr)
473 {
474     std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
475         sensorAuxNames{};
476     AuxiliaryNames nameStrings{};
477 
478     auto pdr =
479         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
480 
481     if (sPdr.size() <
482         (sizeof(pldm_compact_numeric_sensor_pdr) - sizeof(uint8_t)))
483     {
484         return nullptr;
485     }
486 
487     if (!pdr->sensor_name_length ||
488         (sPdr.size() < (sizeof(pldm_compact_numeric_sensor_pdr) -
489                         sizeof(uint8_t) + pdr->sensor_name_length)))
490     {
491         return nullptr;
492     }
493 
494     std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
495                            pdr->sensor_name_length);
496     nameStrings.emplace_back(
497         std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
498     sensorAuxNames.emplace_back(std::move(nameStrings));
499 
500     return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
501                                                   std::move(sensorAuxNames));
502 }
503 
504 std::shared_ptr<pldm_compact_numeric_sensor_pdr>
parseCompactNumericSensorPDR(const std::vector<uint8_t> & sPdr)505     Terminus::parseCompactNumericSensorPDR(const std::vector<uint8_t>& sPdr)
506 {
507     auto pdr =
508         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
509     if (sPdr.size() < sizeof(pldm_compact_numeric_sensor_pdr))
510     {
511         // Handle error: input data too small to contain valid pdr
512         return nullptr;
513     }
514     auto parsedPdr = std::make_shared<pldm_compact_numeric_sensor_pdr>();
515 
516     parsedPdr->hdr = pdr->hdr;
517     parsedPdr->terminus_handle = pdr->terminus_handle;
518     parsedPdr->sensor_id = pdr->sensor_id;
519     parsedPdr->entity_type = pdr->entity_type;
520     parsedPdr->entity_instance = pdr->entity_instance;
521     parsedPdr->container_id = pdr->container_id;
522     parsedPdr->sensor_name_length = pdr->sensor_name_length;
523     parsedPdr->base_unit = pdr->base_unit;
524     parsedPdr->unit_modifier = pdr->unit_modifier;
525     parsedPdr->occurrence_rate = pdr->occurrence_rate;
526     parsedPdr->range_field_support = pdr->range_field_support;
527     parsedPdr->warning_high = pdr->warning_high;
528     parsedPdr->warning_low = pdr->warning_low;
529     parsedPdr->critical_high = pdr->critical_high;
530     parsedPdr->critical_low = pdr->critical_low;
531     parsedPdr->fatal_high = pdr->fatal_high;
532     parsedPdr->fatal_low = pdr->fatal_low;
533     return parsedPdr;
534 }
535 
addCompactNumericSensor(const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)536 void Terminus::addCompactNumericSensor(
537     const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)
538 {
539     if (!pdr)
540     {
541         lg2::error(
542             "Terminus ID {TID}: Skip adding Compact Numeric Sensor - invalid pointer to PDR.",
543             "TID", tid);
544         addNextSensorFromPDRs();
545     }
546 
547     auto sensorId = pdr->sensor_id;
548     auto sensorNames = getSensorNames(sensorId);
549 
550     if (sensorNames.empty())
551     {
552         lg2::error(
553             "Terminus ID {TID}: Failed to get name for Compact Numeric Sensor {SID}",
554             "TID", tid, "SID", sensorId);
555         addNextSensorFromPDRs();
556     }
557 
558     std::string sensorName = sensorNames.front();
559 
560     try
561     {
562         auto sensor = std::make_shared<NumericSensor>(
563             tid, true, pdr, sensorName, inventoryPath);
564         lg2::info("Created Compact NumericSensor {NAME}", "NAME", sensorName);
565         numericSensors.emplace_back(sensor);
566     }
567     catch (const sdbusplus::exception_t& e)
568     {
569         lg2::error(
570             "Failed to create Compact NumericSensor. error - {ERROR} sensorname - {NAME}",
571             "ERROR", e, "NAME", sensorName);
572     }
573 
574     addNextSensorFromPDRs();
575 }
576 
getSensorObject(SensorID id)577 std::shared_ptr<NumericSensor> Terminus::getSensorObject(SensorID id)
578 {
579     if (terminusName.empty())
580     {
581         lg2::error(
582             "Terminus ID {TID}: DOES NOT have terminus name. No numeric sensor object.",
583             "TID", tid);
584         return nullptr;
585     }
586     if (!numericSensors.size())
587     {
588         lg2::error("Terminus ID {TID} name {NAME}: DOES NOT have sensor.",
589                    "TID", tid, "NAME", terminusName);
590         return nullptr;
591     }
592 
593     for (auto& sensor : numericSensors)
594     {
595         if (!sensor)
596         {
597             continue;
598         }
599 
600         if (sensor->sensorId == id)
601         {
602             return sensor;
603         }
604     }
605 
606     return nullptr;
607 }
608 
609 /** @brief Check if a pointer is go through end of table
610  *  @param[in] table - pointer to FRU record table
611  *  @param[in] p - pointer to each record of FRU record table
612  *  @param[in] tableSize - FRU table size
613  */
isTableEnd(const uint8_t * table,const uint8_t * p,const size_t tableSize)614 static bool isTableEnd(const uint8_t* table, const uint8_t* p,
615                        const size_t tableSize)
616 {
617     auto offset = p - table;
618     return (tableSize - offset) < sizeof(struct pldm_fru_record_data_format);
619 }
620 
updateInventoryWithFru(const uint8_t * fruData,const size_t fruLen)621 void Terminus::updateInventoryWithFru(const uint8_t* fruData,
622                                       const size_t fruLen)
623 {
624     auto tmp = getTerminusName();
625     if (!tmp || tmp.value().empty())
626     {
627         lg2::error(
628             "Terminus ID {TID}: Failed to update Inventory with Fru Data - error : Terminus name is empty.",
629             "TID", tid);
630         return;
631     }
632 
633     if (createInventoryPath(static_cast<std::string>(tmp.value())))
634     {
635         lg2::info("Terminus ID {TID}: Created Inventory path.", "TID", tid);
636     }
637 
638     auto ptr = fruData;
639     while (!isTableEnd(fruData, ptr, fruLen))
640     {
641         auto record = reinterpret_cast<const pldm_fru_record_data_format*>(ptr);
642         ptr += sizeof(pldm_fru_record_data_format) -
643                sizeof(pldm_fru_record_tlv);
644 
645         if (!record->num_fru_fields)
646         {
647             lg2::error(
648                 "Invalid number of fields {NUM} of Record ID Type {TYPE} of terminus {TID}",
649                 "NUM", record->num_fru_fields, "TYPE", record->record_type,
650                 "TID", tid);
651             return;
652         }
653 
654         if (record->record_type != PLDM_FRU_RECORD_TYPE_GENERAL)
655         {
656             lg2::error(
657                 "Does not support Fru Record ID Type {TYPE} of terminus {TID}",
658                 "TYPE", record->record_type, "TID", tid);
659 
660             for ([[maybe_unused]] const auto& idx :
661                  std::views::iota(0, static_cast<int>(record->num_fru_fields)))
662             {
663                 auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
664                 ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
665             }
666             continue;
667         }
668         /* FRU General record type */
669         for ([[maybe_unused]] const auto& idx :
670              std::views::iota(0, static_cast<int>(record->num_fru_fields)))
671         {
672             auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
673             std::string fruField{};
674             if (tlv->type != PLDM_FRU_FIELD_TYPE_IANA)
675             {
676                 auto strOptional =
677                     pldm::utils::fruFieldValuestring(tlv->value, tlv->length);
678                 if (!strOptional)
679                 {
680                     ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
681                     continue;
682                 }
683                 fruField = strOptional.value();
684 
685                 if (fruField.empty())
686                 {
687                     ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
688                     continue;
689                 }
690             }
691 
692             switch (tlv->type)
693             {
694                 case PLDM_FRU_FIELD_TYPE_MODEL:
695                     inventoryItemBoardInft->model(fruField);
696                     break;
697                 case PLDM_FRU_FIELD_TYPE_PN:
698                     inventoryItemBoardInft->partNumber(fruField);
699                     break;
700                 case PLDM_FRU_FIELD_TYPE_SN:
701                     inventoryItemBoardInft->serialNumber(fruField);
702                     break;
703                 case PLDM_FRU_FIELD_TYPE_MANUFAC:
704                     inventoryItemBoardInft->manufacturer(fruField);
705                     break;
706                 case PLDM_FRU_FIELD_TYPE_NAME:
707                     inventoryItemBoardInft->names({fruField});
708                     break;
709                 case PLDM_FRU_FIELD_TYPE_VERSION:
710                     inventoryItemBoardInft->version(fruField);
711                     break;
712                 case PLDM_FRU_FIELD_TYPE_ASSET_TAG:
713                     inventoryItemBoardInft->assetTag(fruField);
714                     break;
715                 case PLDM_FRU_FIELD_TYPE_VENDOR:
716                 case PLDM_FRU_FIELD_TYPE_CHASSIS:
717                 case PLDM_FRU_FIELD_TYPE_SKU:
718                 case PLDM_FRU_FIELD_TYPE_DESC:
719                 case PLDM_FRU_FIELD_TYPE_EC_LVL:
720                 case PLDM_FRU_FIELD_TYPE_OTHER:
721                     break;
722                 case PLDM_FRU_FIELD_TYPE_IANA:
723                     auto iana =
724                         pldm::utils::fruFieldParserU32(tlv->value, tlv->length);
725                     if (!iana)
726                     {
727                         ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
728                         continue;
729                     }
730                     break;
731             }
732             ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
733         }
734     }
735 }
736 
getSensorNames(const SensorID & sensorId)737 std::vector<std::string> Terminus::getSensorNames(const SensorID& sensorId)
738 {
739     std::vector<std::string> sensorNames;
740     std::string defaultName =
741         std::format("{}_Sensor_{}", terminusName, unsigned(sensorId));
742     // To ensure there's always a default name at offset 0
743     sensorNames.emplace_back(defaultName);
744 
745     auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
746     if (!sensorAuxiliaryNames)
747     {
748         return sensorNames;
749     }
750 
751     const auto& [id, sensorCount, nameMap] = *sensorAuxiliaryNames;
752     for (const unsigned int& i :
753          std::views::iota(0, static_cast<int>(sensorCount)))
754     {
755         auto sensorName = defaultName;
756         if (i > 0)
757         {
758             // Sensor name at offset 0 will be the default name
759             sensorName += "_" + std::to_string(i);
760         }
761 
762         for (const auto& [languageTag, name] : nameMap[i])
763         {
764             if (languageTag == "en" && !name.empty())
765             {
766                 sensorName = std::format("{}_{}", terminusName, name);
767             }
768         }
769 
770         if (i >= sensorNames.size())
771         {
772             sensorNames.emplace_back(sensorName);
773         }
774         else
775         {
776             sensorNames[i] = sensorName;
777         }
778     }
779 
780     return sensorNames;
781 }
782 
783 } // namespace platform_mc
784 } // namespace pldm
785