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