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