blob: c9360dfffa1ec75ac3e447dbc79e94e61b028199 [file] [log] [blame]
#include "fru_oem_ibm.hpp"
#include <com/ibm/VPD/Manager/client.hpp>
#include <phosphor-logging/lg2.hpp>
#include <format>
#include <ranges>
PHOSPHOR_LOG2_USING;
namespace pldm
{
namespace responder
{
namespace oem_ibm_fru
{
void pldm::responder::oem_ibm_fru::Handler::setIBMFruHandler(
pldm::responder::fru::Handler* handler)
{
fruHandler = handler;
}
int pldm::responder::oem_ibm_fru::Handler::processOEMFRUTable(
const std::vector<uint8_t>& fruData)
{
uint8_t dataSize = 0;
const uint8_t* data = fruData.data();
while (dataSize < fruData.size())
{
auto record =
reinterpret_cast<const pldm_fru_record_data_format*>(data);
if (!record)
{
return PLDM_ERROR_INVALID_DATA;
}
auto& entityAssociationMap = getAssociateEntityMap();
uint16_t fruRSI = le16toh(record->record_set_id);
dataSize += sizeof(pldm_fru_record_data_format) -
sizeof(pldm_fru_record_tlv);
data += dataSize;
for ([[maybe_unused]] const auto& i :
std::views::iota(0, (int)record->num_fru_fields))
{
auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(data);
if (!tlv)
{
return PLDM_ERROR_INVALID_DATA;
}
if (tlv->type == PLDM_OEM_FRU_FIELD_TYPE_PCIE_CONFIG_SPACE_DATA)
{
auto pcieData =
reinterpret_cast<const PcieConfigSpaceData*>(tlv->value);
if (!pcieData)
{
return PLDM_ERROR_INVALID_DATA;
}
auto vendorId = std::format("0x{:04x}",
htole16(pcieData->vendorId));
auto deviceId = std::format("0x{:04x}",
htole16(pcieData->deviceId));
auto revisionId = std::format("0x{:02x}",
htole16(pcieData->revisionId));
std::string classCode = "0x";
for (const auto& ele : pcieData->classCode)
{
classCode += std::format("{:02x}", ele);
}
auto subSystemVendorId = std::format(
"0x{:04x}", htole16(pcieData->subSystemVendorId));
auto subSystemId = std::format("0x{:04x}",
htole16(pcieData->subSystemId));
updateDBusProperty(fruRSI, entityAssociationMap, vendorId,
deviceId, revisionId, classCode,
subSystemVendorId, subSystemId);
}
if (tlv->type == PLDM_OEM_IBM_FRU_FIELD_TYPE_FIRMWARE_UAK)
{
std::vector<uint8_t> value(&tlv->value[0],
&tlv->value[tlv->length]);
setFirmwareUAK(value);
}
// length of tlv is removed from the structure pldm_fru_record_tlv
// and the new tlv length is added back.
dataSize += sizeof(pldm_fru_record_tlv) - sizeof(uint8_t) +
tlv->length;
data += dataSize;
}
}
return PLDM_SUCCESS;
}
void Handler::updateDBusProperty(
uint16_t fruRSI, const AssociatedEntityMap& fruAssociationMap,
const std::string& vendorId, const std::string& deviceId,
const std::string& revisionId, const std::string& classCode,
const std::string& subSystemVendorId, const std::string& subSystemId)
{
uint16_t entityType{};
uint16_t entityInstanceNum{};
uint16_t containerId{};
uint16_t terminusHandle{};
const pldm_pdr_record* record{};
record = pldm_pdr_fru_record_set_find_by_rsi(
pdrRepo, fruRSI, &terminusHandle, &entityType, &entityInstanceNum,
&containerId);
if (record)
{
for (const auto& [key, value] : fruAssociationMap)
{
if (entityInstanceNum == value.entity_instance_num &&
entityType == value.entity_type &&
containerId == value.entity_container_id)
{
if (!(pldm::responder::utils::checkIfIBMFru(key)))
{
pldm::utils::setFruPresence(key, true);
}
dbus_map_update(key, "Function0VendorId", vendorId);
dbus_map_update(key, "Function0DeviceId", deviceId);
dbus_map_update(key, "Function0RevisionId", revisionId);
dbus_map_update(key, "Function0ClassCode", classCode);
dbus_map_update(key, "Function0SubsystemVendorId",
subSystemVendorId);
dbus_map_update(key, "Function0SubsystemId", subSystemId);
}
}
}
}
void Handler::dbus_map_update(const std::string& adapterObjPath,
const std::string& propertyName,
const std::string& propValue)
{
pldm::utils::PropertyValue value = propValue;
pldm::utils::DBusMapping dbusMapping;
dbusMapping.objectPath = adapterObjPath;
dbusMapping.interface = "xyz.openbmc_project.Inventory.Item.PCIeDevice";
dbusMapping.propertyName = propertyName;
dbusMapping.propertyType = "string";
try
{
pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
}
catch (const std::exception& e)
{
error("Failed to set '{PROPERTY}' property: {ERROR}", "PROPERTY",
propertyName, "ERROR", e);
}
}
void Handler::setFirmwareUAK(const std::vector<uint8_t>& data)
{
using VPDManager = sdbusplus::client::com::ibm::vpd::Manager<>;
static constexpr auto uakObjPath = "/com/ibm/VPD/Manager";
static constexpr auto fruPath =
"/xyz/openbmc_project/inventory/system/chassis/motherboard";
auto& bus = pldm::utils::DBusHandler::getBus();
try
{
auto service = pldm::utils::DBusHandler().getService(
uakObjPath, VPDManager::interface);
auto method = bus.new_method_call(
service.c_str(), uakObjPath, VPDManager::interface, "WriteKeyword");
method.append(static_cast<sdbusplus::message::object_path>(fruPath),
"UTIL", "D8", data);
bus.call_noreply(method);
}
catch (const std::exception& e)
{
error("Failed to make a DBus call to VPD manager: {ERROR}", "ERROR", e);
}
}
} // namespace oem_ibm_fru
} // namespace responder
} // namespace pldm