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