Handle FRU records from host
- Get FRU record table from host and traverse the entity structure
from the FRU record set PDR, and create D-Bus object.
- When the FRU field type is a PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE
, the location code is used to populate the
xyz.openbmc_project.Inventory.Decorator.LocationCode D-Bus
interface for the corresponding FRU.
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I482c4d371f76b4881109ef420dfd9543e1aa810b
diff --git a/host-bmc/custom_dbus.cpp b/host-bmc/custom_dbus.cpp
new file mode 100644
index 0000000..6427a93
--- /dev/null
+++ b/host-bmc/custom_dbus.cpp
@@ -0,0 +1,31 @@
+#include "custom_dbus.hpp"
+
+namespace pldm
+{
+namespace dbus
+{
+void CustomDBus::setLocationCode(const std::string& path, std::string value)
+{
+ if (!location.contains(path))
+ {
+ location.emplace(path,
+ std::make_unique<LocationIntf>(
+ pldm::utils::DBusHandler::getBus(), path.c_str()));
+ }
+
+ location.at(path)->locationCode(value);
+}
+
+std::optional<std::string>
+ CustomDBus::getLocationCode(const std::string& path) const
+{
+ if (location.contains(path))
+ {
+ return location.at(path)->locationCode();
+ }
+
+ return std::nullopt;
+}
+
+} // namespace dbus
+} // namespace pldm
diff --git a/host-bmc/custom_dbus.hpp b/host-bmc/custom_dbus.hpp
new file mode 100644
index 0000000..6ff7daa
--- /dev/null
+++ b/host-bmc/custom_dbus.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "common/utils.hpp"
+
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Inventory/Decorator/LocationCode/server.hpp>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace pldm
+{
+namespace dbus
+{
+using ObjectPath = std::string;
+
+using LocationIntf = sdbusplus::server::object::object<
+ sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::
+ LocationCode>;
+
+/** @class CustomDBus
+ * @brief This is a custom D-Bus object, used to add D-Bus interface and update
+ * the corresponding properties value.
+ */
+class CustomDBus
+{
+ private:
+ CustomDBus() {}
+
+ public:
+ CustomDBus(const CustomDBus&) = delete;
+ CustomDBus(CustomDBus&&) = delete;
+ CustomDBus& operator=(const CustomDBus&) = delete;
+ CustomDBus& operator=(CustomDBus&&) = delete;
+ ~CustomDBus() = default;
+
+ static CustomDBus& getCustomDBus()
+ {
+ static CustomDBus customDBus;
+ return customDBus;
+ }
+
+ public:
+ /** @brief Set the LocationCode property
+ *
+ * @param[in] path - The object path
+ *
+ * @param[in] value - The value of the LocationCode property
+ */
+ void setLocationCode(const std::string& path, std::string value);
+
+ /** @brief Get the LocationCode property
+ *
+ * @param[in] path - The object path
+ *
+ * @return std::optional<std::string> - The value of the LocationCode
+ * property
+ */
+ std::optional<std::string> getLocationCode(const std::string& path) const;
+
+ private:
+ std::unordered_map<ObjectPath, std::unique_ptr<LocationIntf>> location;
+};
+
+} // namespace dbus
+} // namespace pldm
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index 3ceae13..32993ee 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -1,5 +1,12 @@
#include "host_pdr_handler.hpp"
+#include "libpldm/fru.h"
+#ifdef OEM_IBM
+#include "libpldm/fru_oem_ibm.h"
+#endif
+
+#include "custom_dbus.hpp"
+
#include <assert.h>
#include <libpldm/pldm.h>
@@ -21,6 +28,7 @@
using namespace sdbusplus::bus::match::rules;
using Json = nlohmann::json;
namespace fs = std::filesystem;
+using namespace pldm::dbus;
constexpr auto fruJson = "host_frus.json";
const Json emptyJson{};
const std::vector<Json> emptyJsonList{};
@@ -403,6 +411,7 @@
{
static bool merged = false;
static PDRList stateSensorPDRs{};
+ static PDRList fruRecordSetPDRs{};
uint32_t nextRecordHandle{};
uint8_t tlEid = 0;
bool tlValid = true;
@@ -521,6 +530,7 @@
{
pdrTerminusHandle =
extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
+ fruRecordSetPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
{
@@ -558,11 +568,13 @@
/*received last record*/
this->parseStateSensorPDRs(stateSensorPDRs);
+ this->createDbusObjects(fruRecordSetPDRs);
if (isHostUp())
{
this->setHostSensorState(stateSensorPDRs);
}
stateSensorPDRs.clear();
+ fruRecordSetPDRs.clear();
entityAssociations.clear();
if (merged)
@@ -817,4 +829,211 @@
}
}
}
+
+void HostPDRHandler::getFRURecordTableMetadataByRemote(
+ const PDRList& fruRecordSetPDRs)
+{
+ auto instanceId = instanceIdDb.next(mctp_eid);
+ std::vector<uint8_t> requestMsg(
+ sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
+
+ // GetFruRecordTableMetadata
+ auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto rc = encode_get_fru_record_table_metadata_req(
+ instanceId, request, requestMsg.size() - sizeof(pldm_msg_hdr));
+ if (rc != PLDM_SUCCESS)
+ {
+ instanceIdDb.free(mctp_eid, instanceId);
+ lg2::error(
+ "Failed to encode_get_fru_record_table_metadata_req, rc = {RC}",
+ "RC", lg2::hex, rc);
+ return;
+ }
+
+ auto getFruRecordTableMetadataResponseHandler =
+ [this, fruRecordSetPDRs](mctp_eid_t /*eid*/, const pldm_msg* response,
+ size_t respMsgLen) {
+ if (response == nullptr || !respMsgLen)
+ {
+ lg2::error(
+ "Failed to receive response for the Get FRU Record Table Metadata");
+ return;
+ }
+
+ uint8_t cc = 0;
+ uint8_t fru_data_major_version, fru_data_minor_version;
+ uint32_t fru_table_maximum_size, fru_table_length;
+ uint16_t total_record_set_identifiers;
+ uint16_t total;
+ uint32_t checksum;
+
+ auto rc = decode_get_fru_record_table_metadata_resp(
+ response, respMsgLen, &cc, &fru_data_major_version,
+ &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
+ &total_record_set_identifiers, &total, &checksum);
+
+ if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
+ {
+ lg2::error(
+ "Faile to decode get fru record table metadata resp, Message Error: {RC}, cc: {CC}",
+ "RC", lg2::hex, rc, "CC", cc);
+ return;
+ }
+
+ // pass total to getFRURecordTableByRemote
+ this->getFRURecordTableByRemote(fruRecordSetPDRs, total);
+ };
+
+ rc = handler->registerRequest(
+ mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA,
+ std::move(requestMsg),
+ std::move(getFruRecordTableMetadataResponseHandler));
+ if (rc != PLDM_SUCCESS)
+ {
+ lg2::error("Failed to send the the Set State Effecter States request");
+ }
+
+ return;
+}
+
+void HostPDRHandler::getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
+ uint16_t totalTableRecords)
+{
+ fruRecordData.clear();
+
+ if (!totalTableRecords)
+ {
+ lg2::error("Failed to get fru record table");
+ return;
+ }
+
+ auto instanceId = instanceIdDb.next(mctp_eid);
+ std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+ PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
+
+ // send the getFruRecordTable command
+ auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto rc = encode_get_fru_record_table_req(
+ instanceId, 0, PLDM_GET_FIRSTPART, request,
+ requestMsg.size() - sizeof(pldm_msg_hdr));
+ if (rc != PLDM_SUCCESS)
+ {
+ instanceIdDb.free(mctp_eid, instanceId);
+ lg2::error("Failed to encode_get_fru_record_table_req, rc = {RC}", "RC",
+ lg2::hex, rc);
+ return;
+ }
+
+ auto getFruRecordTableResponseHandler =
+ [totalTableRecords, this, fruRecordSetPDRs](
+ mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
+ if (response == nullptr || !respMsgLen)
+ {
+ lg2::error(
+ "Failed to receive response for the Get FRU Record Table");
+ return;
+ }
+
+ uint8_t cc = 0;
+ uint32_t next_data_transfer_handle = 0;
+ uint8_t transfer_flag = 0;
+ size_t fru_record_table_length = 0;
+ std::vector<uint8_t> fru_record_table_data(respMsgLen -
+ sizeof(pldm_msg_hdr));
+ auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
+ auto rc = decode_get_fru_record_table_resp(
+ responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &cc,
+ &next_data_transfer_handle, &transfer_flag,
+ fru_record_table_data.data(), &fru_record_table_length);
+
+ if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
+ {
+ lg2::error(
+ "Failed to decode get fru record table resp, Message Error: {RC}, cc: {CC}",
+ "RC", lg2::hex, rc, "CC", cc);
+ return;
+ }
+
+ fruRecordData = responder::pdr_utils::parseFruRecordTable(
+ fru_record_table_data.data(), fru_record_table_length);
+
+ if (totalTableRecords != fruRecordData.size())
+ {
+ fruRecordData.clear();
+
+ lg2::error("failed to parse fru recrod data format.");
+ return;
+ }
+
+ this->setFRUDataOnDBus(fruRecordSetPDRs, fruRecordData);
+ };
+
+ rc = handler->registerRequest(
+ mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE,
+ std::move(requestMsg), std::move(getFruRecordTableResponseHandler));
+ if (rc != PLDM_SUCCESS)
+ {
+ lg2::error("Failed to send the the Set State Effecter States request");
+ }
+}
+
+std::optional<uint16_t> HostPDRHandler::getRSI(const PDRList& fruRecordSetPDRs,
+ const pldm_entity& entity)
+{
+ for (const auto& pdr : fruRecordSetPDRs)
+ {
+ auto fruPdr = reinterpret_cast<const pldm_pdr_fru_record_set*>(
+ const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
+
+ if (fruPdr->entity_type == entity.entity_type &&
+ fruPdr->entity_instance_num == entity.entity_instance_num)
+ {
+ return fruPdr->fru_rsi;
+ }
+ }
+
+ return std::nullopt;
+}
+
+void HostPDRHandler::setFRUDataOnDBus(
+ const PDRList& fruRecordSetPDRs,
+ const std::vector<responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
+{
+ for (const auto& entity : objPathMap)
+ {
+ pldm_entity node = pldm_entity_extract(entity.second);
+ auto fruRSI = getRSI(fruRecordSetPDRs, node);
+
+ for (const auto& data : fruRecordData)
+ {
+ if (!fruRSI || *fruRSI != data.fruRSI)
+ {
+ continue;
+ }
+
+ if (data.fruRecType == PLDM_FRU_RECORD_TYPE_OEM)
+ {
+ for (const auto& tlv : data.fruTLV)
+ {
+ if (tlv.fruFieldType ==
+ PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE)
+ {
+ CustomDBus::getCustomDBus().setLocationCode(
+ entity.first,
+ std::string(reinterpret_cast<const char*>(
+ tlv.fruFieldValue.data()),
+ tlv.fruFieldLen));
+ }
+ }
+ }
+ }
+ }
+}
+void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
+{
+ // TODO: Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
+
+ getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
+}
+
} // namespace pldm
diff --git a/host-bmc/host_pdr_handler.hpp b/host-bmc/host_pdr_handler.hpp
index 5dc5e9e..1821005 100644
--- a/host-bmc/host_pdr_handler.hpp
+++ b/host-bmc/host_pdr_handler.hpp
@@ -205,6 +205,48 @@
void _processFetchPDREvent(uint32_t nextRecordHandle,
sdeventplus::source::EventBase& source);
+ /** @brief Get FRU record table metadata by remote PLDM terminus
+ *
+ * @param[out] uint16_t - total table records
+ */
+ void getFRURecordTableMetadataByRemote(const PDRList& fruRecordSetPDRs);
+
+ /** @brief Set Location Code in the dbus objects
+ *
+ * @param[in] fruRecordSetPDRs - the Fru Record set PDR's
+ * @param[in] fruRecordData - the Fru Record Data
+ */
+
+ void setFRUDataOnDBus(
+ const PDRList& fruRecordSetPDRs,
+ const std::vector<responder::pdr_utils::FruRecordDataFormat>&
+ fruRecordData);
+
+ /** @brief Get FRU record table by remote PLDM terminus
+ *
+ * @param[in] fruRecordSetPDRs - the Fru Record set PDR's
+ * @param[in] totalTableRecords - the Number of total table records
+ * @return
+ */
+ void getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
+ uint16_t totalTableRecords);
+
+ /** @brief Create Dbus objects by remote PLDM entity Fru PDRs
+ *
+ * @param[in] fruRecordSetPDRs - fru record set pdr
+ *
+ * @ return
+ */
+ void createDbusObjects(const PDRList& fruRecordSetPDRs);
+
+ /** @brief Get FRU Record Set Identifier from FRU Record data Format
+ * @param[in] fruRecordSetPDRs - fru record set pdr
+ * @param[in] entity - PLDM entity information
+ * @return
+ */
+ std::optional<uint16_t> getRSI(const PDRList& fruRecordSetPDRs,
+ const pldm_entity& entity);
+
/** @brief fd of MCTP communications socket */
int mctp_fd;
/** @brief MCTP EID of host firmware */
@@ -277,6 +319,14 @@
/** @brief maps an entity name to map, maps to entity name to pldm_entity
*/
utils::EntityAssociations entityAssociations;
+
+ /** @brief the vector of FRU Record Data Format
+ */
+ std::vector<responder::pdr_utils::FruRecordDataFormat> fruRecordData;
+
+ /** @brief Object path and entity association and is only loaded once
+ */
+ bool objPathEntityAssociation;
};
} // namespace pldm
diff --git a/host-bmc/test/custom_dbus_test.cpp b/host-bmc/test/custom_dbus_test.cpp
new file mode 100644
index 0000000..4549c0d
--- /dev/null
+++ b/host-bmc/test/custom_dbus_test.cpp
@@ -0,0 +1,16 @@
+#include "../custom_dbus.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace pldm::dbus;
+TEST(CustomDBus, LocationCode)
+{
+ std::string tmpPath = "/abc/def";
+ std::string locationCode = "testLocationCode";
+
+ CustomDBus::getCustomDBus().setLocationCode(tmpPath, locationCode);
+ auto retLocationCode = CustomDBus::getCustomDBus().getLocationCode(tmpPath);
+
+ EXPECT_NE(retLocationCode, std::nullopt);
+ EXPECT_EQ(locationCode, retLocationCode);
+}
diff --git a/host-bmc/test/meson.build b/host-bmc/test/meson.build
index d19d4ef..9dee59f 100644
--- a/host-bmc/test/meson.build
+++ b/host-bmc/test/meson.build
@@ -4,11 +4,13 @@
test_sources = [
'../../common/utils.cpp',
+ '../custom_dbus.cpp',
]
tests = [
'dbus_to_host_effecter_test',
'utils_test',
+ 'custom_dbus_test',
]
foreach t : tests
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index ad4207b..7de2cf2 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -26,6 +26,7 @@
'../host-bmc/dbus_to_event_handler.cpp',
'../host-bmc/dbus_to_host_effecters.cpp',
'../host-bmc/host_condition.cpp',
+ '../host-bmc/custom_dbus.cpp',
'event_parser.cpp'
]
diff --git a/libpldmresponder/pdr_utils.cpp b/libpldmresponder/pdr_utils.cpp
index 7e5e491..06832a1 100644
--- a/libpldmresponder/pdr_utils.cpp
+++ b/libpldmresponder/pdr_utils.cpp
@@ -1,3 +1,5 @@
+#include "libpldm/fru.h"
+
#include "pdr.hpp"
#include <libpldm/platform.h>
@@ -16,6 +18,15 @@
{
namespace pdr_utils
{
+// Refer: DSP0257_1.0.0 Table 2
+// 7: uint16_t(FRU Record Set Identifier), uint8_t(FRU Record Type),
+// uint8_t(Number of FRU fields), uint8_t(Encoding Type for FRU fields),
+// uint8_t(FRU Field Type), uint8_t(FRU Field Length)
+static constexpr uint8_t fruRecordDataFormatLength = 7;
+
+// // 2: 1byte FRU Field Type, 1byte FRU Field Length
+static constexpr uint8_t fruFieldTypeLength = 2;
+
pldm_pdr* Repo::getPdr() const
{
return repo;
@@ -196,6 +207,58 @@
std::move(sensorInfo));
}
+std::vector<FruRecordDataFormat> parseFruRecordTable(const uint8_t* fruData,
+ size_t fruLen)
+{
+ // Refer: DSP0257_1.0.0 Table 2
+ // 7: uint16_t(FRU Record Set Identifier), uint8_t(FRU Record Type),
+ // uint8_t(Number of FRU fields), uint8_t(Encoding Type for FRU fields),
+ // uint8_t(FRU Field Type), uint8_t(FRU Field Length)
+ if (fruLen < fruRecordDataFormatLength)
+ {
+ lg2::error("Invalid fru len: {FRULEN}", "FRULEN", fruLen);
+ return {};
+ }
+
+ std::vector<FruRecordDataFormat> frus;
+
+ size_t index = 0;
+ while (index < fruLen)
+ {
+ FruRecordDataFormat fru;
+
+ auto record = reinterpret_cast<const pldm_fru_record_data_format*>(
+ fruData + index);
+ fru.fruRSI = (int)le16toh(record->record_set_id);
+ fru.fruRecType = record->record_type;
+ fru.fruNum = record->num_fru_fields;
+ fru.fruEncodeType = record->encoding_type;
+
+ index += 5;
+
+ std::ranges::for_each(std::views::iota(0, (int)record->num_fru_fields),
+ [fruData, &fru, &index](int) {
+ auto tlv =
+ reinterpret_cast<const pldm_fru_record_tlv*>(fruData + index);
+ FruTLV frutlv;
+ frutlv.fruFieldType = tlv->type;
+ frutlv.fruFieldLen = tlv->length;
+ frutlv.fruFieldValue.resize(tlv->length);
+ for (const auto& i : std::views::iota(0, (int)tlv->length))
+ {
+ memcpy(frutlv.fruFieldValue.data() + i, tlv->value + i, 1);
+ }
+ fru.fruTLV.push_back(frutlv);
+
+ // 2: 1byte FRU Field Type, 1byte FRU Field Length
+ index += fruFieldTypeLength + (unsigned)tlv->length;
+ });
+
+ frus.push_back(fru);
+ }
+
+ return frus;
+}
} // namespace pdr_utils
} // namespace responder
} // namespace pldm
diff --git a/libpldmresponder/pdr_utils.hpp b/libpldmresponder/pdr_utils.hpp
index 739058e..b44120a 100644
--- a/libpldmresponder/pdr_utils.hpp
+++ b/libpldmresponder/pdr_utils.hpp
@@ -38,6 +38,22 @@
PLDM_SENSOR_ID
};
+struct FruTLV
+{
+ uint8_t fruFieldType;
+ uint8_t fruFieldLen;
+ std::vector<uint8_t> fruFieldValue;
+};
+
+struct FruRecordDataFormat
+{
+ uint16_t fruRSI;
+ uint8_t fruRecType;
+ uint8_t fruNum;
+ uint8_t fruEncodeType;
+ std::vector<FruTLV> fruTLV;
+};
+
/** @struct PdrEntry
* PDR entry structure that acts as a PDR record structure in the PDR
* repository to handle PDR APIs.
@@ -214,6 +230,18 @@
pldm::pdr::SensorInfo>
parseStateSensorPDR(const std::vector<uint8_t>& stateSensorPdr);
+/** @brief Parse FRU record table and return the vector of the FRU record data
+ * format structure
+ *
+ * @param[in] fruData - fru data
+ * @param[in] fruLen - fru len
+ *
+ * @return std::vector<FruRecordDataFormat> - the vector of the FRU record data
+ * format structure
+ */
+std::vector<FruRecordDataFormat> parseFruRecordTable(const uint8_t* fruData,
+ size_t fruLen);
+
} // namespace pdr_utils
} // namespace responder
} // namespace pldm
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index b8315b7..8e57f20 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -202,6 +202,8 @@
InstanceIdDb instanceIdDb;
dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm",
instanceIdDb);
+ sdbusplus::server::manager::manager inventoryManager(
+ bus, "/xyz/openbmc_project/inventory");
Invoker invoker{};
requester::Handler<requester::Request> reqHandler(