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
Terminus(pldm_tid_t tid,uint64_t supportedTypes)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
doesSupportType(uint8_t type)21 bool Terminus::doesSupportType(uint8_t type)
22 {
23 return supportedTypes.test(type);
24 }
25
doesSupportCommand(uint8_t type,uint8_t command)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
findTerminusName()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
createInventoryPath(std::string tName)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
parseTerminusPDRs()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>
getSensorAuxiliaryNames(SensorId id)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>
parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)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>
parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t> & pdrData)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>
parseNumericSensorPDR(const std::vector<uint8_t> & 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
addNumericSensor(const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)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>
parseCompactNumericSensorNames(const std::vector<uint8_t> & sPdr)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>
parseCompactNumericSensorPDR(const std::vector<uint8_t> & sPdr)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
addCompactNumericSensor(const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)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
getSensorObject(SensorId id)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