xref: /openbmc/pldm/platform-mc/terminus.cpp (revision a743e384)
1 #include "terminus.hpp"
2 
3 #include "libpldm/platform.h"
4 
5 #include "terminus_manager.hpp"
6 
7 #include <common/utils.hpp>
8 
9 #include <ranges>
10 
11 namespace pldm
12 {
13 namespace platform_mc
14 {
15 
16 Terminus::Terminus(pldm_tid_t tid, uint64_t supportedTypes) :
17     initialized(false), maxBufferSize(PLDM_PLATFORM_EVENT_MSG_MAX_BUFFER_SIZE),
18     synchronyConfigurationSupported(0), pollEvent(false), tid(tid),
19     supportedTypes(supportedTypes)
20 {}
21 
22 bool Terminus::doesSupportType(uint8_t type)
23 {
24     return supportedTypes.test(type);
25 }
26 
27 bool Terminus::doesSupportCommand(uint8_t type, uint8_t command)
28 {
29     if (!doesSupportType(type))
30     {
31         return false;
32     }
33 
34     try
35     {
36         const size_t idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (command / 8);
37         if (idx >= supportedCmds.size())
38         {
39             return false;
40         }
41 
42         if (supportedCmds[idx] & (1 << (command % 8)))
43         {
44             lg2::info(
45                 "PLDM type {TYPE} command {CMD} is supported by terminus {TID}",
46                 "TYPE", type, "CMD", command, "TID", getTid());
47             return true;
48         }
49     }
50     catch (const std::exception& e)
51     {
52         return false;
53     }
54 
55     return false;
56 }
57 
58 std::optional<std::string_view> Terminus::findTerminusName()
59 {
60     auto it = std::find_if(
61         entityAuxiliaryNamesTbl.begin(), entityAuxiliaryNamesTbl.end(),
62         [](const std::shared_ptr<EntityAuxiliaryNames>& entityAuxiliaryNames) {
63             const auto& [key, entityNames] = *entityAuxiliaryNames;
64             /**
65              * There is only one Overall system container entity in one
66              * terminus. The entity auxiliary name PDR of that terminus with the
67              * that type of containerID will include terminus name.
68              */
69             return (
70                 entityAuxiliaryNames &&
71                 key.containerId == PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID &&
72                 entityNames.size());
73         });
74 
75     if (it != entityAuxiliaryNamesTbl.end())
76     {
77         const auto& [key, entityNames] = **it;
78         if (!entityNames.size())
79         {
80             return std::nullopt;
81         }
82         return entityNames[0].second;
83     }
84 
85     return std::nullopt;
86 }
87 
88 bool Terminus::createInventoryPath(std::string tName)
89 {
90     if (tName.empty())
91     {
92         return false;
93     }
94 
95     inventoryPath = "/xyz/openbmc_project/inventory/system/board/" + tName;
96     try
97     {
98         inventoryItemBoardInft = std::make_unique<InventoryItemBoardIntf>(
99             utils::DBusHandler::getBus(), inventoryPath.c_str());
100         return true;
101     }
102     catch (const sdbusplus::exception_t& e)
103     {
104         lg2::error(
105             "Failed to create Inventory Board interface for device {PATH}",
106             "PATH", inventoryPath);
107     }
108 
109     return false;
110 }
111 
112 void Terminus::parseTerminusPDRs()
113 {
114     std::vector<std::shared_ptr<pldm_numeric_sensor_value_pdr>>
115         numericSensorPdrs{};
116     std::vector<std::shared_ptr<pldm_compact_numeric_sensor_pdr>>
117         compactNumericSensorPdrs{};
118 
119     for (auto& pdr : pdrs)
120     {
121         auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
122         switch (pdrHdr->type)
123         {
124             case PLDM_SENSOR_AUXILIARY_NAMES_PDR:
125             {
126                 auto sensorAuxNames = parseSensorAuxiliaryNamesPDR(pdr);
127                 if (!sensorAuxNames)
128                 {
129                     lg2::error(
130                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
131                         "TYPE", pdrHdr->type, "HANDLE",
132                         static_cast<uint32_t>(pdrHdr->record_handle));
133                     continue;
134                 }
135                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
136                 break;
137             }
138             case PLDM_NUMERIC_SENSOR_PDR:
139             {
140                 auto parsedPdr = parseNumericSensorPDR(pdr);
141                 if (!parsedPdr)
142                 {
143                     lg2::error(
144                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
145                         "TYPE", pdrHdr->type, "HANDLE",
146                         static_cast<uint32_t>(pdrHdr->record_handle));
147                     continue;
148                 }
149                 numericSensorPdrs.emplace_back(std::move(parsedPdr));
150                 break;
151             }
152             case PLDM_COMPACT_NUMERIC_SENSOR_PDR:
153             {
154                 auto parsedPdr = parseCompactNumericSensorPDR(pdr);
155                 if (!parsedPdr)
156                 {
157                     lg2::error(
158                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
159                         "TYPE", pdrHdr->type, "HANDLE",
160                         static_cast<uint32_t>(pdrHdr->record_handle));
161                     continue;
162                 }
163                 auto sensorAuxNames = parseCompactNumericSensorNames(pdr);
164                 if (!sensorAuxNames)
165                 {
166                     lg2::error(
167                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
168                         "TYPE", pdrHdr->type, "HANDLE",
169                         static_cast<uint32_t>(pdrHdr->record_handle));
170                     continue;
171                 }
172                 compactNumericSensorPdrs.emplace_back(std::move(parsedPdr));
173                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
174                 break;
175             }
176             case PLDM_ENTITY_AUXILIARY_NAMES_PDR:
177             {
178                 auto entityNames = parseEntityAuxiliaryNamesPDR(pdr);
179                 if (!entityNames)
180                 {
181                     lg2::error(
182                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
183                         "TYPE", pdrHdr->type, "HANDLE",
184                         static_cast<uint32_t>(pdrHdr->record_handle));
185                     continue;
186                 }
187                 entityAuxiliaryNamesTbl.emplace_back(std::move(entityNames));
188                 break;
189             }
190             default:
191             {
192                 lg2::error("Unsupported PDR with type {TYPE} handle {HANDLE}",
193                            "TYPE", pdrHdr->type, "HANDLE",
194                            static_cast<uint32_t>(pdrHdr->record_handle));
195                 break;
196             }
197         }
198     }
199 
200     auto tName = findTerminusName();
201     if (tName && !tName.value().empty())
202     {
203         lg2::info("Terminus {TID} has Auxiliary Name {NAME}.", "TID", tid,
204                   "NAME", tName.value());
205         terminusName = static_cast<std::string>(tName.value());
206     }
207 
208     if (terminusName.empty() &&
209         (numericSensorPdrs.size() || compactNumericSensorPdrs.size()))
210     {
211         lg2::error(
212             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
213             "TID", tid);
214         return;
215     }
216 
217     if (createInventoryPath(terminusName))
218     {
219         lg2::error("Terminus ID {TID}: Created Inventory path.", "TID", tid);
220     }
221 
222     for (auto pdr : numericSensorPdrs)
223     {
224         addNumericSensor(pdr);
225     }
226 
227     for (auto pdr : compactNumericSensorPdrs)
228     {
229         addCompactNumericSensor(pdr);
230     }
231 }
232 
233 std::shared_ptr<SensorAuxiliaryNames>
234     Terminus::getSensorAuxiliaryNames(SensorId id)
235 {
236     auto it = std::find_if(
237         sensorAuxiliaryNamesTbl.begin(), sensorAuxiliaryNamesTbl.end(),
238         [id](
239             const std::shared_ptr<SensorAuxiliaryNames>& sensorAuxiliaryNames) {
240             const auto& [sensorId, sensorCnt, sensorNames] =
241                 *sensorAuxiliaryNames;
242             return sensorId == id;
243         });
244 
245     if (it != sensorAuxiliaryNamesTbl.end())
246     {
247         return *it;
248     }
249     return nullptr;
250 };
251 
252 std::shared_ptr<SensorAuxiliaryNames>
253     Terminus::parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
254 {
255     constexpr uint8_t nullTerminator = 0;
256     auto pdr = reinterpret_cast<const struct pldm_sensor_auxiliary_names_pdr*>(
257         pdrData.data());
258     const uint8_t* ptr = pdr->names;
259     std::vector<AuxiliaryNames> sensorAuxNames{};
260     char16_t alignedBuffer[PLDM_STR_UTF_16_MAX_LEN];
261     for ([[maybe_unused]] const auto& sensor :
262          std::views::iota(0, static_cast<int>(pdr->sensor_count)))
263     {
264         const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
265         ptr += sizeof(uint8_t);
266         AuxiliaryNames nameStrings{};
267         for ([[maybe_unused]] const auto& count :
268              std::views::iota(0, static_cast<int>(nameStringCount)))
269         {
270             std::string_view nameLanguageTag(
271                 reinterpret_cast<const char*>(ptr));
272             ptr += nameLanguageTag.size() + sizeof(nullTerminator);
273 
274             int u16NameStringLen = 0;
275             for (int i = 0; ptr[i] != 0 || ptr[i + 1] != 0; i += 2)
276             {
277                 u16NameStringLen++;
278             }
279             /* include terminator */
280             u16NameStringLen++;
281 
282             std::fill(std::begin(alignedBuffer), std::end(alignedBuffer), 0);
283             if (u16NameStringLen > PLDM_STR_UTF_16_MAX_LEN)
284             {
285                 lg2::error("Sensor name to long.");
286                 return nullptr;
287             }
288             memcpy(alignedBuffer, ptr, u16NameStringLen * sizeof(uint16_t));
289             std::u16string u16NameString(alignedBuffer, u16NameStringLen);
290             ptr += u16NameString.size() * sizeof(uint16_t);
291             std::transform(u16NameString.cbegin(), u16NameString.cend(),
292                            u16NameString.begin(),
293                            [](uint16_t utf16) { return be16toh(utf16); });
294             std::string nameString =
295                 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
296                                      char16_t>{}
297                     .to_bytes(u16NameString);
298             nameStrings.emplace_back(std::make_pair(
299                 nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
300         }
301         sensorAuxNames.emplace_back(std::move(nameStrings));
302     }
303     return std::make_shared<SensorAuxiliaryNames>(
304         pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
305 }
306 
307 std::shared_ptr<EntityAuxiliaryNames>
308     Terminus::parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
309 {
310     auto names_offset = sizeof(struct pldm_pdr_hdr) +
311                         PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
312     auto names_size = pdrData.size() - names_offset;
313 
314     size_t decodedPdrSize =
315         sizeof(struct pldm_entity_auxiliary_names_pdr) + names_size;
316     auto vPdr = std::vector<char>(decodedPdrSize);
317     auto decodedPdr =
318         reinterpret_cast<struct pldm_entity_auxiliary_names_pdr*>(vPdr.data());
319 
320     auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
321                                                 decodedPdr, decodedPdrSize);
322 
323     if (rc)
324     {
325         lg2::error(
326             "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
327             "RC", rc);
328         return nullptr;
329     }
330 
331     auto vNames =
332         std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
333     decodedPdr->names = vNames.data();
334 
335     rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
336     if (rc)
337     {
338         lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
339                    rc);
340         return nullptr;
341     }
342 
343     AuxiliaryNames nameStrings{};
344     for (const auto& count :
345          std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
346     {
347         std::string_view nameLanguageTag =
348             static_cast<std::string_view>(decodedPdr->names[count].tag);
349         const size_t u16NameStringLen =
350             std::char_traits<char16_t>::length(decodedPdr->names[count].name);
351         std::u16string u16NameString(decodedPdr->names[count].name,
352                                      u16NameStringLen);
353         std::transform(u16NameString.cbegin(), u16NameString.cend(),
354                        u16NameString.begin(),
355                        [](uint16_t utf16) { return be16toh(utf16); });
356         std::string nameString =
357             std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
358                 .to_bytes(u16NameString);
359         nameStrings.emplace_back(std::make_pair(
360             nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
361     }
362 
363     EntityKey key{decodedPdr->container.entity_type,
364                   decodedPdr->container.entity_instance_num,
365                   decodedPdr->container.entity_container_id};
366 
367     return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
368 }
369 
370 std::shared_ptr<pldm_numeric_sensor_value_pdr>
371     Terminus::parseNumericSensorPDR(const std::vector<uint8_t>& pdr)
372 {
373     const uint8_t* ptr = pdr.data();
374     auto parsedPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
375     auto rc = decode_numeric_sensor_pdr_data(ptr, pdr.size(), parsedPdr.get());
376     if (rc)
377     {
378         return nullptr;
379     }
380     return parsedPdr;
381 }
382 
383 void Terminus::addNumericSensor(
384     const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)
385 {
386     uint16_t sensorId = pdr->sensor_id;
387     if (terminusName.empty())
388     {
389         lg2::error(
390             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
391             "TID", tid);
392         return;
393     }
394     std::string sensorName =
395         terminusName + "_" + "Sensor_" + std::to_string(pdr->sensor_id);
396 
397     if (pdr->sensor_auxiliary_names_pdr)
398     {
399         auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
400         if (sensorAuxiliaryNames)
401         {
402             const auto& [sensorId, sensorCnt, sensorNames] =
403                 *sensorAuxiliaryNames;
404             if (sensorCnt == 1)
405             {
406                 for (const auto& [languageTag, name] : sensorNames[0])
407                 {
408                     if (languageTag == "en" && !name.empty())
409                     {
410                         sensorName = terminusName + "_" + name;
411                     }
412                 }
413             }
414         }
415     }
416 
417     try
418     {
419         auto sensor = std::make_shared<NumericSensor>(
420             tid, true, pdr, sensorName, inventoryPath);
421         lg2::info("Created NumericSensor {NAME}", "NAME", sensorName);
422         numericSensors.emplace_back(sensor);
423     }
424     catch (const sdbusplus::exception_t& e)
425     {
426         lg2::error(
427             "Failed to create NumericSensor. error - {ERROR} sensorname - {NAME}",
428             "ERROR", e, "NAME", sensorName);
429     }
430 }
431 
432 std::shared_ptr<SensorAuxiliaryNames>
433     Terminus::parseCompactNumericSensorNames(const std::vector<uint8_t>& sPdr)
434 {
435     std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
436         sensorAuxNames{};
437     AuxiliaryNames nameStrings{};
438     auto pdr =
439         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
440 
441     if (sPdr.size() <
442         (sizeof(pldm_compact_numeric_sensor_pdr) - sizeof(uint8_t)))
443     {
444         return nullptr;
445     }
446 
447     if (!pdr->sensor_name_length ||
448         (sPdr.size() < (sizeof(pldm_compact_numeric_sensor_pdr) -
449                         sizeof(uint8_t) + pdr->sensor_name_length)))
450     {
451         return nullptr;
452     }
453 
454     std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
455                            pdr->sensor_name_length);
456     nameStrings.emplace_back(
457         std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
458     sensorAuxNames.emplace_back(std::move(nameStrings));
459 
460     return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
461                                                   std::move(sensorAuxNames));
462 }
463 
464 std::shared_ptr<pldm_compact_numeric_sensor_pdr>
465     Terminus::parseCompactNumericSensorPDR(const std::vector<uint8_t>& sPdr)
466 {
467     auto pdr =
468         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
469     if (sPdr.size() < sizeof(pldm_compact_numeric_sensor_pdr))
470     {
471         // Handle error: input data too small to contain valid pdr
472         return nullptr;
473     }
474     auto parsedPdr = std::make_shared<pldm_compact_numeric_sensor_pdr>();
475 
476     parsedPdr->hdr = pdr->hdr;
477     parsedPdr->terminus_handle = pdr->terminus_handle;
478     parsedPdr->sensor_id = pdr->sensor_id;
479     parsedPdr->entity_type = pdr->entity_type;
480     parsedPdr->entity_instance = pdr->entity_instance;
481     parsedPdr->container_id = pdr->container_id;
482     parsedPdr->sensor_name_length = pdr->sensor_name_length;
483     parsedPdr->base_unit = pdr->base_unit;
484     parsedPdr->unit_modifier = pdr->unit_modifier;
485     parsedPdr->occurrence_rate = pdr->occurrence_rate;
486     parsedPdr->range_field_support = pdr->range_field_support;
487     parsedPdr->warning_high = pdr->warning_high;
488     parsedPdr->warning_low = pdr->warning_low;
489     parsedPdr->critical_high = pdr->critical_high;
490     parsedPdr->critical_low = pdr->critical_low;
491     parsedPdr->fatal_high = pdr->fatal_high;
492     parsedPdr->fatal_low = pdr->fatal_low;
493     return parsedPdr;
494 }
495 
496 void Terminus::addCompactNumericSensor(
497     const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)
498 {
499     uint16_t sensorId = pdr->sensor_id;
500     if (terminusName.empty())
501     {
502         lg2::error(
503             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
504             "TID", tid);
505         return;
506     }
507     std::string sensorName =
508         terminusName + "_" + "Sensor_" + std::to_string(pdr->sensor_id);
509 
510     auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
511     if (sensorAuxiliaryNames)
512     {
513         const auto& [sensorId, sensorCnt, sensorNames] = *sensorAuxiliaryNames;
514         if (sensorCnt == 1)
515         {
516             for (const auto& [languageTag, name] : sensorNames[0])
517             {
518                 if (languageTag == "en" && !name.empty())
519                 {
520                     sensorName = terminusName + "_" + name;
521                 }
522             }
523         }
524     }
525 
526     try
527     {
528         auto sensor = std::make_shared<NumericSensor>(
529             tid, true, pdr, sensorName, inventoryPath);
530         lg2::info("Created Compact NumericSensor {NAME}", "NAME", sensorName);
531         numericSensors.emplace_back(sensor);
532     }
533     catch (const sdbusplus::exception_t& e)
534     {
535         lg2::error(
536             "Failed to create Compact NumericSensor. error - {ERROR} sensorname - {NAME}",
537             "ERROR", e, "NAME", sensorName);
538     }
539 }
540 
541 std::shared_ptr<NumericSensor> Terminus::getSensorObject(SensorId id)
542 {
543     if (terminusName.empty())
544     {
545         lg2::error(
546             "Terminus ID {TID}: DOES NOT have terminus name. No numeric sensor object.",
547             "TID", tid);
548         return nullptr;
549     }
550     if (!numericSensors.size())
551     {
552         lg2::error("Terminus ID {TID} name {NAME}: DOES NOT have sensor.",
553                    "TID", tid, "NAME", terminusName);
554         return nullptr;
555     }
556 
557     for (auto& sensor : numericSensors)
558     {
559         if (!sensor)
560         {
561             continue;
562         }
563 
564         if (sensor->sensorId == id)
565         {
566             return sensor;
567         }
568     }
569 
570     return nullptr;
571 }
572 } // namespace platform_mc
573 } // namespace pldm
574