xref: /openbmc/pldm/platform-mc/terminus.cpp (revision 6a7682ea)
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(nullTerminator)) *
287                    sizeof(uint16_t);
288             std::transform(u16NameString.cbegin(), u16NameString.cend(),
289                            u16NameString.begin(),
290                            [](uint16_t utf16) { return be16toh(utf16); });
291             std::string nameString =
292                 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
293                                      char16_t>{}
294                     .to_bytes(u16NameString);
295             nameStrings.emplace_back(std::make_pair(
296                 nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
297         }
298         sensorAuxNames.emplace_back(std::move(nameStrings));
299     }
300     return std::make_shared<SensorAuxiliaryNames>(
301         pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
302 }
303 
304 std::shared_ptr<EntityAuxiliaryNames>
305     Terminus::parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
306 {
307     auto names_offset = sizeof(struct pldm_pdr_hdr) +
308                         PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
309     auto names_size = pdrData.size() - names_offset;
310 
311     size_t decodedPdrSize = sizeof(struct pldm_entity_auxiliary_names_pdr) +
312                             names_size;
313     auto vPdr = std::vector<char>(decodedPdrSize);
314     auto decodedPdr =
315         reinterpret_cast<struct pldm_entity_auxiliary_names_pdr*>(vPdr.data());
316 
317     auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
318                                                 decodedPdr, decodedPdrSize);
319 
320     if (rc)
321     {
322         lg2::error(
323             "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
324             "RC", rc);
325         return nullptr;
326     }
327 
328     auto vNames =
329         std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
330     decodedPdr->names = vNames.data();
331 
332     rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
333     if (rc)
334     {
335         lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
336                    rc);
337         return nullptr;
338     }
339 
340     AuxiliaryNames nameStrings{};
341     for (const auto& count :
342          std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
343     {
344         std::string_view nameLanguageTag =
345             static_cast<std::string_view>(decodedPdr->names[count].tag);
346         const size_t u16NameStringLen =
347             std::char_traits<char16_t>::length(decodedPdr->names[count].name);
348         std::u16string u16NameString(decodedPdr->names[count].name,
349                                      u16NameStringLen);
350         std::transform(u16NameString.cbegin(), u16NameString.cend(),
351                        u16NameString.begin(),
352                        [](uint16_t utf16) { return be16toh(utf16); });
353         std::string nameString =
354             std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
355                 .to_bytes(u16NameString);
356         nameStrings.emplace_back(std::make_pair(
357             nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
358     }
359 
360     EntityKey key{decodedPdr->container.entity_type,
361                   decodedPdr->container.entity_instance_num,
362                   decodedPdr->container.entity_container_id};
363 
364     return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
365 }
366 
367 std::shared_ptr<pldm_numeric_sensor_value_pdr>
368     Terminus::parseNumericSensorPDR(const std::vector<uint8_t>& pdr)
369 {
370     const uint8_t* ptr = pdr.data();
371     auto parsedPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
372     auto rc = decode_numeric_sensor_pdr_data(ptr, pdr.size(), parsedPdr.get());
373     if (rc)
374     {
375         return nullptr;
376     }
377     return parsedPdr;
378 }
379 
380 void Terminus::addNumericSensor(
381     const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)
382 {
383     uint16_t sensorId = pdr->sensor_id;
384     if (terminusName.empty())
385     {
386         lg2::error(
387             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
388             "TID", tid);
389         return;
390     }
391     std::string sensorName = terminusName + "_" + "Sensor_" +
392                              std::to_string(pdr->sensor_id);
393 
394     if (pdr->sensor_auxiliary_names_pdr)
395     {
396         auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
397         if (sensorAuxiliaryNames)
398         {
399             const auto& [sensorId, sensorCnt,
400                          sensorNames] = *sensorAuxiliaryNames;
401             if (sensorCnt == 1)
402             {
403                 for (const auto& [languageTag, name] : sensorNames[0])
404                 {
405                     if (languageTag == "en" && !name.empty())
406                     {
407                         sensorName = terminusName + "_" + name;
408                     }
409                 }
410             }
411         }
412     }
413 
414     try
415     {
416         auto sensor = std::make_shared<NumericSensor>(
417             tid, true, pdr, sensorName, inventoryPath);
418         lg2::info("Created NumericSensor {NAME}", "NAME", sensorName);
419         numericSensors.emplace_back(sensor);
420     }
421     catch (const sdbusplus::exception_t& e)
422     {
423         lg2::error(
424             "Failed to create NumericSensor. error - {ERROR} sensorname - {NAME}",
425             "ERROR", e, "NAME", sensorName);
426     }
427 }
428 
429 std::shared_ptr<SensorAuxiliaryNames>
430     Terminus::parseCompactNumericSensorNames(const std::vector<uint8_t>& sPdr)
431 {
432     std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
433         sensorAuxNames{};
434     AuxiliaryNames nameStrings{};
435     auto pdr =
436         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
437 
438     if (sPdr.size() <
439         (sizeof(pldm_compact_numeric_sensor_pdr) - sizeof(uint8_t)))
440     {
441         return nullptr;
442     }
443 
444     if (!pdr->sensor_name_length ||
445         (sPdr.size() < (sizeof(pldm_compact_numeric_sensor_pdr) -
446                         sizeof(uint8_t) + pdr->sensor_name_length)))
447     {
448         return nullptr;
449     }
450 
451     std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
452                            pdr->sensor_name_length);
453     nameStrings.emplace_back(
454         std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
455     sensorAuxNames.emplace_back(std::move(nameStrings));
456 
457     return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
458                                                   std::move(sensorAuxNames));
459 }
460 
461 std::shared_ptr<pldm_compact_numeric_sensor_pdr>
462     Terminus::parseCompactNumericSensorPDR(const std::vector<uint8_t>& sPdr)
463 {
464     auto pdr =
465         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
466     if (sPdr.size() < sizeof(pldm_compact_numeric_sensor_pdr))
467     {
468         // Handle error: input data too small to contain valid pdr
469         return nullptr;
470     }
471     auto parsedPdr = std::make_shared<pldm_compact_numeric_sensor_pdr>();
472 
473     parsedPdr->hdr = pdr->hdr;
474     parsedPdr->terminus_handle = pdr->terminus_handle;
475     parsedPdr->sensor_id = pdr->sensor_id;
476     parsedPdr->entity_type = pdr->entity_type;
477     parsedPdr->entity_instance = pdr->entity_instance;
478     parsedPdr->container_id = pdr->container_id;
479     parsedPdr->sensor_name_length = pdr->sensor_name_length;
480     parsedPdr->base_unit = pdr->base_unit;
481     parsedPdr->unit_modifier = pdr->unit_modifier;
482     parsedPdr->occurrence_rate = pdr->occurrence_rate;
483     parsedPdr->range_field_support = pdr->range_field_support;
484     parsedPdr->warning_high = pdr->warning_high;
485     parsedPdr->warning_low = pdr->warning_low;
486     parsedPdr->critical_high = pdr->critical_high;
487     parsedPdr->critical_low = pdr->critical_low;
488     parsedPdr->fatal_high = pdr->fatal_high;
489     parsedPdr->fatal_low = pdr->fatal_low;
490     return parsedPdr;
491 }
492 
493 void Terminus::addCompactNumericSensor(
494     const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)
495 {
496     uint16_t sensorId = pdr->sensor_id;
497     if (terminusName.empty())
498     {
499         lg2::error(
500             "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
501             "TID", tid);
502         return;
503     }
504     std::string sensorName = terminusName + "_" + "Sensor_" +
505                              std::to_string(pdr->sensor_id);
506 
507     auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
508     if (sensorAuxiliaryNames)
509     {
510         const auto& [sensorId, sensorCnt, sensorNames] = *sensorAuxiliaryNames;
511         if (sensorCnt == 1)
512         {
513             for (const auto& [languageTag, name] : sensorNames[0])
514             {
515                 if (languageTag == "en" && !name.empty())
516                 {
517                     sensorName = terminusName + "_" + name;
518                 }
519             }
520         }
521     }
522 
523     try
524     {
525         auto sensor = std::make_shared<NumericSensor>(
526             tid, true, pdr, sensorName, inventoryPath);
527         lg2::info("Created Compact NumericSensor {NAME}", "NAME", sensorName);
528         numericSensors.emplace_back(sensor);
529     }
530     catch (const sdbusplus::exception_t& e)
531     {
532         lg2::error(
533             "Failed to create Compact NumericSensor. error - {ERROR} sensorname - {NAME}",
534             "ERROR", e, "NAME", sensorName);
535     }
536 }
537 
538 } // namespace platform_mc
539 } // namespace pldm
540