blob: 0d85d2bcbfd9675da90e14a809378718cd842afa [file] [log] [blame]
#include "host_pdr_handler.hpp"
#include <libpldm/fru.h>
#ifdef OEM_IBM
#include <libpldm/oem/ibm/fru.h>
#endif
#include "custom_dbus.hpp"
#include <assert.h>
#include <nlohmann/json.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdeventplus/clock.hpp>
#include <sdeventplus/exception.hpp>
#include <sdeventplus/source/io.hpp>
#include <sdeventplus/source/time.hpp>
#include <fstream>
#include <type_traits>
PHOSPHOR_LOG2_USING;
namespace pldm
{
using namespace pldm::responder::events;
using namespace pldm::utils;
using namespace sdbusplus::bus::match::rules;
using namespace pldm::responder::pdr_utils;
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{};
template <typename T>
uint16_t extractTerminusHandle(std::vector<uint8_t>& pdr)
{
T* var = nullptr;
if (std::is_same<T, pldm_pdr_fru_record_set>::value)
{
var = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
}
else
{
var = (T*)(pdr.data());
}
if (var != nullptr)
{
return var->terminus_handle;
}
return TERMINUS_HANDLE;
}
template <typename T>
void updateContainerId(pldm_entity_association_tree* entityTree,
std::vector<uint8_t>& pdr)
{
T* t = nullptr;
if (entityTree == nullptr)
{
return;
}
if (std::is_same<T, pldm_pdr_fru_record_set>::value)
{
t = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
}
else
{
t = (T*)(pdr.data());
}
if (t == nullptr)
{
return;
}
pldm_entity entity{t->entity_type, t->entity_instance, t->container_id};
auto node = pldm_entity_association_tree_find_with_locality(entityTree,
&entity, true);
if (node)
{
pldm_entity e = pldm_entity_extract(node);
t->container_id = e.entity_container_id;
}
}
HostPDRHandler::HostPDRHandler(
int mctp_fd, uint8_t mctp_eid, sdeventplus::Event& event, pldm_pdr* repo,
const std::string& eventsJsonsDir, pldm_entity_association_tree* entityTree,
pldm_entity_association_tree* bmcEntityTree,
pldm::InstanceIdDb& instanceIdDb,
pldm::requester::Handler<pldm::requester::Request>* handler,
pldm::responder::oem_platform::Handler* oemPlatformHandler) :
mctp_fd(mctp_fd),
mctp_eid(mctp_eid), event(event), repo(repo),
stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
bmcEntityTree(bmcEntityTree), instanceIdDb(instanceIdDb), handler(handler),
oemPlatformHandler(oemPlatformHandler)
{
mergedHostParents = false;
fs::path hostFruJson(fs::path(HOST_JSONS_DIR) / fruJson);
if (fs::exists(hostFruJson))
{
// Note parent entities for entities sent down by the host firmware.
// This will enable a merge of entity associations.
try
{
std::ifstream jsonFile(hostFruJson);
auto data = Json::parse(jsonFile, nullptr, false);
if (data.is_discarded())
{
error("Parsing Host FRU json file failed");
}
else
{
auto entities = data.value("entities", emptyJsonList);
for (auto& entity : entities)
{
EntityType entityType = entity.value("entity_type", 0);
auto parent = entity.value("parent", emptyJson);
pldm_entity p{};
p.entity_type = parent.value("entity_type", 0);
p.entity_instance_num = parent.value("entity_instance", 0);
parents.emplace(entityType, std::move(p));
}
}
}
catch (const std::exception& e)
{
error("Parsing Host FRU json file failed, exception = {ERR_EXCEP}",
"ERR_EXCEP", e.what());
}
}
hostOffMatch = std::make_unique<sdbusplus::bus::match_t>(
pldm::utils::DBusHandler::getBus(),
propertiesChanged("/xyz/openbmc_project/state/host0",
"xyz.openbmc_project.State.Host"),
[this, repo, entityTree, bmcEntityTree](sdbusplus::message_t& msg) {
DbusChangedProps props{};
std::string intf;
msg.read(intf, props);
const auto itr = props.find("CurrentHostState");
if (itr != props.end())
{
PropertyValue value = itr->second;
auto propVal = std::get<std::string>(value);
if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
{
// Delete all the remote terminus information
std::erase_if(tlPDRInfo, [](const auto& item) {
auto const& [key, value] = item;
return key != TERMINUS_HANDLE;
});
pldm_pdr_remove_remote_pdrs(repo);
pldm_entity_association_tree_destroy_root(entityTree);
pldm_entity_association_tree_copy_root(bmcEntityTree,
entityTree);
this->sensorMap.clear();
this->responseReceived = false;
this->mergedHostParents = false;
}
}
});
}
void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles)
{
pdrRecordHandles.clear();
modifiedPDRRecordHandles.clear();
if (isHostPdrModified)
{
modifiedPDRRecordHandles = std::move(recordHandles);
}
else
{
pdrRecordHandles = std::move(recordHandles);
}
// Defer the actual fetch of PDRs from the host (by queuing the call on the
// main event loop). That way, we can respond to the platform event msg from
// the host firmware.
pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>(
event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this,
std::placeholders::_1));
}
void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/)
{
getHostPDR();
}
void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle)
{
pdrFetchEvent.reset();
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
PLDM_GET_PDR_REQ_BYTES);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
uint32_t recordHandle{};
if (!nextRecordHandle && (!modifiedPDRRecordHandles.empty()) &&
isHostPdrModified)
{
recordHandle = modifiedPDRRecordHandles.front();
modifiedPDRRecordHandles.pop_front();
}
else if (!nextRecordHandle && (!pdrRecordHandles.empty()))
{
recordHandle = pdrRecordHandles.front();
pdrRecordHandles.pop_front();
}
else
{
recordHandle = nextRecordHandle;
}
auto instanceId = instanceIdDb.next(mctp_eid);
auto rc = encode_get_pdr_req(instanceId, recordHandle, 0,
PLDM_GET_FIRSTPART, UINT16_MAX, 0, request,
PLDM_GET_PDR_REQ_BYTES);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(mctp_eid, instanceId);
error("Failed to encode_get_pdr_req, rc = {RC}", "RC", rc);
return;
}
rc = handler->registerRequest(
mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR,
std::move(requestMsg),
std::move(std::bind_front(&HostPDRHandler::processHostPDRs, this)));
if (rc)
{
error("Failed to send the GetPDR request to Host");
}
}
int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
pdr::EventState state)
{
auto rc = stateSensorHandler.eventAction(entry, state);
if (rc != PLDM_SUCCESS)
{
error("Failed to fetch and update D-bus property, rc = {RC}", "RC", rc);
return rc;
}
return PLDM_SUCCESS;
}
void HostPDRHandler::mergeEntityAssociations(
const std::vector<uint8_t>& pdr, [[maybe_unused]] const uint32_t& size,
[[maybe_unused]] const uint32_t& record_handle)
{
size_t numEntities{};
pldm_entity* entities = nullptr;
bool merged = false;
auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
if (oemPlatformHandler &&
oemPlatformHandler->checkRecordHandleInRange(record_handle))
{
// Adding the remote range PDRs to the repo before merging it
uint32_t handle = record_handle;
pldm_pdr_add_check(repo, pdr.data(), size, true, 0xFFFF, &handle);
}
pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
&entities);
if (numEntities > 0)
{
pldm_entity_node* pNode = nullptr;
if (!mergedHostParents)
{
pNode = pldm_entity_association_tree_find_with_locality(
entityTree, &entities[0], false);
}
else
{
pNode = pldm_entity_association_tree_find_with_locality(
entityTree, &entities[0], true);
}
if (!pNode)
{
return;
}
Entities entityAssoc;
entityAssoc.push_back(pNode);
for (size_t i = 1; i < numEntities; ++i)
{
bool isUpdateContainerId = true;
if (oemPlatformHandler)
{
isUpdateContainerId =
checkIfLogicalBitSet(entities[i].entity_container_id);
}
auto node = pldm_entity_association_tree_add_entity(
entityTree, &entities[i], entities[i].entity_instance_num,
pNode, entityPdr->association_type, true, isUpdateContainerId,
0xFFFF);
if (!node)
{
continue;
}
merged = true;
entityAssoc.push_back(node);
}
mergedHostParents = true;
if (merged)
{
entityAssociations.push_back(entityAssoc);
}
}
if (merged)
{
// Update our PDR repo with the merged entity association PDRs
pldm_entity_node* node = nullptr;
pldm_find_entity_ref_in_tree(entityTree, entities[0], &node);
if (node == nullptr)
{
error("could not find referrence of the entity in the tree");
}
else
{
int rc = 0;
if (oemPlatformHandler)
{
auto record = oemPlatformHandler->fetchLastBMCRecord(repo);
uint32_t record_handle = pldm_pdr_get_record_handle(repo,
record);
rc =
pldm_entity_association_pdr_add_from_node_with_record_handle(
node, repo, &entities, numEntities, true,
TERMINUS_HANDLE, (record_handle + 1));
}
else
{
rc = pldm_entity_association_pdr_add_from_node_check(
node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
}
if (rc)
{
error(
"Failed to add entity association PDR from node: {LIBPLDM_ERROR}",
"LIBPLDM_ERROR", rc);
}
}
}
free(entities);
}
void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
uint8_t eventDataFormat)
{
assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
// Extract from the PDR repo record handles of PDRs we want the host
// to pull up.
std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
std::vector<uint8_t> numsOfChangeEntries(1);
std::vector<std::vector<ChangeEntry>> changeEntries(
numsOfChangeEntries.size());
for (auto pdrType : pdrTypes)
{
const pldm_pdr_record* record{};
do
{
record = pldm_pdr_find_record_by_type(repo, pdrType, record,
nullptr, nullptr);
if (record && pldm_pdr_record_is_remote(record))
{
changeEntries[0].push_back(
pldm_pdr_get_record_handle(repo, record));
}
} while (record);
}
if (changeEntries.empty())
{
return;
}
numsOfChangeEntries[0] = changeEntries[0].size();
// Encode PLDM platform event msg to indicate a PDR repo change.
size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
changeEntries[0].size() * sizeof(uint32_t);
std::vector<uint8_t> eventDataVec{};
eventDataVec.resize(maxSize);
auto eventData =
reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
eventDataVec.data());
size_t actualSize{};
auto firstEntry = changeEntries[0].data();
auto rc = encode_pldm_pdr_repository_chg_event_data(
eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
&firstEntry, eventData, &actualSize, maxSize);
if (rc != PLDM_SUCCESS)
{
error("Failed to encode_pldm_pdr_repository_chg_event_data, rc = {RC}",
"RC", rc);
return;
}
auto instanceId = instanceIdDb.next(mctp_eid);
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
actualSize);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
rc = encode_platform_event_message_req(
instanceId, 1, TERMINUS_ID, PLDM_PDR_REPOSITORY_CHG_EVENT,
eventDataVec.data(), actualSize, request,
actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(mctp_eid, instanceId);
error("Failed to encode_platform_event_message_req, rc = {RC}", "RC",
rc);
return;
}
auto platformEventMessageResponseHandler =
[](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
if (response == nullptr || !respMsgLen)
{
error(
"Failed to receive response for the PDR repository changed event");
return;
}
uint8_t completionCode{};
uint8_t status{};
auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
auto rc = decode_platform_event_message_resp(responsePtr, respMsgLen,
&completionCode, &status);
if (rc || completionCode)
{
error(
"Failed to decode_platform_event_message_resp: {RC}, cc = {CC}",
"RC", rc, "CC", static_cast<unsigned>(completionCode));
}
};
rc = handler->registerRequest(
mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE,
std::move(requestMsg), std::move(platformEventMessageResponseHandler));
if (rc)
{
error("Failed to send the PDR repository changed event request");
}
}
void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs)
{
for (const auto& pdr : stateSensorPDRs)
{
SensorEntry sensorEntry{};
const auto& [terminusHandle, sensorID, sensorInfo] =
responder::pdr_utils::parseStateSensorPDR(pdr);
sensorEntry.sensorID = sensorID;
try
{
sensorEntry.terminusID = std::get<0>(tlPDRInfo.at(terminusHandle));
}
// If there is no mapping for terminusHandle assign the reserved TID
// value of 0xFF to indicate that.
catch (const std::out_of_range&)
{
sensorEntry.terminusID = PLDM_TID_RESERVED;
}
sensorMap.emplace(sensorEntry, std::move(sensorInfo));
}
}
void HostPDRHandler::processHostPDRs(mctp_eid_t /*eid*/,
const pldm_msg* response,
size_t respMsgLen)
{
static bool merged = false;
static PDRList stateSensorPDRs{};
static PDRList fruRecordSetPDRs{};
uint32_t nextRecordHandle{};
uint8_t tlEid = 0;
bool tlValid = true;
uint32_t rh = 0;
uint16_t terminusHandle = 0;
uint16_t pdrTerminusHandle = 0;
uint8_t tid = 0;
uint8_t completionCode{};
uint32_t nextDataTransferHandle{};
uint8_t transferFlag{};
uint16_t respCount{};
uint8_t transferCRC{};
if (response == nullptr || !respMsgLen)
{
error("Failed to receive response for the GetPDR command");
return;
}
auto rc = decode_get_pdr_resp(
response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
&nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
nullptr, 0, &transferCRC);
std::vector<uint8_t> responsePDRMsg;
responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
if (rc != PLDM_SUCCESS)
{
error("Failed to decode_get_pdr_resp, rc = {RC}", "RC", rc);
return;
}
else
{
std::vector<uint8_t> pdr(respCount, 0);
rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
&nextRecordHandle, &nextDataTransferHandle,
&transferFlag, &respCount, pdr.data(),
respCount, &transferCRC);
if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
{
error("Failed to decode_get_pdr_resp: rc = {RC}, cc = {CC}", "RC",
rc, "CC", static_cast<unsigned>(completionCode));
return;
}
else
{
// when nextRecordHandle is 0, we need the recordHandle of the last
// PDR and not 0-1.
if (!nextRecordHandle)
{
rh = nextRecordHandle;
}
else
{
rh = nextRecordHandle - 1;
}
auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
if (!rh)
{
rh = pdrHdr->record_handle;
}
if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
{
this->mergeEntityAssociations(pdr, respCount, rh);
merged = true;
}
else
{
if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
{
pdrTerminusHandle =
extractTerminusHandle<pldm_terminus_locator_pdr>(pdr);
auto tlpdr =
reinterpret_cast<const pldm_terminus_locator_pdr*>(
pdr.data());
terminusHandle = tlpdr->terminus_handle;
tid = tlpdr->tid;
auto terminus_locator_type = tlpdr->terminus_locator_type;
if (terminus_locator_type ==
PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
{
auto locatorValue = reinterpret_cast<
const pldm_terminus_locator_type_mctp_eid*>(
tlpdr->terminus_locator_value);
tlEid = static_cast<uint8_t>(locatorValue->eid);
}
if (tlpdr->validity == 0)
{
tlValid = false;
}
for (const auto& terminusMap : tlPDRInfo)
{
if ((terminusHandle == (terminusMap.first)) &&
(get<1>(terminusMap.second) == tlEid) &&
(get<2>(terminusMap.second) == tlpdr->validity))
{
// TL PDR already present with same validity don't
// add the PDR to the repo just return
return;
}
}
tlPDRInfo.insert_or_assign(
tlpdr->terminus_handle,
std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
}
else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
{
pdrTerminusHandle =
extractTerminusHandle<pldm_state_sensor_pdr>(pdr);
updateContainerId<pldm_state_sensor_pdr>(entityTree, pdr);
stateSensorPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_PDR_FRU_RECORD_SET)
{
pdrTerminusHandle =
extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
updateContainerId<pldm_pdr_fru_record_set>(entityTree, pdr);
fruRecordSetPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
{
pdrTerminusHandle =
extractTerminusHandle<pldm_state_effecter_pdr>(pdr);
updateContainerId<pldm_state_effecter_pdr>(entityTree, pdr);
}
else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
{
pdrTerminusHandle =
extractTerminusHandle<pldm_numeric_effecter_value_pdr>(
pdr);
updateContainerId<pldm_numeric_effecter_value_pdr>(
entityTree, pdr);
}
// if the TLPDR is invalid update the repo accordingly
if (!tlValid)
{
pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
tlValid);
if (!isHostUp())
{
// The terminus PDR becomes invalid when the terminus
// itself is down. We don't need to do PDR exchange in
// that case, so setting the next record handle to 0.
nextRecordHandle = 0;
}
}
else
{
rc = pldm_pdr_add_check(repo, pdr.data(), respCount, true,
pdrTerminusHandle, &rh);
if (rc)
{
// pldm_pdr_add() assert()ed on failure to add a PDR.
throw std::runtime_error("Failed to add PDR");
}
}
}
}
}
if (!nextRecordHandle)
{
updateEntityAssociation(entityAssociations, entityTree, objPathMap);
/*received last record*/
this->parseStateSensorPDRs(stateSensorPDRs);
this->createDbusObjects(fruRecordSetPDRs);
if (isHostUp())
{
this->setHostSensorState(stateSensorPDRs);
}
stateSensorPDRs.clear();
fruRecordSetPDRs.clear();
entityAssociations.clear();
if (merged)
{
merged = false;
deferredPDRRepoChgEvent =
std::make_unique<sdeventplus::source::Defer>(
event,
std::bind(
std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
this, std::placeholders::_1));
}
}
else
{
if (modifiedPDRRecordHandles.empty() && isHostPdrModified)
{
isHostPdrModified = false;
}
else
{
deferredFetchPDREvent =
std::make_unique<sdeventplus::source::Defer>(
event,
std::bind(
std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
this, nextRecordHandle, std::placeholders::_1));
}
}
}
void HostPDRHandler::_processPDRRepoChgEvent(
sdeventplus::source::EventBase& /*source */)
{
deferredPDRRepoChgEvent.reset();
this->sendPDRRepositoryChgEvent(
std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)),
FORMAT_IS_PDR_HANDLES);
}
void HostPDRHandler::_processFetchPDREvent(
uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
{
deferredFetchPDREvent.reset();
if (!this->pdrRecordHandles.empty())
{
nextRecordHandle = this->pdrRecordHandles.front();
this->pdrRecordHandles.pop_front();
}
if (isHostPdrModified && (!this->modifiedPDRRecordHandles.empty()))
{
nextRecordHandle = this->modifiedPDRRecordHandles.front();
this->modifiedPDRRecordHandles.pop_front();
}
this->getHostPDR(nextRecordHandle);
}
void HostPDRHandler::setHostFirmwareCondition()
{
responseReceived = false;
auto instanceId = instanceIdDb.next(mctp_eid);
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
PLDM_GET_VERSION_REQ_BYTES);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto rc = encode_get_version_req(instanceId, 0, PLDM_GET_FIRSTPART,
PLDM_BASE, request);
if (rc != PLDM_SUCCESS)
{
error("GetPLDMVersion encode failure. PLDM error code = {RC}", "RC",
lg2::hex, rc);
instanceIdDb.free(mctp_eid, instanceId);
return;
}
auto getPLDMVersionHandler = [this](mctp_eid_t /*eid*/,
const pldm_msg* response,
size_t respMsgLen) {
if (response == nullptr || !respMsgLen)
{
error(
"Failed to receive response for getPLDMVersion command, Host seems to be off");
return;
}
info("Getting the response. PLDM RC = {RC}", "RC", lg2::hex,
static_cast<uint16_t>(response->payload[0]));
this->responseReceived = true;
getHostPDR();
};
rc = handler->registerRequest(mctp_eid, instanceId, PLDM_BASE,
PLDM_GET_PLDM_VERSION, std::move(requestMsg),
std::move(getPLDMVersionHandler));
if (rc)
{
error("Failed to discover Host state. Assuming Host as off");
}
}
bool HostPDRHandler::isHostUp()
{
return responseReceived;
}
void HostPDRHandler::setHostSensorState(const PDRList& stateSensorPDRs)
{
for (const auto& stateSensorPDR : stateSensorPDRs)
{
auto pdr = reinterpret_cast<const pldm_state_sensor_pdr*>(
stateSensorPDR.data());
if (!pdr)
{
error("Failed to get State sensor PDR");
pldm::utils::reportError(
"xyz.openbmc_project.PLDM.Error.SetHostSensorState.GetStateSensorPDRFail");
return;
}
uint16_t sensorId = pdr->sensor_id;
for (const auto& [terminusHandle, terminusInfo] : tlPDRInfo)
{
if (terminusHandle == pdr->terminus_handle)
{
if (std::get<2>(terminusInfo) == PLDM_TL_PDR_VALID)
{
mctp_eid = std::get<1>(terminusInfo);
}
bitfield8_t sensorRearm;
sensorRearm.byte = 0;
uint8_t tid = std::get<0>(terminusInfo);
auto instanceId = instanceIdDb.next(mctp_eid);
std::vector<uint8_t> requestMsg(
sizeof(pldm_msg_hdr) +
PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto rc = encode_get_state_sensor_readings_req(
instanceId, sensorId, sensorRearm, 0, request);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(mctp_eid, instanceId);
error(
"Failed to encode_get_state_sensor_readings_req, rc = {RC}",
"RC", rc);
pldm::utils::reportError(
"xyz.openbmc_project.PLDM.Error.SetHostSensorState.EncodeStateSensorFail");
return;
}
auto getStateSensorReadingRespHandler =
[=, this](mctp_eid_t /*eid*/, const pldm_msg* response,
size_t respMsgLen) {
if (response == nullptr || !respMsgLen)
{
error(
"Failed to receive response for getStateSensorReading command");
return;
}
std::array<get_sensor_state_field, 8> stateField{};
uint8_t completionCode = 0;
uint8_t comp_sensor_count = 0;
auto rc = decode_get_state_sensor_readings_resp(
response, respMsgLen, &completionCode,
&comp_sensor_count, stateField.data());
if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
{
error(
"Failed to decode_get_state_sensor_readings_resp, rc = {RC} cc = {CC}",
"RC", rc, "CC",
static_cast<unsigned>(completionCode));
}
uint8_t eventState;
uint8_t previousEventState;
for (uint8_t sensorOffset = 0;
sensorOffset < comp_sensor_count; sensorOffset++)
{
eventState = stateField[sensorOffset].present_state;
previousEventState =
stateField[sensorOffset].previous_state;
emitStateSensorEventSignal(tid, sensorId, sensorOffset,
eventState,
previousEventState);
SensorEntry sensorEntry{tid, sensorId};
pldm::pdr::EntityInfo entityInfo{};
pldm::pdr::CompositeSensorStates
compositeSensorStates{};
std::vector<pldm::pdr::StateSetId> stateSetIds{};
try
{
std::tie(entityInfo, compositeSensorStates,
stateSetIds) =
lookupSensorInfo(sensorEntry);
}
catch (const std::out_of_range&)
{
try
{
sensorEntry.terminusID = PLDM_TID_RESERVED;
std::tie(entityInfo, compositeSensorStates,
stateSetIds) =
lookupSensorInfo(sensorEntry);
}
catch (const std::out_of_range&)
{
error("No mapping for the events");
}
}
if (sensorOffset > compositeSensorStates.size())
{
error("Error Invalid data, Invalid sensor offset");
return;
}
const auto& possibleStates =
compositeSensorStates[sensorOffset];
if (possibleStates.find(eventState) ==
possibleStates.end())
{
error("Error invalid_data, Invalid event state");
return;
}
const auto& [containerId, entityType,
entityInstance] = entityInfo;
auto stateSetId = stateSetIds[sensorOffset];
pldm::responder::events::StateSensorEntry
stateSensorEntry{containerId, entityType,
entityInstance, sensorOffset,
stateSetId};
handleStateSensorEvent(stateSensorEntry, eventState);
}
};
rc = handler->registerRequest(
mctp_eid, instanceId, PLDM_PLATFORM,
PLDM_GET_STATE_SENSOR_READINGS, std::move(requestMsg),
std::move(getStateSensorReadingRespHandler));
if (rc != PLDM_SUCCESS)
{
error(
"Failed to send request to get State sensor reading on Host");
}
}
}
}
}
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, &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 == entity.entity_instance_num &&
fruPdr->container_id == entity.entity_container_id)
{
return fruPdr->fru_rsi;
}
}
return std::nullopt;
}
void HostPDRHandler::setFRUDataOnDBus(
[[maybe_unused]] const PDRList& fruRecordSetPDRs,
[[maybe_unused]] const std::vector<
responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
{
#ifdef OEM_IBM
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));
}
}
}
}
}
#endif
}
void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
{
// TODO: Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
}
} // namespace pldm