platform-mc: Terminus name handling
`PLDM-stack: Adding sensor monitoring section` design spec details that
the `Terminus name` is required to create the terminus's sensors,
states, effecters... D-Bus interfaces and `Terminus Name` can be
encoded in the Terminus's `Entity Auxiliary Names PDR` or in the MCTP
Entity-manager endpoint EID configuration file.
[1] https://gerrit.openbmc.org/c/openbmc/docs/+/47252/34/designs/pldm-stack.md#433
Section `28.18 Entity Auxiliary Names PDR` in DSP0248 V1.2.2 details
about the PDRs response for the name of one PLDM entity. When the
containerID is `0x0000` this entity is `Overall system` or `top most
containing entity`. The name of this entity will can be used as
`Terminus name`.
Support parsing `Entity Auxiliary Names PDR` and find the `Terminus
name` if it is reponsed in the terminus' PDRs. `Terminus Name` string
will be assigned to local variable and can be used as prefix for
sensors, state, effecters... names.
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I701537c48651b9de86de77941b752e30de112916
diff --git a/common/test/pldm_utils_test.cpp b/common/test/pldm_utils_test.cpp
index 86704c4..73cd92c 100644
--- a/common/test/pldm_utils_test.cpp
+++ b/common/test/pldm_utils_test.cpp
@@ -1094,3 +1094,15 @@
rc = isValidEID(254);
EXPECT_EQ(rc, true);
}
+
+TEST(TrimNameForDbus, goodTest)
+{
+ std::string name = "Name with space";
+ std::string_view expectedName = "Name_with__space";
+ std::string_view result = trimNameForDbus(name);
+ EXPECT_EQ(expectedName, result);
+ name = "Name 1\0";
+ expectedName = "Name_1";
+ result = trimNameForDbus(name);
+ EXPECT_EQ(expectedName, result);
+}
diff --git a/common/utils.cpp b/common/utils.cpp
index 60154be..1cd5a72 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -671,5 +671,15 @@
}
}
+std::string_view trimNameForDbus(std::string& name)
+{
+ std::replace(name.begin(), name.end(), ' ', '_');
+ auto nullTerminatorPos = name.find('\0');
+ if (nullTerminatorPos != std::string::npos)
+ {
+ name.erase(nullTerminatorPos);
+ }
+ return name;
+}
} // namespace utils
} // namespace pldm
diff --git a/common/utils.hpp b/common/utils.hpp
index 103783d..6b8ea7c 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -526,5 +526,15 @@
* @param[in] present - status to set either true/false
*/
void setFruPresence(const std::string& fruObjPath, bool present);
+
+/** @brief Trim `\0` in string and replace ` ` by `_` to use name in D-Bus
+ * object path
+ *
+ * @param[in] name - the input string
+ *
+ * @return the result string
+ */
+std::string_view trimNameForDbus(std::string& name);
+
} // namespace utils
} // namespace pldm
diff --git a/platform-mc/terminus.cpp b/platform-mc/terminus.cpp
index 194583f..2892dc1 100644
--- a/platform-mc/terminus.cpp
+++ b/platform-mc/terminus.cpp
@@ -4,6 +4,8 @@
#include "terminus_manager.hpp"
+#include <common/utils.hpp>
+
#include <ranges>
namespace pldm
@@ -51,6 +53,35 @@
return false;
}
+std::optional<std::string_view> Terminus::findTerminusName()
+{
+ auto it = std::find_if(
+ entityAuxiliaryNamesTbl.begin(), entityAuxiliaryNamesTbl.end(),
+ [](const std::shared_ptr<EntityAuxiliaryNames>& entityAuxiliaryNames) {
+ const auto& [key, entityNames] = *entityAuxiliaryNames;
+ /**
+ * There is only one Overal system container entity in one terminus.
+ * The entity auxiliary name PDR of that terminus with the that type of
+ * containerID will include terminus name.
+ **/
+ return (entityAuxiliaryNames &&
+ key.containerId == PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID &&
+ entityNames.size());
+ });
+
+ if (it != entityAuxiliaryNamesTbl.end())
+ {
+ const auto& [key, entityNames] = **it;
+ if (!entityNames.size())
+ {
+ return std::nullopt;
+ }
+ return entityNames[0].second;
+ }
+
+ return std::nullopt;
+}
+
void Terminus::parseTerminusPDRs()
{
std::vector<std::shared_ptr<pldm_numeric_sensor_value_pdr>>
@@ -115,6 +146,20 @@
sensorAuxiliaryNamesTbl.emplace_back(std::move(sensorAuxNames));
break;
}
+ case PLDM_ENTITY_AUXILIARY_NAMES_PDR:
+ {
+ auto entityNames = parseEntityAuxiliaryNamesPDR(pdr);
+ if (!entityNames)
+ {
+ lg2::error(
+ "Failed to parse sensor name PDR with type {TYPE} handle {HANDLE}",
+ "TYPE", pdrHdr->type, "HANDLE",
+ static_cast<uint32_t>(pdrHdr->record_handle));
+ continue;
+ }
+ entityAuxiliaryNamesTbl.emplace_back(std::move(entityNames));
+ break;
+ }
default:
{
lg2::error("Unsupported PDR with type {TYPE} handle {HANDLE}",
@@ -124,6 +169,14 @@
}
}
}
+
+ auto tName = findTerminusName();
+ if (tName && !tName.value().empty())
+ {
+ lg2::info("Terminus {TID} has Auxiliary Name {NAME}.", "TID", tid,
+ "NAME", tName.value());
+ terminusName = static_cast<std::string>(tName.value());
+ }
}
std::shared_ptr<SensorAuxiliaryNames>
@@ -151,15 +204,14 @@
auto pdr = reinterpret_cast<const struct pldm_sensor_auxiliary_names_pdr*>(
pdrData.data());
const uint8_t* ptr = pdr->names;
- std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
- sensorAuxNames{};
+ std::vector<AuxiliaryNames> sensorAuxNames{};
char16_t alignedBuffer[PLDM_STR_UTF_16_MAX_LEN];
for ([[maybe_unused]] const auto& sensor :
std::views::iota(0, static_cast<int>(pdr->sensor_count)))
{
const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
ptr += sizeof(uint8_t);
- std::vector<std::pair<NameLanguageTag, SensorName>> nameStrings{};
+ AuxiliaryNames nameStrings{};
for ([[maybe_unused]] const auto& count :
std::views::iota(0, static_cast<int>(nameStringCount)))
{
@@ -192,14 +244,8 @@
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
char16_t>{}
.to_bytes(u16NameString);
- std::replace(nameString.begin(), nameString.end(), ' ', '_');
- auto nullTerminatorPos = nameString.find('\0');
- if (nullTerminatorPos != std::string::npos)
- {
- nameString.erase(nullTerminatorPos);
- }
- nameStrings.emplace_back(
- std::make_pair(nameLanguageTag, nameString));
+ nameStrings.emplace_back(std::make_pair(
+ nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
}
sensorAuxNames.emplace_back(std::move(nameStrings));
}
@@ -207,6 +253,69 @@
pdr->sensor_id, pdr->sensor_count, std::move(sensorAuxNames));
}
+std::shared_ptr<EntityAuxiliaryNames>
+ Terminus::parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData)
+{
+ auto names_offset = sizeof(struct pldm_pdr_hdr) +
+ PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
+ auto names_size = pdrData.size() - names_offset;
+
+ size_t decodedPdrSize = sizeof(struct pldm_entity_auxiliary_names_pdr) +
+ names_size;
+ auto vPdr = std::vector<char>(decodedPdrSize);
+ auto decodedPdr =
+ reinterpret_cast<struct pldm_entity_auxiliary_names_pdr*>(vPdr.data());
+
+ auto rc = decode_entity_auxiliary_names_pdr(pdrData.data(), pdrData.size(),
+ decodedPdr, decodedPdrSize);
+
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode Entity Auxiliary Name PDR data, error {RC}.",
+ "RC", rc);
+ return nullptr;
+ }
+
+ auto vNames =
+ std::vector<pldm_entity_auxiliary_name>(decodedPdr->name_string_count);
+ decodedPdr->names = vNames.data();
+
+ rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
+ if (rc)
+ {
+ lg2::error("Failed to decode Entity Auxiliary Name, error {RC}.", "RC",
+ rc);
+ return nullptr;
+ }
+
+ AuxiliaryNames nameStrings{};
+ for (const auto& count :
+ std::views::iota(0, static_cast<int>(decodedPdr->name_string_count)))
+ {
+ std::string_view nameLanguageTag =
+ static_cast<std::string_view>(decodedPdr->names[count].tag);
+ const size_t u16NameStringLen =
+ std::char_traits<char16_t>::length(decodedPdr->names[count].name);
+ std::u16string u16NameString(decodedPdr->names[count].name,
+ u16NameStringLen);
+ std::transform(u16NameString.cbegin(), u16NameString.cend(),
+ u16NameString.begin(),
+ [](uint16_t utf16) { return be16toh(utf16); });
+ std::string nameString =
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
+ .to_bytes(u16NameString);
+ nameStrings.emplace_back(std::make_pair(
+ nameLanguageTag, pldm::utils::trimNameForDbus(nameString)));
+ }
+
+ EntityKey key{decodedPdr->container.entity_type,
+ decodedPdr->container.entity_instance_num,
+ decodedPdr->container.entity_container_id};
+
+ return std::make_shared<EntityAuxiliaryNames>(key, nameStrings);
+}
+
std::shared_ptr<pldm_numeric_sensor_value_pdr>
Terminus::parseNumericSensorPDR(const std::vector<uint8_t>& pdr)
{
@@ -225,7 +334,7 @@
{
std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>
sensorAuxNames{};
- std::vector<std::pair<NameLanguageTag, SensorName>> nameStrings{};
+ AuxiliaryNames nameStrings{};
auto pdr =
reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(sPdr.data());
@@ -244,13 +353,8 @@
std::string nameString(reinterpret_cast<const char*>(pdr->sensor_name),
pdr->sensor_name_length);
- std::replace(nameString.begin(), nameString.end(), ' ', '_');
- auto nullTerminatorPos = nameString.find('\0');
- if (nullTerminatorPos != std::string::npos)
- {
- nameString.erase(nullTerminatorPos);
- }
- nameStrings.emplace_back(std::make_pair("en", nameString));
+ nameStrings.emplace_back(
+ std::make_pair("en", pldm::utils::trimNameForDbus(nameString)));
sensorAuxNames.emplace_back(std::move(nameStrings));
return std::make_shared<SensorAuxiliaryNames>(pdr->sensor_id, 1,
diff --git a/platform-mc/terminus.hpp b/platform-mc/terminus.hpp
index 3a1a302..aacf288 100644
--- a/platform-mc/terminus.hpp
+++ b/platform-mc/terminus.hpp
@@ -21,6 +21,10 @@
namespace platform_mc
{
+using ContainerID = uint16_t;
+using EntityInstanceNumber = uint16_t;
+using EntityName = std::string;
+using EntityType = uint16_t;
using SensorId = uint16_t;
using SensorCnt = uint8_t;
using NameLanguageTag = std::string;
@@ -29,6 +33,29 @@
SensorId, SensorCnt,
std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>>;
+/** @struct EntityKey
+ *
+ * EntityKey uniquely identifies the PLDM entity and a combination of Entity
+ * Type, Entity Instance Number, Entity Container ID
+ *
+ */
+struct EntityKey
+{
+ EntityType type; //!< Entity type
+ EntityInstanceNumber instanceIdx; //!< Entity instance number
+ ContainerID containerId; //!< Entity container ID
+
+ bool operator==(const EntityKey& e) const
+ {
+ return ((type == e.type) && (instanceIdx == e.instanceIdx) &&
+ (containerId == e.containerId));
+ }
+};
+
+using AuxiliaryNames = std::vector<std::pair<NameLanguageTag, std::string>>;
+using EntityKey = struct EntityKey;
+using EntityAuxiliaryNames = std::tuple<EntityKey, AuxiliaryNames>;
+
/**
* @brief Terminus
*
@@ -90,6 +117,12 @@
return tid;
}
+ /** @brief The getter to get terminus's mctp medium */
+ std::string_view getTerminusName()
+ {
+ return terminusName;
+ }
+
/** @brief A list of PDRs fetched from Terminus */
std::vector<std::vector<uint8_t>> pdrs{};
@@ -104,6 +137,12 @@
std::shared_ptr<SensorAuxiliaryNames> getSensorAuxiliaryNames(SensorId id);
private:
+ /** @brief Find the Terminus Name from the Entity Auxiliary name list
+ * The Entity Auxiliary name list is entityAuxiliaryNamesTbl.
+ * @return terminus name in string option
+ */
+ std::optional<std::string_view> findTerminusName();
+
/** @brief Parse the numeric sensor PDRs
*
* @param[in] pdrData - the response PDRs from GetPDR command
@@ -120,6 +159,14 @@
std::shared_ptr<SensorAuxiliaryNames>
parseSensorAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData);
+ /** @brief Parse the Entity Auxiliary name PDRs
+ *
+ * @param[in] pdrData - the response PDRs from GetPDR command
+ * @return pointer to Entity Auxiliary name info struct
+ */
+ std::shared_ptr<EntityAuxiliaryNames>
+ parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData);
+
/** @brief Parse the compact numeric sensor PDRs
*
* @param[in] pdrData - the response PDRs from GetPDR command
@@ -155,6 +202,13 @@
/* @brief Sensor Auxiliary Name list */
std::vector<std::shared_ptr<SensorAuxiliaryNames>>
sensorAuxiliaryNamesTbl{};
+
+ /* @brief Entity Auxiliary Name list */
+ std::vector<std::shared_ptr<EntityAuxiliaryNames>>
+ entityAuxiliaryNamesTbl{};
+
+ /** @brief Terminus name */
+ EntityName terminusName{};
};
} // namespace platform_mc
} // namespace pldm
diff --git a/platform-mc/test/platform_manager_test.cpp b/platform-mc/test/platform_manager_test.cpp
index 2df2b48..fe2b96a 100644
--- a/platform-mc/test/platform_manager_test.cpp
+++ b/platform-mc/test/platform_manager_test.cpp
@@ -152,6 +152,164 @@
EXPECT_EQ(1, terminus->pdrs.size());
}
+TEST_F(PlatformManagerTest, parseTerminusNameTest)
+{
+ // Add terminus
+ auto mappedTid = mockTerminusManager.mapTid(pldm::MctpInfo(10, "", "", 1));
+ auto tid = mappedTid.value();
+ termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(
+ tid, 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+ auto terminus = termini[tid];
+
+ /* Set supported command by terminus */
+ auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8);
+ std::vector<uint8_t> pldmCmds(size);
+ uint8_t type = PLDM_PLATFORM;
+ uint8_t cmd = PLDM_GET_PDR;
+ auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+ pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+ cmd = PLDM_GET_PDR_REPOSITORY_INFO;
+ idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+ pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+ termini[tid]->setSupportedCommands(pldmCmds);
+
+ // queue getPDRRepositoryInfo response
+ const size_t getPDRRepositoryInfoLen =
+ PLDM_GET_PDR_REPOSITORY_INFO_RESP_BYTES;
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPDRRepositoryInfoLen>
+ getPDRRepositoryInfoResp{
+ 0x0, 0x02, 0x50, PLDM_SUCCESS,
+ 0x0, // repositoryState
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // updateTime
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // OEMUpdateTime
+ 2, 0x0, 0x0, 0x0, // recordCount
+ 0x0, 0x1, 0x0, 0x0, // repositorySize
+ 59, 0x0, 0x0, 0x0, // largestRecordSize
+ 0x0 // dataTransferHandleTimeout
+ };
+ auto rc = mockTerminusManager.enqueueResponse(
+ reinterpret_cast<pldm_msg*>(getPDRRepositoryInfoResp.data()),
+ sizeof(getPDRRepositoryInfoResp));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ // queue getPDR responses
+ const size_t getPdrRespLen = 81;
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrRespLen> getPdrResp{
+ 0x0, 0x02, 0x51, PLDM_SUCCESS, 0x1, 0x0, 0x0, 0x0, // nextRecordHandle
+ 0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
+ 0x5, // transferFlag
+ 69, 0x0, // responseCount
+ // numeric Sensor PDR
+ 0x0, 0x0, 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_NUMERIC_SENSOR_PDR, // PDRType
+ 0x0,
+ 0x0, // recordChangeNumber
+ PLDM_PDR_NUMERIC_SENSOR_PDR_FIXED_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_SENSOR_DATA_SIZE_MIN_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_RANGE_FIELD_MIN_LENGTH,
+ 0, // dataLength
+ 0,
+ 0, // PLDMTerminusHandle
+ 0x1,
+ 0x0, // sensorID=1
+ 120,
+ 0, // entityType=Power Supply(120)
+ 1,
+ 0, // entityInstanceNumber
+ 0x1,
+ 0x0, // containerID=1
+ PLDM_NO_INIT, // sensorInit
+ false, // sensorAuxiliaryNamesPDR
+ PLDM_SENSOR_UNIT_DEGRESS_C, // baseUint(2)=degrees C
+ 1, // unitModifier = 1
+ 0, // rateUnit
+ 0, // baseOEMUnitHandle
+ 0, // auxUnit
+ 0, // auxUnitModifier
+ 0, // auxRateUnit
+ 0, // rel
+ 0, // auxOEMUnitHandle
+ true, // isLinear
+ PLDM_SENSOR_DATA_SIZE_UINT8, // sensorDataSize
+ 0, 0, 0xc0,
+ 0x3f, // resolution=1.5
+ 0, 0, 0x80,
+ 0x3f, // offset=1.0
+ 0,
+ 0, // accuracy
+ 0, // plusTolerance
+ 0, // minusTolerance
+ 2, // hysteresis
+ 0, // supportedThresholds
+ 0, // thresholdAndHysteresisVolatility
+ 0, 0, 0x80,
+ 0x3f, // stateTransistionInterval=1.0
+ 0, 0, 0x80,
+ 0x3f, // updateInverval=1.0
+ 255, // maxReadable
+ 0, // minReadable
+ PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+ 0, // rangeFieldsupport
+ 0, // nominalValue
+ 0, // normalMax
+ 0, // normalMin
+ 0, // warningHigh
+ 0, // warningLow
+ 0, // criticalHigh
+ 0, // criticalLow
+ 0, // fatalHigh
+ 0 // fatalLow
+ };
+ rc = mockTerminusManager.enqueueResponse(
+ reinterpret_cast<pldm_msg*>(getPdrResp.data()), sizeof(getPdrResp));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ const size_t getPdrAuxNameRespLen = 39;
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrAuxNameRespLen>
+ getPdrAuxNameResp{
+ 0x0, 0x02, 0x51, PLDM_SUCCESS, 0x0, 0x0, 0x0,
+ 0x0, // nextRecordHandle
+ 0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
+ 0x5, // transferFlag
+ 0x1b, 0x0, // responseCount
+ // Common PDR Header
+ 0x1, 0x0, 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+ 0x1,
+ 0x0, // recordChangeNumber
+ 0x11,
+ 0, // dataLength
+ /* Entity Auxiliary Names PDR Data*/
+ 3,
+ 0x80, // entityType system software
+ 0x1,
+ 0x0, // Entity instance number =1
+ 0,
+ 0, // Overal system
+ 0, // shared Name Count one name only
+ 01, // nameStringCount
+ 0x65, 0x6e, 0x00,
+ 0x00, // Language Tag "en"
+ 0x53, 0x00, 0x30, 0x00,
+ 0x00 // Entity Name "S0"
+ };
+ rc = mockTerminusManager.enqueueResponse(
+ reinterpret_cast<pldm_msg*>(getPdrAuxNameResp.data()),
+ sizeof(getPdrAuxNameResp));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ stdexec::sync_wait(platformManager.initTerminus());
+ EXPECT_EQ(true, terminus->initialized);
+ EXPECT_EQ(2, terminus->pdrs.size());
+ EXPECT_EQ("S0", terminus->getTerminusName());
+}
+
TEST_F(PlatformManagerTest, initTerminusDontSupportGetPDRTest)
{
// Add terminus