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