platform-mc: discovery FRU data from terminus
As [1], `pldmd` will host Fru D-Bus inventory object path and Fru
`xyz.openbmc_project.Inventory.Decorator.*` D-Bus interfaces of one PLDM
terminus.
[1] https://github.com/openbmc/docs/blob/master/designs/pldm-stack.md#processing-pldm-fru-information-sent-down-by-the-host-firmware
Support getting FRU data from terminus in the terminus discovery phase
and expose FRU data to the Fru D-Bus properties in
`xyz.openbmc_project.Inventory.Decorator.Asset`,
`xyz.openbmc_project.Inventory.Decorator.AssetTag`,
`xyz.openbmc_project.Inventory.Decorator.Compatible` and
`xyz.openbmc_project.Inventory.Decorator.Revision` interfaces of
`xyz/openbmc_project/FruPldm/<Terminus_Name>` D-Bus object path which is
created by `xyz.openbmc_project.PLDM` D-Bus service. The object path
will be available until the endpoint EID is removed from the MCTP
D-Bus.
```
busctl introspect xyz.openbmc_project.PLDM /xyz/openbmc_project/inventory/system/board/S0
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
xyz.openbmc_project.Inventory.Decorator.Asset interface - - -
.BuildDate property s "" emits-change writable
.Manufacturer property s "" emits-change writable
.Model property s "00014003" emits-change writable
.PartNumber property s "" emits-change writable
.SerialNumber property s "000000218" emits-change writable
.SparePartNumber property s "" emits-change writable
.SubModel property s "" emits-change writable
xyz.openbmc_project.Inventory.Decorator.AssetTag interface - - -
.AssetTag property s "" emits-change writable
xyz.openbmc_project.Inventory.Decorator.Compatible interface - - -
.Names property as 0 emits-change writable
xyz.openbmc_project.Inventory.Decorator.Revision interface - - -
.Version property s "x.x.00008.004" emits-change writable
xyz.openbmc_project.Inventory.Item.Board interface - - -
```
Signed-off-by: Dung Cao <dung@os.amperecomputing.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I4f8869f8fee0fc0f8a5a5670d9a42fc7f48cc798
diff --git a/platform-mc/terminus.cpp b/platform-mc/terminus.cpp
index 70d1112..99ba92b 100644
--- a/platform-mc/terminus.cpp
+++ b/platform-mc/terminus.cpp
@@ -2,6 +2,7 @@
#include "libpldm/platform.h"
+#include "dbus_impl_fru.hpp"
#include "terminus_manager.hpp"
#include <common/utils.hpp>
@@ -92,11 +93,18 @@
return false;
}
+ /* inventory object is created */
+ if (inventoryItemBoardInft)
+ {
+ return false;
+ }
+
inventoryPath = "/xyz/openbmc_project/inventory/system/board/" + tName;
try
{
- inventoryItemBoardInft = std::make_unique<InventoryItemBoardIntf>(
- utils::DBusHandler::getBus(), inventoryPath.c_str());
+ inventoryItemBoardInft =
+ std::make_unique<pldm::dbus_api::PldmEntityReq>(
+ utils::DBusHandler::getBus(), inventoryPath.c_str());
return true;
}
catch (const sdbusplus::exception_t& e)
@@ -216,7 +224,8 @@
if (createInventoryPath(terminusName))
{
- lg2::error("Terminus ID {TID}: Created Inventory path.", "TID", tid);
+ lg2::error("Terminus ID {TID}: Created Inventory path {PATH}.", "TID",
+ tid, "PATH", inventoryPath);
}
for (auto pdr : numericSensorPdrs)
@@ -569,5 +578,134 @@
return nullptr;
}
+
+/** @brief Check if a pointer is go through end of table
+ * @param[in] table - pointer to FRU record table
+ * @param[in] p - pointer to each record of FRU record table
+ * @param[in] tableSize - FRU table size
+ */
+static bool isTableEnd(const uint8_t* table, const uint8_t* p,
+ const size_t tableSize)
+{
+ auto offset = p - table;
+ return (tableSize - offset) < sizeof(struct pldm_fru_record_data_format);
+}
+
+void Terminus::updateInventoryWithFru(const uint8_t* fruData,
+ const size_t fruLen)
+{
+ auto tmp = getTerminusName();
+ if (!tmp || tmp.value().empty())
+ {
+ lg2::error(
+ "Terminus ID {TID}: Failed to update Inventory with Fru Data - error : Terminus name is empty.",
+ "TID", tid);
+ return;
+ }
+
+ if (createInventoryPath(static_cast<std::string>(tmp.value())))
+ {
+ lg2::info("Terminus ID {TID}: Created Inventory path.", "TID", tid);
+ }
+
+ auto ptr = fruData;
+ while (!isTableEnd(fruData, ptr, fruLen))
+ {
+ auto record = reinterpret_cast<const pldm_fru_record_data_format*>(ptr);
+ ptr += sizeof(pldm_fru_record_data_format) -
+ sizeof(pldm_fru_record_tlv);
+
+ if (!record->num_fru_fields)
+ {
+ lg2::error(
+ "Invalid number of fields {NUM} of Record ID Type {TYPE} of terminus {TID}",
+ "NUM", record->num_fru_fields, "TYPE", record->record_type,
+ "TID", tid);
+ return;
+ }
+
+ if (record->record_type != PLDM_FRU_RECORD_TYPE_GENERAL)
+ {
+ lg2::error(
+ "Does not support Fru Record ID Type {TYPE} of terminus {TID}",
+ "TYPE", record->record_type, "TID", tid);
+
+ for ([[maybe_unused]] const auto& idx :
+ std::views::iota(0, static_cast<int>(record->num_fru_fields)))
+ {
+ auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
+ ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
+ }
+ continue;
+ }
+ /* FRU General record type */
+ for ([[maybe_unused]] const auto& idx :
+ std::views::iota(0, static_cast<int>(record->num_fru_fields)))
+ {
+ auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(ptr);
+ std::string fruField{};
+ if (tlv->type != PLDM_FRU_FIELD_TYPE_IANA)
+ {
+ auto strOptional =
+ pldm::utils::fruFieldValuestring(tlv->value, tlv->length);
+ if (!strOptional)
+ {
+ ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
+ continue;
+ }
+ fruField = strOptional.value();
+
+ if (fruField.empty())
+ {
+ ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
+ continue;
+ }
+ }
+
+ switch (tlv->type)
+ {
+ case PLDM_FRU_FIELD_TYPE_MODEL:
+ inventoryItemBoardInft->model(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_PN:
+ inventoryItemBoardInft->partNumber(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_SN:
+ inventoryItemBoardInft->serialNumber(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_MANUFAC:
+ inventoryItemBoardInft->manufacturer(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_NAME:
+ inventoryItemBoardInft->names({fruField});
+ break;
+ case PLDM_FRU_FIELD_TYPE_VERSION:
+ inventoryItemBoardInft->version(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_ASSET_TAG:
+ inventoryItemBoardInft->assetTag(fruField);
+ break;
+ case PLDM_FRU_FIELD_TYPE_VENDOR:
+ case PLDM_FRU_FIELD_TYPE_CHASSIS:
+ case PLDM_FRU_FIELD_TYPE_SKU:
+ case PLDM_FRU_FIELD_TYPE_DESC:
+ case PLDM_FRU_FIELD_TYPE_EC_LVL:
+ case PLDM_FRU_FIELD_TYPE_OTHER:
+ break;
+ case PLDM_FRU_FIELD_TYPE_IANA:
+ auto iana =
+ pldm::utils::fruFieldParserU32(tlv->value, tlv->length);
+ if (!iana)
+ {
+ ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
+ continue;
+ }
+ break;
+ }
+ ptr += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
+ }
+ }
+}
+
} // namespace platform_mc
} // namespace pldm