blob: b7036184f458200b23b5e99d822b59ddb02ed9c9 [file] [log] [blame]
#include "pldm.hpp"
#include "file.hpp"
#include <fmt/core.h>
#include <libpldm/entity.h>
#include <libpldm/platform.h>
#include <libpldm/state_set.h>
#include <libpldm/state_set_oem_ibm.h>
#include <phosphor-logging/log.hpp>
namespace pldm
{
using namespace phosphor::logging;
void Interface::fetchSensorInfo(uint16_t stateSetId,
SensorToInstance& sensorInstanceMap,
SensorOffset& sensorOffset)
{
PdrList pdrs{};
auto& bus = open_power::occ::utils::getBus();
try
{
auto method = bus.new_method_call(
"xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
"xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
method.append(tid, (uint16_t)PLDM_ENTITY_PROC, stateSetId);
auto responseMsg = bus.call(method);
responseMsg.read(pdrs);
}
catch (const sdbusplus::exception::exception& e)
{
log<level::ERR>("pldm: Failed to fetch the state sensor PDRs",
entry("ERROR=%s", e.what()));
}
if (pdrs.empty())
{
log<level::ERR>("pldm: state sensor PDRs not present");
return;
}
bool offsetFound = false;
auto stateSensorPDR =
reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
auto possibleStatesPtr = stateSensorPDR->possible_states;
for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
offset++)
{
auto possibleStates =
reinterpret_cast<const state_sensor_possible_states*>(
possibleStatesPtr);
if (possibleStates->state_set_id == stateSetId)
{
sensorOffset = offset;
offsetFound = true;
break;
}
possibleStatesPtr += sizeof(possibleStates->state_set_id) +
sizeof(possibleStates->possible_states_size) +
possibleStates->possible_states_size;
}
if (!offsetFound)
{
log<level::ERR>("pldm: state sensor PDR not found");
return;
}
// To order SensorID based on the EntityInstance.
// Note that when a proc is on a DCM, the PDRs for these sensors
// could have the same instance IDs but different container IDs.
std::map<uint32_t, SensorID> entityInstMap{};
for (auto& pdr : pdrs)
{
auto pdrPtr =
reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
uint32_t key = (static_cast<uint32_t>(pdrPtr->container_id) << 16) |
static_cast<uint32_t>(pdrPtr->entity_instance);
entityInstMap.emplace(key, static_cast<SensorID>(pdrPtr->sensor_id));
}
open_power::occ::instanceID count = start;
for (auto const& pair : entityInstMap)
{
sensorInstanceMap.emplace(pair.second, count);
count++;
}
}
void Interface::sensorEvent(sdbusplus::message::message& msg)
{
if (!isOCCSensorCacheValid())
{
fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
sensorToOCCInstance, OCCSensorOffset);
}
if (sensorToSBEInstance.empty())
{
fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSBEInstance,
SBESensorOffset);
}
TerminusID tid{};
SensorID sensorId{};
SensorOffset msgSensorOffset{};
EventState eventState{};
EventState previousEventState{};
msg.read(tid, sensorId, msgSensorOffset, eventState, previousEventState);
if (msgSensorOffset == OCCSensorOffset)
{
auto sensorEntry = sensorToOCCInstance.find(sensorId);
if (sensorEntry != sensorToOCCInstance.end())
{
if (eventState ==
static_cast<EventState>(
PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))
{
log<level::INFO>(
fmt::format("PLDM: OCC{} is RUNNING", sensorEntry->second)
.c_str());
callBack(sensorEntry->second, true);
}
else if (eventState ==
static_cast<EventState>(
PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))
{
log<level::INFO>(fmt::format("PLDM: OCC{} has now STOPPED",
sensorEntry->second)
.c_str());
callBack(sensorEntry->second, false);
}
return;
}
}
if (msgSensorOffset == SBESensorOffset)
{
auto sensorEntry = sensorToSBEInstance.find(sensorId);
if (sensorEntry != sensorToSBEInstance.end())
{
if (eventState == static_cast<EventState>(SBE_HRESET_NOT_READY))
{
log<level::INFO>("pldm: HRESET is NOT READY",
entry("SBE=%d", sensorEntry->second));
}
else if (eventState == static_cast<EventState>(SBE_HRESET_READY))
{
sbeCallBack(sensorEntry->second, true);
}
else if (eventState == static_cast<EventState>(SBE_HRESET_FAILED))
{
sbeCallBack(sensorEntry->second, false);
}
}
}
}
void Interface::hostStateEvent(sdbusplus::message::message& msg)
{
std::map<std::string, std::variant<std::string>> properties{};
std::string interface;
msg.read(interface, properties);
const auto stateEntry = properties.find("CurrentHostState");
if (stateEntry != properties.end())
{
auto stateEntryValue = stateEntry->second;
auto propVal = std::get<std::string>(stateEntryValue);
if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
{
sensorToOCCInstance.clear();
occInstanceToEffecter.clear();
sensorToSBEInstance.clear();
sbeInstanceToEffecter.clear();
}
}
}
void Interface::fetchEffecterInfo(uint16_t stateSetId,
InstanceToEffecter& instanceToEffecterMap,
CompositeEffecterCount& effecterCount,
uint8_t& stateIdPos)
{
PdrList pdrs{};
auto& bus = open_power::occ::utils::getBus();
try
{
auto method = bus.new_method_call(
"xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
"xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
method.append(tid, (uint16_t)PLDM_ENTITY_PROC, stateSetId);
auto responseMsg = bus.call(method);
responseMsg.read(pdrs);
}
catch (const sdbusplus::exception::exception& e)
{
log<level::ERR>("pldm: Failed to fetch the state effecter PDRs",
entry("ERROR=%s", e.what()));
}
if (!pdrs.size())
{
log<level::ERR>("pldm: state effecter PDRs not present");
return;
}
bool offsetFound = false;
auto stateEffecterPDR =
reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
auto possibleStatesPtr = stateEffecterPDR->possible_states;
for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
offset++)
{
auto possibleStates =
reinterpret_cast<const state_effecter_possible_states*>(
possibleStatesPtr);
if (possibleStates->state_set_id == stateSetId)
{
stateIdPos = offset;
effecterCount = stateEffecterPDR->composite_effecter_count;
offsetFound = true;
break;
}
possibleStatesPtr += sizeof(possibleStates->state_set_id) +
sizeof(possibleStates->possible_states_size) +
possibleStates->possible_states_size;
}
if (!offsetFound)
{
return;
}
std::map<uint32_t, EffecterID> entityInstMap{};
for (auto& pdr : pdrs)
{
auto pdrPtr =
reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
uint32_t key = (static_cast<uint32_t>(pdrPtr->container_id) << 16) |
static_cast<uint32_t>(pdrPtr->entity_instance);
entityInstMap.emplace(key, static_cast<SensorID>(pdrPtr->effecter_id));
}
open_power::occ::instanceID position = start;
for (auto const& pair : entityInstMap)
{
instanceToEffecterMap.emplace(position, pair.second);
position++;
}
}
std::vector<uint8_t>
Interface::prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,
CompositeEffecterCount effecterCount,
uint8_t stateIdPos, uint8_t stateSetValue)
{
std::vector<uint8_t> request(
sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
(effecterCount * sizeof(set_effecter_state_field)));
auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
std::vector<set_effecter_state_field> stateField;
for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
{
if (effecterPos == stateIdPos)
{
stateField.emplace_back(
set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
}
else
{
stateField.emplace_back(
set_effecter_state_field{PLDM_NO_CHANGE, 0});
}
}
auto rc = encode_set_state_effecter_states_req(
instanceId, effecterId, effecterCount, stateField.data(), requestMsg);
if (rc != PLDM_SUCCESS)
{
log<level::ERR>("encode set effecter states request returned error ",
entry("RC=%d", rc));
request.clear();
}
return request;
}
void Interface::resetOCC(open_power::occ::instanceID occInstanceId)
{
if (!isPDREffecterCacheValid())
{
fetchEffecterInfo(PLDM_STATE_SET_BOOT_RESTART_CAUSE,
occInstanceToEffecter, OCCEffecterCount,
bootRestartPosition);
}
// Find the matching effecter for the OCC instance
auto effecterEntry = occInstanceToEffecter.find(occInstanceId);
if (effecterEntry == occInstanceToEffecter.end())
{
log<level::ERR>(
fmt::format(
"pldm: Failed to find a matching effecter for OCC instance {}",
occInstanceId)
.c_str());
return;
}
uint8_t instanceId{};
if (!getMctpInstanceId(instanceId))
{
return;
}
// Prepare the SetStateEffecterStates request to reset the OCC
auto request = prepareSetEffecterReq(
instanceId, effecterEntry->second, OCCEffecterCount,
bootRestartPosition, PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET);
if (request.empty())
{
log<level::ERR>("pldm: SetStateEffecterStates OCC reset request empty");
return;
}
// Make asynchronous call to reset the OCCs/PM Complex
sendPldm(request, true);
}
void Interface::sendHRESET(open_power::occ::instanceID sbeInstanceId)
{
if (sbeInstanceToEffecter.empty())
{
fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
sbeInstanceToEffecter, SBEEffecterCount,
sbeMaintenanceStatePosition);
}
auto effecterEntry = sbeInstanceToEffecter.find(sbeInstanceId);
if (effecterEntry == sbeInstanceToEffecter.end())
{
log<level::ERR>(
"pldm: Failed to find a matching effecter for SBE instance",
entry("SBE=%d", sbeInstanceId));
return;
}
uint8_t instanceId{};
if (!getMctpInstanceId(instanceId))
{
return;
}
// Prepare the SetStateEffecterStates request to HRESET the SBE
auto request = prepareSetEffecterReq(
instanceId, effecterEntry->second, SBEEffecterCount,
sbeMaintenanceStatePosition, SBE_RETRY_REQUIRED);
if (request.empty())
{
log<level::ERR>("pldm: SetStateEffecterStates HRESET request empty");
return;
}
// Make asynchronous call to do the reset
sendPldm(request, true);
}
bool Interface::getMctpInstanceId(uint8_t& instanceId)
{
auto& bus = open_power::occ::utils::getBus();
try
{
auto method = bus.new_method_call(
"xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
"xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
method.append(mctpEid);
auto reply = bus.call(method);
reply.read(instanceId);
}
catch (const sdbusplus::exception::exception& e)
{
log<level::ERR>("pldm: GetInstanceId returned error",
entry("ERROR=%s", e.what()));
return false;
}
return true;
}
void Interface::sendPldm(const std::vector<uint8_t>& request, const bool async)
{
// Connect to MCTP scoket
int fd = pldm_open();
if (fd == -1)
{
log<level::ERR>(
fmt::format("sendPldm: Failed to connect to MCTP socket, errno={}",
errno)
.c_str());
return;
}
open_power::occ::FileDescriptor fileFd(fd);
// Send the PLDM request message to HBRT
if (async == false)
{
uint8_t* response = nullptr;
size_t responseSize{};
auto rc = pldm_send_recv(mctpEid, fileFd(), request.data(),
request.size(), &response, &responseSize);
std::unique_ptr<uint8_t, decltype(std::free)*> responsePtr{response,
std::free};
if (rc)
{
log<level::ERR>(
fmt::format(
"sendPldm: pldm_send_recv({},{},req,{},...) failed with rc={} and errno={}",
mctpEid, fileFd(), request.size(), rc, errno)
.c_str());
}
uint8_t completionCode{};
auto responseMsg = reinterpret_cast<const pldm_msg*>(responsePtr.get());
auto rcDecode = decode_set_state_effecter_states_resp(
responseMsg, responseSize - sizeof(pldm_msg_hdr), &completionCode);
if (rcDecode || completionCode)
{
log<level::ERR>(
fmt::format(
"sendPldm: decode_set_state_effecter_states_resp failed with rc={} and compCode={}",
rcDecode, completionCode)
.c_str());
}
}
else
{
log<level::INFO>(fmt::format("sendPldm: calling pldm_send({}, {})",
mctpEid, fileFd())
.c_str());
auto rc = pldm_send(mctpEid, fileFd(), request.data(), request.size());
if (rc)
{
log<level::ERR>(
fmt::format(
"sendPldm: pldm_send({},{},req,{}) failed with rc={} and errno={}",
mctpEid, fileFd(), request.size(), rc, errno)
.c_str());
}
}
}
} // namespace pldm