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