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