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