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