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