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