xref: /openbmc/pldm/platform-mc/terminus.cpp (revision 51cc5bda)
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 void Terminus::parseTerminusPDRs()
86 {
87     std::vector<std::shared_ptr<pldm_numeric_sensor_value_pdr>>
88         numericSensorPdrs{};
89     std::vector<std::shared_ptr<pldm_compact_numeric_sensor_pdr>>
90         compactNumericSensorPdrs{};
91 
92     for (auto& pdr : pdrs)
93     {
94         auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
95         switch (pdrHdr->type)
96         {
97             case PLDM_SENSOR_AUXILIARY_NAMES_PDR:
98             {
99                 auto sensorAuxNames = parseSensorAuxiliaryNamesPDR(pdr);
100                 if (!sensorAuxNames)
101                 {
102                     lg2::error(
103                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
104                         "TYPE", pdrHdr->type, "HANDLE",
105                         static_cast<uint32_t>(pdrHdr->record_handle));
106                     continue;
107                 }
108                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
109                 break;
110             }
111             case PLDM_NUMERIC_SENSOR_PDR:
112             {
113                 auto parsedPdr = parseNumericSensorPDR(pdr);
114                 if (!parsedPdr)
115                 {
116                     lg2::error(
117                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
118                         "TYPE", pdrHdr->type, "HANDLE",
119                         static_cast<uint32_t>(pdrHdr->record_handle));
120                     continue;
121                 }
122                 numericSensorPdrs.emplace_back(std::move(parsedPdr));
123                 break;
124             }
125             case PLDM_COMPACT_NUMERIC_SENSOR_PDR:
126             {
127                 auto parsedPdr = parseCompactNumericSensorPDR(pdr);
128                 if (!parsedPdr)
129                 {
130                     lg2::error(
131                         "Failed to parse PDR with type {TYPE} handle {HANDLE}",
132                         "TYPE", pdrHdr->type, "HANDLE",
133                         static_cast<uint32_t>(pdrHdr->record_handle));
134                     continue;
135                 }
136                 auto sensorAuxNames = parseCompactNumericSensorNames(pdr);
137                 if (!sensorAuxNames)
138                 {
139                     lg2::error(
140                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
141                         "TYPE", pdrHdr->type, "HANDLE",
142                         static_cast<uint32_t>(pdrHdr->record_handle));
143                     continue;
144                 }
145                 compactNumericSensorPdrs.emplace_back(std::move(parsedPdr));
146                 sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
147                 break;
148             }
149             case PLDM_ENTITY_AUXILIARY_NAMES_PDR:
150             {
151                 auto entityNames = parseEntityAuxiliaryNamesPDR(pdr);
152                 if (!entityNames)
153                 {
154                     lg2::error(
155                         "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
156                         "TYPE", pdrHdr->type, "HANDLE",
157                         static_cast<uint32_t>(pdrHdr->record_handle));
158                     continue;
159                 }
160                 entityAuxiliaryNamesTbl.emplace_back(std::move(entityNames));
161                 break;
162             }
163             default:
164             {
165                 lg2::error("Unsupported PDR with type {TYPE} handle {HANDLE}",
166                            "TYPE", pdrHdr->type, "HANDLE",
167                            static_cast<uint32_t>(pdrHdr->record_handle));
168                 break;
169             }
170         }
171     }
172 
173     auto tName = findTerminusName();
174     if (tName && !tName.value().empty())
175     {
176         lg2::info("Terminus {TID} has Auxiliary Name {NAME}.", "TID", tid,
177                   "NAME", tName.value());
178         terminusName = static_cast<std::string>(tName.value());
179     }
180 }
181 
182 std::shared_ptr<SensorAuxiliaryNames>
183     Terminus::getSensorAuxiliaryNames(SensorId id)
184 {
185     auto it = std::find_if(
186         sensorAuxiliaryNamesTbl.begin(), sensorAuxiliaryNamesTbl.end(),
187         [id](
188             const std::shared_ptr<SensorAuxiliaryNames>& sensorAuxiliaryNames) {
189         const auto& [sensorId, sensorCnt, sensorNames] = *sensorAuxiliaryNames;
190         return sensorId == id;
191     });
192 
193     if (it != sensorAuxiliaryNamesTbl.end())
194     {
195         return *it;
196     }
197     return nullptr;
198 };
199 
200 std::shared_ptr<SensorAuxiliaryNames>
201     Terminus::parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
202 {
203     constexpr uint8_t nullTerminator = 0;
204     auto pdr = reinterpret_cast<const struct pldm_sensor_auxiliary_names_pdr*>(
205         pdrData.data());
206     const uint8_t* ptr = pdr->names;
207     std::vector<AuxiliaryNames> sensorAuxNames{};
208     char16_t alignedBuffer[PLDM_STR_UTF_16_MAX_LEN];
209     for ([[maybe_unused]] const auto& sensor :
210          std::views::iota(0, static_cast<int>(pdr->sensor_count)))
211     {
212         const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
213         ptr += sizeof(uint8_t);
214         AuxiliaryNames nameStrings{};
215         for ([[maybe_unused]] const auto& count :
216              std::views::iota(0, static_cast<int>(nameStringCount)))
217         {
218             std::string_view nameLanguageTag(
219                 reinterpret_cast<const char*>(ptr));
220             ptr += nameLanguageTag.size() + sizeof(nullTerminator);
221 
222             int u16NameStringLen = 0;
223             for (int i = 0; ptr[i] != 0 || ptr[i + 1] != 0; i += 2)
224             {
225                 u16NameStringLen++;
226             }
227             /* include terminator */
228             u16NameStringLen++;
229 
230             std::fill(std::begin(alignedBuffer), std::end(alignedBuffer), 0);
231             if (u16NameStringLen > PLDM_STR_UTF_16_MAX_LEN)
232             {
233                 lg2::error("Sensor name to long.");
234                 return nullptr;
235             }
236             memcpy(alignedBuffer, ptr, u16NameStringLen * sizeof(uint16_t));
237             std::u16string u16NameString(alignedBuffer, u16NameStringLen);
238             ptr += (u16NameString.size() + sizeof(nullTerminator)) *
239                    sizeof(uint16_t);
240             std::transform(u16NameString.cbegin(), u16NameString.cend(),
241                            u16NameString.begin(),
242                            [](uint16_t utf16) { return be16toh(utf16); });
243             std::string nameString =
244                 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
245                                      char16_t>{}
246                     .to_bytes(u16NameString);
247             nameStrings.emplace_back(std::make_pair(
248                 nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
249         }
250         sensorAuxNames.emplace_back(std::move(nameStrings));
251     }
252     return std::make_shared<SensorAuxiliaryNames>(
253         pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
254 }
255 
256 std::shared_ptr<EntityAuxiliaryNames>
257     Terminus::parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
258 {
259     auto names_offset = sizeof(struct pldm_pdr_hdr) +
260                         PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
261     auto names_size = pdrData.size() - names_offset;
262 
263     size_t decodedPdrSize = sizeof(struct pldm_entity_auxiliary_names_pdr) +
264                             names_size;
265     auto vPdr = std::vector<char>(decodedPdrSize);
266     auto decodedPdr =
267         reinterpret_cast<struct pldm_entity_auxiliary_names_pdr*>(vPdr.data());
268 
269     auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
270                                                 decodedPdr, decodedPdrSize);
271 
272     if (rc)
273     {
274         lg2::error(
275             "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
276             "RC", rc);
277         return nullptr;
278     }
279 
280     auto vNames =
281         std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
282     decodedPdr->names = vNames.data();
283 
284     rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
285     if (rc)
286     {
287         lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
288                    rc);
289         return nullptr;
290     }
291 
292     AuxiliaryNames nameStrings{};
293     for (const auto& count :
294          std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
295     {
296         std::string_view nameLanguageTag =
297             static_cast<std::string_view>(decodedPdr->names[count].tag);
298         const size_t u16NameStringLen =
299             std::char_traits<char16_t>::length(decodedPdr->names[count].name);
300         std::u16string u16NameString(decodedPdr->names[count].name,
301                                      u16NameStringLen);
302         std::transform(u16NameString.cbegin(), u16NameString.cend(),
303                        u16NameString.begin(),
304                        [](uint16_t utf16) { return be16toh(utf16); });
305         std::string nameString =
306             std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
307                 .to_bytes(u16NameString);
308         nameStrings.emplace_back(std::make_pair(
309             nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
310     }
311 
312     EntityKey key{decodedPdr->container.entity_type,
313                   decodedPdr->container.entity_instance_num,
314                   decodedPdr->container.entity_container_id};
315 
316     return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
317 }
318 
319 std::shared_ptr<pldm_numeric_sensor_value_pdr>
320     Terminus::parseNumericSensorPDR(const std::vector<uint8_t>& pdr)
321 {
322     const uint8_t* ptr = pdr.data();
323     auto parsedPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
324     auto rc = decode_numeric_sensor_pdr_data(ptr, pdr.size(), parsedPdr.get());
325     if (rc)
326     {
327         return nullptr;
328     }
329     return parsedPdr;
330 }
331 
332 std::shared_ptr<SensorAuxiliaryNames>
333     Terminus::parseCompactNumericSensorNames(const std::vector<uint8_t>& sPdr)
334 {
335     std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
336         sensorAuxNames{};
337     AuxiliaryNames nameStrings{};
338     auto pdr =
339         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
340 
341     if (sPdr.size() <
342         (sizeof(pldm_compact_numeric_sensor_pdr) - sizeof(uint8_t)))
343     {
344         return nullptr;
345     }
346 
347     if (!pdr->sensor_name_length ||
348         (sPdr.size() < (sizeof(pldm_compact_numeric_sensor_pdr) -
349                         sizeof(uint8_t) + pdr->sensor_name_length)))
350     {
351         return nullptr;
352     }
353 
354     std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
355                            pdr->sensor_name_length);
356     nameStrings.emplace_back(
357         std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
358     sensorAuxNames.emplace_back(std::move(nameStrings));
359 
360     return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
361                                                   std::move(sensorAuxNames));
362 }
363 
364 std::shared_ptr<pldm_compact_numeric_sensor_pdr>
365     Terminus::parseCompactNumericSensorPDR(const std::vector<uint8_t>& sPdr)
366 {
367     auto pdr =
368         reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
369     if (sPdr.size() < sizeof(pldm_compact_numeric_sensor_pdr))
370     {
371         // Handle error: input data too small to contain valid pdr
372         return nullptr;
373     }
374     auto parsedPdr = std::make_shared<pldm_compact_numeric_sensor_pdr>();
375 
376     parsedPdr->hdr = pdr->hdr;
377     parsedPdr->terminus_handle = pdr->terminus_handle;
378     parsedPdr->sensor_id = pdr->sensor_id;
379     parsedPdr->entity_type = pdr->entity_type;
380     parsedPdr->entity_instance = pdr->entity_instance;
381     parsedPdr->container_id = pdr->container_id;
382     parsedPdr->sensor_name_length = pdr->sensor_name_length;
383     parsedPdr->base_unit = pdr->base_unit;
384     parsedPdr->unit_modifier = pdr->unit_modifier;
385     parsedPdr->occurrence_rate = pdr->occurrence_rate;
386     parsedPdr->range_field_support = pdr->range_field_support;
387     parsedPdr->warning_high = pdr->warning_high;
388     parsedPdr->warning_low = pdr->warning_low;
389     parsedPdr->critical_high = pdr->critical_high;
390     parsedPdr->critical_low = pdr->critical_low;
391     parsedPdr->fatal_high = pdr->fatal_high;
392     parsedPdr->fatal_low = pdr->fatal_low;
393     return parsedPdr;
394 }
395 
396 } // namespace platform_mc
397 } // namespace pldm
398