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