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), maxBufferSize(PLDM_PLATFORM_EVENT_MSG_MAX_BUFFER_SIZE), 18 synchronyConfigurationSupported(0), pollEvent(false), tid(tid), 19 supportedTypes(supportedTypes) 20 {} 21 22 bool Terminus::doesSupportType(uint8_t type) 23 { 24 return supportedTypes.test(type); 25 } 26 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 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 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 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> 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> 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> 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> 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 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> 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> 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 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 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