blob: f5f341fb4539069c89cbee821216834a7ea7ed99 [file] [log] [blame]
#include "config.h"
#include "terminus_handler.hpp"
#include <sdeventplus/source/time.hpp>
#include <chrono>
#include <filesystem>
#include "common/flight_recorder.hpp"
namespace pldm
{
namespace terminus
{
using namespace pldm::sensor;
using EpochTimeUS = uint64_t;
std::string fruPath = "/xyz/openbmc_project/pldm/fru";
static constexpr uint16_t MCControlEffecterID = 254;
std::string exec(const char *cmd)
{
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
std::cerr << "popen() failed!" << std::endl;
return "failed";
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
TerminusHandler::TerminusHandler(
uint8_t eid, sdeventplus::Event& event, sdbusplus::bus::bus& bus,
pldm::requester::Handler<pldm::requester::Request>* handler,
pldm::InstanceIdDb& instanceIdDb) :
eid(eid), bus(bus), event(event), handler(handler),
instanceIdDb(instanceIdDb), _state(),
_timer(event, std::bind(&TerminusHandler::pollSensors, this)),
_timer2(event, std::bind(&TerminusHandler::readSensor, this)),
_timer3(event,
std::bind(&TerminusHandler::waitForRASPollingFinished, this)),
_timer4(event, std::bind(&TerminusHandler::waitForMProRecovery, this))
{}
TerminusHandler::~TerminusHandler()
{
continuePollSensor = false;
this->frus.clear();
this->compNumSensorPDRs.clear();
this->effecterAuxNamePDRs.clear();
this->effecterPDRs.clear();
this->entityAssociationPDRs.clear();
this->_state.clear();
this->_sensorObjects.clear();
this->_effecterLists.clear();
this->eventDataHndl.reset();
this->_auxNameMaps.clear();
}
void TerminusHandler::loadAuxNameMapping()
{
auxNameOverride.clear();
try
{
utils::GetSubTreeResponse response = utils::DBusHandler().getSubtree(
chassisInventoryPath, 0,
{"xyz.openbmc_project.Configuration.SensorAuxName"});
for (const auto& [path, serviceMap] : response)
{
/* Use terminus inventory path as prefix to match its SensorAuxName config:
inventoryPath : chassis1/terminusA
SensorAuxName path : chassis1/terminusA_AuxName_Temp
SensorAuxName path : chassis1/terminusA_AuxName_HealthState
inventoryPath : chassis1/terminusB
SensorAuxName path : chassis1/terminusB_AuxName_Temp
SensorAuxName path : chassis1/terminusB_AuxName_HealthState
*/
if (!path.starts_with(inventoryPath))
{
continue;
}
uint64_t sensorId = utils::DBusHandler().getDbusProperty<uint64_t>(
path.c_str(), "SensorId",
"xyz.openbmc_project.Configuration.SensorAuxName");
std::vector<std::string> auxNames =
utils::DBusHandler().getDbusProperty<std::vector<std::string>>(
path.c_str(), "AuxNames",
"xyz.openbmc_project.Configuration.SensorAuxName");
auxNameOverride[sensorId] = auxNames;
}
}
catch (const std::exception& e)
{
std::cerr << std::format(
"Warning: failed to load SensorAuxName mappping for {}, e.what(): {}\n",
chassisInventoryPath, e.what());
}
}
requester::Coroutine TerminusHandler::discoveryTerminus()
{
std::cerr << "Discovery Terminus: " << unsigned(eid) << std::endl;
/*
* 1. Initialize PLDM if PLDM Type is supported
*
* 1.1 Get supported PLDM Types
*
* 1.2. If PLDM for BIOS control and configuration is supported
* 1.2.1 Set the date and time using the SetDateTime command
*
* 1.3. If PLDM Base Type is supported, get PLDM Base commands
* 1.3.1 Get TID
* 1.3.2 Get PLDM Commands
*
* 1.4. If FRU Type is supported, issue these FRU commands
* 1.4.1 Get FRU Meta data via GetFRURecordTableMetadata
* 1.4.2 Get FRU Table data via GetFRURecordTable
*
* 1.5. If PLDM Platform Type is supported, get PLDM Platform commands
* 1.5.1 Prepare to receive event notification SetEventReceiver
* 1.5.2 Get all Sensor/Effecter/Association information via GetPDR command
*
*/
auto rc = co_await getPLDMTypes();
if (rc)
{
std::cerr << "Failed to getPLDMTypes, rc=" << unsigned(rc) << std::endl;
removeMCTPEndpoint();
co_return rc;
}
/* Received the response, terminus is on */
this->responseReceived = true;
if (supportPLDMType(PLDM_BASE))
{
rc = co_await getPLDMCommands();
if (rc)
{
std::cerr << "Failed to getPLDMCommands, rc=" << unsigned(rc)
<< std::endl;
}
}
if (supportPLDMType(PLDM_BASE))
{
rc = co_await getTID();
if (rc)
{
std::cerr << "Failed to getTID, rc=" << unsigned(rc) << std::endl;
removeMCTPEndpoint();
co_return PLDM_ERROR;
}
devInfo.eid = eid;
if(devInfo.tid != eid)
{
rc = co_await setTID(eid);
if (rc)
{
removeMCTPEndpoint();
co_return PLDM_ERROR;
}
}
}
if (supportPLDMType(PLDM_BIOS))
{
rc = co_await setDateTime();
if (rc)
{
std::cerr << "Failed to setDateTime, rc=" << unsigned(rc)
<< std::endl;
}
}
uint16_t totalTableRecords = 0;
if (supportPLDMType(PLDM_FRU))
{
rc = co_await getFRURecordTableMetadata(&totalTableRecords);
if (rc)
{
std::cerr << "Failed to getFRURecordTableMetadata, "
<< "rc=" << unsigned(rc) << std::endl;
}
if (!totalTableRecords)
{
std::cerr << "Number of record table is not correct." << std::endl;
}
}
if ((totalTableRecords != 0) && supportPLDMType(PLDM_FRU))
{
rc = co_await getFRURecordTable(totalTableRecords);
if (rc)
{
std::cerr << "Failed to getFRURecordTable, "
<< "rc=" << unsigned(rc) << std::endl;
}
}
/* Check whether the terminus is removed when discoverying */
if (stopTerminusPolling)
{
co_return PLDM_SUCCESS;
}
if (supportPLDMType(PLDM_PLATFORM))
{
if (debugGetPDR)
{
startTime = std::chrono::system_clock::now();
std::cerr << eidToName.second << " Start GetPDR at "
<< getCurrentSystemTime() << std::endl;
}
rc = co_await getDevPDR(0);
if (rc)
{
std::cerr << "Failed to getDevPDR, rc=" << unsigned(rc)
<< std::endl;
removeMCTPEndpoint();
}
else
{
readCount = 0;
if (debugGetPDR)
{
std::chrono::duration<double> elapsed_seconds =
std::chrono::system_clock::now() - startTime;
std::cerr << eidToName.second << " Finish get all PDR "
<< elapsed_seconds.count() << "s at "
<< getCurrentSystemTime() << std::endl;
}
if (entityAssociationPDRs.size() > 0)
{
createEntityAssociationMap(entityAssociationPDRs);
createNicAndPortInventory();
}
if (this->numericSensorPDRs.size() > 0)
{
this->createNumericSensorIntf(this->numericSensorPDRs);
}
if (this->stateSensorPDRs.size() > 0)
{
this->createStateSensorIntf(this->stateSensorPDRs);
}
if (this->compNumSensorPDRs.size() > 0)
{
this->createCompactNummericSensorIntf(this->compNumSensorPDRs);
}
if (this->effecterAuxNamePDRs.size() > 0)
{
this->parseAuxNamePDRs(this->effecterAuxNamePDRs);
}
if (this->effecterPDRs.size() > 0)
{
this->createNummericEffecterDBusIntf(this->effecterPDRs);
}
if (_state.size() > 0)
{
createdDbusObject = true;
}
updateSensorKeys();
}
}
#ifdef AMPERE
if (supportPLDMType(PLDM_PLATFORM))
{
rc = co_await setEventReceiver();
if (rc)
{
std::cerr << "Failed to setEventReceiver, rc=" << unsigned(rc)
<< std::endl;
}
}
/* Start RAS */
eventDataHndl = std::make_shared<PldmMessagePollEvent>(eid, event, bus,
instanceIdDb, handler);
#endif
co_return PLDM_SUCCESS;
}
bool TerminusHandler::supportPLDMType(const uint8_t type)
{
if (devInfo.supportedTypes[type / 8].byte & (1 << (type % 8)))
{
return true;
}
return false;
}
bool TerminusHandler::supportPLDMCommand(const uint8_t type,
const uint8_t command)
{
if (!supportPLDMType(type))
{
return false;
}
if (devInfo.supportedCmds[type].cmdTypes[command / 8].byte &
(1 << (command % 8)))
{
return true;
}
return false;
}
std::string TerminusHandler::getCurrentSystemTime()
{
auto currentTime = std::chrono::system_clock::now();
char buffer[80];
char buffer1[4];
auto transformed = currentTime.time_since_epoch().count() / 1000000;
auto millis = transformed % 1000;
std::time_t tt;
tt = std::chrono::system_clock::to_time_t(currentTime);
auto timeinfo = localtime(&tt);
strftime(buffer, 80, "%F %H:%M:%S", timeinfo);
snprintf(buffer1, 4, "%03d", (int)millis);
return std::string(buffer) + ":" + std::string(buffer1);
}
requester::Coroutine TerminusHandler::getPLDMTypes()
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " get the PLDM Types." << std::endl;
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto instanceId = instanceIdDb.next(eid);
auto rc = encode_get_types_req(instanceId, request);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to encode_get_types_req, rc = " << unsigned(rc)
<< std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_GET_PLDM_TYPES)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << std::endl;
co_return rc;
}
std::vector<bitfield8_t> types(8);
rc = decode_get_types_resp(response, respMsgLen, &cc, types.data());
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_get_types_resp, Message Error: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc)
<< std::endl;
for (int i = 0; i < 8; i++)
{
devInfo.supportedTypes[i].byte = 0;
}
co_return rc;
}
else
{
for (int i = 0; i < 8; i++)
{
devInfo.supportedTypes[i] = types[i];
}
}
co_return cc;
}
requester::Coroutine TerminusHandler::getPLDMCommands()
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " get the supported PLDM Types." << std::endl;
uint8_t type = PLDM_BASE;
while ((type < PLDM_MAX_TYPES) && supportPLDMType(type))
{
auto rc = co_await getPLDMCommand(type);
if (rc)
{
std::cerr << "Failed to getPLDMCommand, Type=" << unsigned(type)
<< " rc =" << unsigned(rc) << std::endl;
}
type++;
}
co_return PLDM_SUCCESS;
}
requester::Coroutine TerminusHandler::getPLDMCommand(const uint8_t& pldmTypeIdx)
{
auto instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
ver32_t version{0xFF, 0xFF, 0xFF, 0xFF};
auto rc =
encode_get_commands_req(instanceId, pldmTypeIdx, version, request);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to encode_get_commands_req, rc = " << unsigned(rc)
<< std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_GET_PLDM_COMMANDS)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
/* Process response */
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_GET_PLDM_COMMANDS)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
std::vector<bitfield8_t> cmdTypes(32);
rc = decode_get_commands_resp(response, respMsgLen, &cc, cmdTypes.data());
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Response Message Error: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc)
<< std::endl;
for (auto i = 0; i < 32; i++)
{
devInfo.supportedCmds[pldmTypeIdx].cmdTypes[i].byte = 0;
}
co_return rc;
}
uint8_t i = 0;
for (const auto& cmd : cmdTypes)
{
devInfo.supportedCmds[pldmTypeIdx].cmdTypes[i++].byte = cmd.byte;
}
co_return cc;
}
requester::Coroutine TerminusHandler::getTID()
{
std::cerr << "Discovery Terminus: " << unsigned(eid) << " get TID."
<< std::endl;
auto instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr));
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto rc = encode_get_tid_req(instanceId, request);
if (rc)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "encode_get_tid_req failed. rc=" << unsigned(rc)
<< std::endl;
;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_GET_TID)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_GET_TID)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t tid = PLDM_TID_RESERVED;
rc = decode_get_tid_resp(response, respMsgLen, &cc, &tid);
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_get_tid_resp, Message Error: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc)
<< std::endl;
devInfo.tid = 0xFF;
co_return cc;
}
devInfo.tid = tid;
std::cerr << "Discovery Terminus: EID=" << unsigned(eid) << " TID="
<< unsigned(tid) << std::endl;
co_return cc;
}
requester::Coroutine TerminusHandler::setTID(uint8_t tid)
{
std::cerr << "Discovery Terminus: " << unsigned(eid) << " set TID."
<< std::endl;
auto instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr) + sizeof(tid));
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto rc = encode_set_tid_req(instanceId, tid, request);
if (rc)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "encode_set_tid_req failed. rc=" << unsigned(rc)
<< std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_SET_TID)
<< ", rc=" << unsigned(rc) << std::endl;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || respMsgLen != PLDM_SET_TID_RESP_BYTES)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BASE)
<< ", cmd= " << unsigned(PLDM_SET_TID)
<< ", rc=" << unsigned(rc) << std::endl;
co_return rc;
}
// No decode_set_tid_resp in libpldm, decode by ourself.
cc = response->payload[0];
if (cc != PLDM_SUCCESS)
{
std::cerr << "Failed to setTID, cc=" << unsigned(cc)
<< std::endl;
devInfo.tid = 0xFF;
co_return cc;
}
devInfo.tid = tid;
std::cerr << "Discovery Terminus: EID=" << unsigned(eid) << " set TID="
<< unsigned(devInfo.tid) << std::endl;
co_return cc;
}
void TerminusHandler::setInventoryPath(const std::string& path)
{
inventoryPath = path;
terminusName = std::filesystem::path(inventoryPath).filename();
chassisInventoryPath = pldm::utils::findParent(inventoryPath);
}
requester::Coroutine TerminusHandler::setEventReceiver()
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " set Event Receiver." << std::endl;
uint8_t eventMessageGlobalEnable =
PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
uint8_t transportProtocolType = PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
/* default BMC EID is 8 */
uint8_t eventReceiverAddressInfo = 0x08;
uint16_t heartbeatTimer = 0x78;
auto instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr) +
PLDM_SET_EVENT_RECEIVER_REQ_BYTES);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto rc = encode_set_event_receiver_req(
instanceId, eventMessageGlobalEnable, transportProtocolType,
eventReceiverAddressInfo, heartbeatTimer, request);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to encode_set_event_receiver_req, rc = "
<< unsigned(rc) << std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_PLATFORM)
<< ", cmd= " << unsigned(PLDM_SET_EVENT_RECEIVER)
<< ", rc=" << rc << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_PLATFORM)
<< ", cmd= " << unsigned(PLDM_SET_EVENT_RECEIVER)
<< ", rc=" << rc << std::endl;
;
co_return rc;
}
rc = decode_set_event_receiver_resp(response, respMsgLen, &cc);
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_set_event_receiver_resp,"
<< ", rc=" << unsigned(rc) << " cc=" << unsigned(cc)
<< std::endl;
co_return rc;
}
co_return cc;
}
void epochToBCDTime(const uint64_t& timeSec, uint8_t* seconds, uint8_t* minutes,
uint8_t* hours, uint8_t* day, uint8_t* month,
uint16_t* year)
{
auto t = time_t(timeSec);
auto time = localtime(&t);
*seconds = (uint8_t)pldm::utils::decimalToBcd(time->tm_sec);
*minutes = (uint8_t)pldm::utils::decimalToBcd(time->tm_min);
*hours = (uint8_t)pldm::utils::decimalToBcd(time->tm_hour);
*day = (uint8_t)pldm::utils::decimalToBcd(time->tm_mday);
*month = (uint8_t)pldm::utils::decimalToBcd(
time->tm_mon + 1); // The number of months in the range
// 0 to 11.PLDM expects range 1 to 12
*year = (uint16_t)pldm::utils::decimalToBcd(
time->tm_year + 1900); // The number of years since 1900
}
requester::Coroutine TerminusHandler::setDateTime()
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " update date time to terminus." << std::endl;
uint8_t seconds = 0;
uint8_t minutes = 0;
uint8_t hours = 0;
uint8_t day = 0;
uint8_t month = 0;
uint16_t year = 0;
constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc";
EpochTimeUS timeUsec;
try
{
timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>(
bmcTimePath, "Elapsed", timeInterface);
}
catch (const sdbusplus::exception::exception& e)
{
std::cerr << "Error getting time, PATH=" << bmcTimePath
<< " TIME INTERACE=" << timeInterface << std::endl;
co_return PLDM_ERROR;
}
uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::microseconds(timeUsec))
.count();
epochToBCDTime(timeSec, &seconds, &minutes, &hours, &day, &month, &year);
std::cerr << "SetDateTime timeUsec=" << timeUsec << " seconds="
<< unsigned(seconds) << " minutes=" << unsigned(minutes)
<< " hours=" << unsigned(hours) << " year=" << year << std::endl;
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
sizeof(struct pldm_set_date_time_req));
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
auto instanceId = instanceIdDb.next(eid);
auto rc = encode_set_date_time_req(instanceId, seconds, minutes, hours, day,
month, year, request,
sizeof(struct pldm_set_date_time_req));
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to encode_set_date_time_req, rc = " << unsigned(rc)
<< std::endl;
co_return PLDM_ERROR;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BIOS)
<< ", cmd= " << unsigned(PLDM_SET_DATE_TIME)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_BIOS)
<< ", cmd= " << unsigned(PLDM_SET_DATE_TIME)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
rc = decode_set_date_time_resp(response, respMsgLen, &cc);
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Response Message Error: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc)
<< std::endl;
co_return rc;
}
std::cerr << "Success SetDateTime to terminus " << devInfo.tid << std::endl;
co_return cc;
}
std::string fruFieldValuestring(const uint8_t* value, const uint8_t& length)
{
return std::string(reinterpret_cast<const char*>(value), length);
}
static uint32_t fruFieldParserU32(const uint8_t* value, const uint8_t& length)
{
assert(length == 4);
uint32_t v;
std::memcpy(&v, value, length);
return v;
}
static std::string fruFieldParserTimestamp(const uint8_t*, uint8_t)
{
return std::string("TODO");
}
/** @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] table_size - FRU table size
*/
bool isTableEnd(const uint8_t* table, const uint8_t* p, size_t& tableSize)
{
auto offset = p - table;
return (tableSize - offset) <= 7;
}
void TerminusHandler::parseFruRecordTable(const uint8_t* fruData,
size_t& fruLen)
{
std::string tidFRUObjPath;
if (devInfo.tid == PLDM_TID_RESERVED)
{
std::cerr << "Invalid TID " << std::endl;
return;
}
if (eidToName.second != "")
{
tidFRUObjPath = fruPath + "/" + eidToName.second;
}
else
{
tidFRUObjPath = fruPath + "/" + std::to_string(devInfo.tid);
}
auto fruPtr = std::make_shared<pldm::dbus_api::FruReq>(bus, tidFRUObjPath);
frus.emplace(devInfo.tid, fruPtr);
auto p = fruData;
while (!isTableEnd(fruData, p, fruLen))
{
auto record = reinterpret_cast<const pldm_fru_record_data_format*>(p);
p += sizeof(pldm_fru_record_data_format) - sizeof(pldm_fru_record_tlv);
for (int i = 0; i < record->num_fru_fields; i++)
{
auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(p);
if (record->record_type == PLDM_FRU_RECORD_TYPE_GENERAL)
{
switch (tlv->type)
{
case PLDM_FRU_FIELD_TYPE_CHASSIS:
fruPtr->chassisType(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_MODEL:
fruPtr->model(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_PN:
fruPtr->pn(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_SN:
fruPtr->sn(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_MANUFAC:
fruPtr->manufacturer(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_MANUFAC_DATE:
fruPtr->manufacturerDate(
fruFieldParserTimestamp(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_VENDOR:
fruPtr->vendor(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_NAME:
fruPtr->name(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_SKU:
fruPtr->sku(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_VERSION:
fruPtr->version(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_ASSET_TAG:
fruPtr->assetTag(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_DESC:
fruPtr->description(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_EC_LVL:
fruPtr->ecLevel(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_OTHER:
fruPtr->other(
fruFieldValuestring(tlv->value, tlv->length));
break;
case PLDM_FRU_FIELD_TYPE_IANA:
fruPtr->iana(
fruFieldParserU32(tlv->value, tlv->length));
break;
}
}
p += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
}
}
}
requester::Coroutine TerminusHandler::getFRURecordTableMetadata(uint16_t* total)
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " get FRU record Table Meta Data." << std::endl;
auto instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr) +
PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
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(eid, instanceId);
std::cerr << "Failed to encode_get_fru_record_table_metadata_req, rc = "
<< unsigned(rc) << std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_FRU)
<< ", cmd= " << unsigned(PLDM_GET_FRU_RECORD_TABLE_METADATA)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_FRU)
<< ", cmd= " << unsigned(PLDM_GET_FRU_RECORD_TABLE_METADATA)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
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;
uint32_t checksum;
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)
{
std::cerr << "Failed to decode get fru record table metadata resp, "
"Message Error: "
<< "rc=" << unsigned(rc) << ", cc=" << unsigned(cc)
<< std::endl;
co_return rc;
}
co_return rc;
}
requester::Coroutine
TerminusHandler::getFRURecordTable(const uint16_t& totalTableRecords)
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " get FRU record Table." << std::endl;
if (!totalTableRecords)
{
std::cerr << "Number of record table is not correct." << std::endl;
co_return PLDM_ERROR;
}
auto instanceId = instanceIdDb.next(eid);
Request 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(eid, instanceId);
std::cerr << "Failed to encode_get_fru_record_table_req, rc = "
<< unsigned(rc) << std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_FRU)
<< ", cmd= " << unsigned(PLDM_GET_FRU_RECORD_TABLE)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_FRU)
<< ", cmd= " << unsigned(PLDM_GET_FRU_RECORD_TABLE)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
uint32_t nextDataTransferHandle = 0;
uint8_t transferFlag = 0;
size_t fruRecordTableLength = 0;
std::vector<uint8_t> fruRecordTableData(respMsgLen - sizeof(pldm_msg_hdr));
auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
rc = decode_get_fru_record_table_resp(
responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &cc,
&nextDataTransferHandle, &transferFlag, fruRecordTableData.data(),
&fruRecordTableLength);
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr
<< "Failed to decode get fru record table resp, Message Error: "
<< "rc=" << unsigned(rc) << ", cc=" << unsigned(cc) << std::endl;
co_return rc;
}
parseFruRecordTable(fruRecordTableData.data(), fruRecordTableLength);
co_return cc;
}
requester::Coroutine TerminusHandler::getDevPDR(uint32_t nextRecordHandle)
{
std::cerr << "Discovery Terminus: " << unsigned(eid)
<< " get terminus PDRs." << std::endl;
do
{
/* Check whether the terminus is removed when getting PDRs */
if (stopTerminusPolling)
{
co_return PLDM_SUCCESS;
}
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)
{
recordHandle = nextRecordHandle;
}
auto instanceId = instanceIdDb.next(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(eid, instanceId);
std::cerr << "Failed to encode_get_pdr_req, rc = " << unsigned(rc)
<< std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_PLATFORM)
<< ", cmd= " << unsigned(PLDM_GET_PDR)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
auto respMsgLen = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !respMsgLen)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId="
<< unsigned(instanceId) << ", type="
<< unsigned(PLDM_PLATFORM) << ", cmd= "
<< unsigned(PLDM_GET_PDR) << ", rc="
<< unsigned(rc) << std::endl;
;
co_return rc;
}
rc = co_await processDevPDRs(eid, response, respMsgLen,
&nextRecordHandle, recordHandle);
if (rc)
{
std::cerr << "Failed to send processDevPDRs, EID=" << unsigned(eid)
<< ", rc=" << unsigned(rc) << std::endl;
;
co_return rc;
}
} while (nextRecordHandle != 0);
if (!nextRecordHandle)
{
co_return PLDM_SUCCESS;
}
co_return PLDM_ERROR;
}
requester::Coroutine TerminusHandler::processDevPDRs(mctp_eid_t& /*eid*/,
const pldm_msg* response,
size_t& respMsgLen,
uint32_t* nextRecordHandle,
uint32_t recordHandle)
{
uint8_t tlEid = 0;
uint32_t rh = 0;
uint8_t completionCode{};
uint32_t nextDataTransferHandle{};
uint8_t transferFlag{};
uint16_t respCount{};
uint8_t transferCRC{};
if (response == nullptr || !respMsgLen)
{
std::cerr << "Failed to receive response for the GetPDR"
" command \n";
co_return PLDM_ERROR;
}
auto rc = decode_get_pdr_resp(
response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
nullptr, 0, &transferCRC);
if (rc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_get_pdr_resp, rc = " << unsigned(rc)
<< std::endl;
co_return rc;
}
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)
{
std::cerr << "Failed to decode_get_pdr_resp: "
<< "rc=" << unsigned(rc)
<< ", cc=" << unsigned(completionCode) << std::endl;
co_return rc;
}
/*
* Temporary: If PDR is multi-part PDR just get the first part then go to
* next PDRs
* Todo: Support multi-part in getting PDRs.
*/
if ((*nextRecordHandle) && (transferFlag != PLDM_END) &&
(transferFlag != PLDM_START_AND_END))
{
*nextRecordHandle = recordHandle + 1;
}
// 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)
{
/* Temporary remove merge Entity Association feature */
// this->mergeEntityAssociations(pdr);
this->entityAssociationPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
{
auto tlpdr =
reinterpret_cast<const pldm_terminus_locator_pdr*>(pdr.data());
terminusHandle = tlpdr->terminus_handle;
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);
}
tlPDRInfo.insert_or_assign(
tlpdr->terminus_handle,
std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
}
else if (pdrHdr->type == PLDM_NUMERIC_SENSOR_PDR)
{
std::vector<uint8_t> parsedPdr(sizeof(pldm_numeric_sensor_value_pdr),
0);
rc = decode_numeric_sensor_pdr_data(
pdr.data(), respCount,
reinterpret_cast<pldm_numeric_sensor_value_pdr*>(parsedPdr.data()));
if (rc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_numeric_sensor_pdr_data, rc = " << rc
<< std::endl;
co_return rc;
}
this->numericSensorPDRs.emplace_back(parsedPdr);
}
else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
{
this->stateSensorPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_COMPACT_NUMERIC_SENSOR_PDR)
{
this->compNumSensorPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
{
this->effecterPDRs.emplace_back(pdr);
}
else if (pdrHdr->type == PLDM_EFFECTER_AUXILIARY_NAMES_PDR)
{
this->effecterAuxNamePDRs.emplace_back(pdr);
}
co_return PLDM_SUCCESS;
}
static void fillNumericSensorThreshold(const pldm_numeric_sensor_value_pdr* pdr,
PldmSensorInfo& sensorInfo)
{
BitField8 bitfield(pdr->supported_thresholds);
sensorInfo.warningHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.warningLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalLow = std::numeric_limits<double>::quiet_NaN();
if (bitfield.bits.bit0)
{
sensorInfo.warningHigh = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->warning_high);
}
if (bitfield.bits.bit1)
{
sensorInfo.criticalHigh = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->critical_high);
}
if (bitfield.bits.bit2)
{
sensorInfo.fatalHigh = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->fatal_high);
}
if (bitfield.bits.bit3)
{
sensorInfo.warningLow = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->warning_low);
}
if (bitfield.bits.bit4)
{
sensorInfo.criticalLow = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->critical_low);
}
if (bitfield.bits.bit5)
{
sensorInfo.fatalLow = pldm::utils::castNumericSensorRangeField(
static_cast<pldm_range_field_format>(pdr->range_field_format),
pdr->fatal_low);
}
}
std::string TerminusHandler::generateSensorName(uint16_t sensorId)
{
// Default sensor name
std::string sensorName = std::format("TID_{}_SensorId_{}", devInfo.tid,
sensorId);
// Todo: check and use Sensor Auxiliary Names PDR
// Check and use aux name override from EM config
if (auxNameOverride.count(sensorId))
{
const std::vector<std::string>& auxNames = auxNameOverride[sensorId];
if (auxNames.size() > 0)
{
sensorName = auxNames[0];
std::string chassisName =
std::filesystem::path(chassisInventoryPath).filename();
if (!sensorName.starts_with(chassisName))
{
// Add chassis and terminus prefix in case that the sensorName
// doesn't have those info.
sensorName = std::format("{}_{}_{}", chassisName, terminusName,
sensorName);
}
}
}
return sensorName;
}
void TerminusHandler::createNumericSensorIntf(const PDRList& sensorPDRs)
{
for (const auto& sensorPDR : sensorPDRs)
{
const pldm_numeric_sensor_value_pdr* pdr =
reinterpret_cast<const pldm_numeric_sensor_value_pdr*>(
sensorPDR.data());
// Todo: check for duplicate sensorId
uint16_t sensorId = pdr->sensor_id;
PldmSensorInfo sensorInfo{};
sensorInfo.entityType = pdr->entity_type;
sensorInfo.entityInstance = pdr->entity_instance;
sensorInfo.containerId = pdr->container_id;
sensorInfo.baseUnit = pdr->base_unit;
sensorInfo.unitModifier = pdr->unit_modifier;
sensorInfo.offset = pdr->offset;
sensorInfo.resolution = pdr->resolution;
sensorInfo.occurrenceRate = pdr->rate_unit;
sensorInfo.maxValue = pldm::utils::castSensorDataToDouble(
static_cast<pldm_sensor_readings_data_type>(pdr->sensor_data_size),
pdr->max_readable);
sensorInfo.minValue = pldm::utils::castSensorDataToDouble(
static_cast<pldm_sensor_readings_data_type>(pdr->sensor_data_size),
pdr->min_readable);
fillNumericSensorThreshold(pdr, sensorInfo);
sensorInfo.sensorName = generateSensorName(sensorId);
std::cerr << std::format("tid: {}, Adding numeric sensor: {}\n",
devInfo.tid, sensorInfo.sensorName);
auto sensorObject = std::make_unique<PldmSensor>(
bus, sensorInfo.sensorName, sensorInfo.baseUnit,
sensorInfo.unitModifier, sensorInfo.offset, sensorInfo.resolution,
sensorInfo.warningHigh, sensorInfo.warningLow,
sensorInfo.criticalHigh, sensorInfo.criticalLow);
sensorObject->initMinMaxValue(sensorInfo.minValue, sensorInfo.maxValue);
sensorObject->setChassisInventoryPath(chassisInventoryPath);
PLDMEntity entity{pdr->entity_type, pdr->entity_instance,
pdr->container_id};
if (entityToInventoryMap.contains(entity))
{
sensorObject->setInventoryObject(entityToInventoryMap[entity]);
}
auto object = sensorObject->createSensor();
if (object)
{
auto value = std::make_tuple(pdr->sensor_id,
std::move((*object).second));
auto key = std::make_tuple(eid, pdr->sensor_id, pdr->hdr.type);
_sensorObjects[key] = std::move(sensorObject);
_state[std::move(key)] = std::move(value);
}
}
}
void TerminusHandler::createCompactNummericSensorIntf(const PDRList& sensorPDRs)
{
/** @brief Store the added sensor D-Bus object path */
std::vector<uint16_t> _addedSensorId;
for (const auto& sensorPDR : sensorPDRs)
{
auto pdr = reinterpret_cast<const pldm_compact_numeric_sensor_pdr*>(
sensorPDR.data());
auto it = std::find(_addedSensorId.begin(), _addedSensorId.end(),
pdr->sensor_id);
if (it != _addedSensorId.end())
{
std::cerr << "Sensor " << pdr->sensor_id << " added." << std::endl;
continue;
}
_addedSensorId.emplace_back(pdr->sensor_id);
PldmSensorInfo sensorInfo{};
auto terminusHandle = pdr->terminus_handle;
sensorInfo.entityType = pdr->entity_type;
sensorInfo.entityInstance = pdr->entity_instance;
sensorInfo.containerId = pdr->container_id;
sensorInfo.sensorNameLength = pdr->sensor_name_length;
if (sensorInfo.sensorNameLength == 0)
{
sensorInfo.sensorName =
"SensorId" + std::to_string(unsigned(pdr->sensor_id));
}
else
{
std::string sTemp(reinterpret_cast<char const*>(pdr->sensor_name),
sensorInfo.sensorNameLength);
size_t pos = 0;
while ((pos = sTemp.find(" ")) != std::string::npos)
{
sTemp.replace(pos, 1, "_");
}
sensorInfo.sensorName = sTemp;
}
sensorInfo.baseUnit = pdr->base_unit;
sensorInfo.unitModifier = pdr->unit_modifier;
sensorInfo.offset = 0;
sensorInfo.resolution = 1;
sensorInfo.occurrenceRate = pdr->occurrence_rate;
sensorInfo.rangeFieldSupport = pdr->range_field_support;
sensorInfo.warningHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.warningLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalLow = std::numeric_limits<double>::quiet_NaN();
if (pdr->range_field_support.bits.bit0)
{
sensorInfo.warningHigh = double(pdr->warning_high);
}
if (pdr->range_field_support.bits.bit1)
{
sensorInfo.warningLow = double(pdr->warning_low);
}
if (pdr->range_field_support.bits.bit2)
{
sensorInfo.criticalHigh = double(pdr->critical_high);
}
if (pdr->range_field_support.bits.bit3)
{
sensorInfo.criticalLow = double(pdr->critical_low);
}
if (pdr->range_field_support.bits.bit4)
{
sensorInfo.fatalHigh = double(pdr->fatal_high);
}
if (pdr->range_field_support.bits.bit5)
{
sensorInfo.fatalLow = double(pdr->fatal_low);
}
auto terminusId = PLDM_TID_RESERVED;
try
{
terminusId = std::get<0>(tlPDRInfo.at(terminusHandle));
}
catch (const std::out_of_range& e)
{
// Do nothing
}
/* There is TID mapping */
if (eidToName.second != "")
{
/* PREFIX */
if (eidToName.first == true)
{
sensorInfo.sensorName =
eidToName.second + sensorInfo.sensorName;
}
else
{
sensorInfo.sensorName =
sensorInfo.sensorName + eidToName.second;
}
}
else
{
sensorInfo.sensorName = sensorInfo.sensorName + "_TID" +
std::to_string(unsigned(terminusId));
}
std::cerr << "Adding sensor name: " << sensorInfo.sensorName
<< std::endl;
auto sensorObject = std::make_unique<PldmSensor>(
bus, sensorInfo.sensorName, sensorInfo.baseUnit,
sensorInfo.unitModifier, sensorInfo.offset, sensorInfo.resolution,
sensorInfo.warningHigh, sensorInfo.warningLow,
sensorInfo.criticalHigh, sensorInfo.criticalLow);
auto object = sensorObject->createSensor();
if (object)
{
auto value =
std::make_tuple(pdr->sensor_id, std::move((*object).second));
auto key = std::make_tuple(eid, pdr->sensor_id, pdr->hdr.type);
_sensorObjects[key] = std::move(sensorObject);
_state[std::move(key)] = std::move(value);
}
}
return;
}
void TerminusHandler::createNummericEffecterDBusIntf(const PDRList& sensorPDRs)
{
std::vector<auxNameKey> _addedEffecter;
for (const auto& sensorPDR : sensorPDRs)
{
auto pdr = reinterpret_cast<const pldm_numeric_effecter_value_pdr*>(
sensorPDR.data());
auxNameKey namekey =
std::make_tuple(pdr->terminus_handle, pdr->effecter_id);
auto it =
std::find(_addedEffecter.begin(), _addedEffecter.end(), namekey);
if (it != _addedEffecter.end())
{
std::cerr << "Effecter " << pdr->effecter_id << " existed."
<< std::endl;
continue;
}
_addedEffecter.emplace_back(namekey);
PldmSensorInfo sensorInfo{};
auto terminusHandle = pdr->terminus_handle;
sensorInfo.entityType = pdr->entity_type;
sensorInfo.entityInstance = pdr->entity_instance;
sensorInfo.containerId = pdr->container_id;
std::string sTemp = "";
if (_auxNameMaps.find(namekey) != _auxNameMaps.end())
{
try
{
/* Use first name of first sensor idx for effecter name */
sTemp = get<1>(_auxNameMaps[namekey][0][0]);
}
catch (const std::exception& e)
{
std::cerr << "Failed to get name of Aux Name Key : "
<< get<0>(namekey) << ":" << get<1>(namekey) << '\n';
sTemp =
"Effecter_" + std::to_string(unsigned(pdr->effecter_id));
}
}
else
{
std::cerr << "No Aux Name of effecter : " << get<0>(namekey) << ":"
<< get<1>(namekey) << '\n';
sTemp = "Effecter_" + std::to_string(unsigned(pdr->effecter_id));
}
size_t pos = 0;
while ((pos = sTemp.find(" ")) != std::string::npos)
{
sTemp.replace(pos, 1, "_");
}
sensorInfo.sensorName = sTemp;
sensorInfo.sensorNameLength = sTemp.length();
sensorInfo.baseUnit = pdr->base_unit;
sensorInfo.unitModifier = pdr->unit_modifier;
sensorInfo.offset = pdr->offset;
sensorInfo.resolution = pdr->resolution;
sensorInfo.occurrenceRate = pdr->rate_unit;
sensorInfo.rangeFieldSupport = pdr->range_field_support;
sensorInfo.warningHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.warningLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.criticalLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalHigh = std::numeric_limits<double>::quiet_NaN();
sensorInfo.fatalLow = std::numeric_limits<double>::quiet_NaN();
sensorInfo.maxSetTable = std::numeric_limits<double>::quiet_NaN();
sensorInfo.minSetTable = std::numeric_limits<double>::quiet_NaN();
switch (pdr->effecter_data_size)
{
case PLDM_SENSOR_DATA_SIZE_UINT8:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_u8);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_u8);
break;
case PLDM_SENSOR_DATA_SIZE_SINT8:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_s8);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_s8);
break;
case PLDM_SENSOR_DATA_SIZE_UINT16:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_u16);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_u16);
break;
case PLDM_SENSOR_DATA_SIZE_SINT16:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_s16);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_s16);
break;
case PLDM_SENSOR_DATA_SIZE_UINT32:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_u32);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_u32);
break;
case PLDM_SENSOR_DATA_SIZE_SINT32:
sensorInfo.maxSetTable =
static_cast<double>(pdr->max_settable.value_s32);
sensorInfo.minSetTable =
static_cast<double>(pdr->min_settable.value_s32);
break;
default:
break;
}
auto terminusId = PLDM_TID_RESERVED;
try
{
terminusId = std::get<0>(tlPDRInfo.at(terminusHandle));
}
catch (const std::out_of_range& e)
{
// Do nothing
}
/* There is TID mapping */
if (eidToName.second != "")
{
/* PREFIX */
if (eidToName.first == true)
{
sensorInfo.sensorName =
eidToName.second + sensorInfo.sensorName;
}
else
{
sensorInfo.sensorName =
sensorInfo.sensorName + eidToName.second;
}
}
else
{
sensorInfo.sensorName = sensorInfo.sensorName + "_TID" +
std::to_string(unsigned(terminusId));
}
std::cerr << "Adding effecter name: " << sensorInfo.sensorName
<< std::endl;
auto sensorObj = std::make_unique<PldmSensor>(
bus, sensorInfo.sensorName, sensorInfo.baseUnit,
sensorInfo.unitModifier, sensorInfo.offset, sensorInfo.resolution,
sensorInfo.warningHigh, sensorInfo.warningLow,
sensorInfo.criticalHigh, sensorInfo.criticalLow);
sensorObj->initMinMaxValue(sensorInfo.minSetTable,
sensorInfo.maxSetTable);
auto object = sensorObj->createSensor();
if (object)
{
auto value =
std::make_tuple(pdr->effecter_id, std::move((*object).second));
auto key = std::make_tuple(eid, pdr->effecter_id, pdr->hdr.type);
_sensorObjects[key] = std::move(sensorObj);
_effecterLists.emplace_back(key);
_state[std::move(key)] = std::move(value);
}
}
return;
}
void TerminusHandler::createStateSensorIntf(const PDRList& sensorPDRs)
{
for (const auto& sensorPDR : sensorPDRs)
{
const pldm_state_sensor_pdr* pdr =
reinterpret_cast<const pldm_state_sensor_pdr*>(sensorPDR.data());
std::string sensorName = generateSensorName(pdr->sensor_id);
std::cerr << std::format("tid: {}, Adding state sensor: {}\n",
devInfo.tid, sensorName);
if (pdr->composite_sensor_count < 1 || pdr->composite_sensor_count > 8)
{
std::cerr << sensorName << ": Invalid composite_sensor_count: "
<< pdr->composite_sensor_count << std::endl;
continue; // Processes next PDR
}
auto sensorObject = std::make_unique<PldmStateSensor>(bus, sensorName);
const uint8_t* pointer = pdr->possible_states;
const uint8_t* end = sensorPDR.data() + sensorPDR.size();
bool invalidPdr = false;
for (int i = 0; i < pdr->composite_sensor_count; i++)
{
if (pointer > end)
{
std::cerr << sensorName << ": Invalid state sensor PDR"
<< std::endl;
invalidPdr = true;
break;
}
auto possibleState =
reinterpret_cast<const state_sensor_possible_states*>(pointer);
uint16_t stateSetId = possibleState->state_set_id;
sensorObject->addStateSet(stateSetId);
uint8_t possibleStatesSize = possibleState->possible_states_size;
pointer += (sizeof(possibleState->state_set_id) +
sizeof(possibleState->possible_states_size) +
possibleStatesSize);
}
if (invalidPdr) break;
PLDMEntity entity{pdr->entity_type, pdr->entity_instance,
pdr->container_id};
if (entityToInventoryMap.contains(entity))
{
sensorObject->setInventoryObject(entityToInventoryMap[entity]);
}
auto object = sensorObject->createStateSensor();
if (object)
{
// Todo: clean this piece of code, it's not straight forward.
auto key = std::make_tuple(eid, pdr->sensor_id, pdr->hdr.type);
auto value = std::make_tuple(pdr->sensor_id,
std::move((*object).second));
_stateSensorObjects[key] = std::move(sensorObject);
_state[std::move(key)] = std::move(value);
}
}
}
void TerminusHandler::parseAuxNamePDRs(const PDRList& sensorPDRs)
{
constexpr uint8_t nullTerminator = 0;
for (const auto& sensorPDR : sensorPDRs)
{
auto pdr =
(struct pldm_sensor_auxiliary_names_pdr*)sensorPDR.data();
if (!pdr)
{
std::cerr << "Failed to get Aux Name PDR" << std::endl;
return;
}
auxNameKey key((uint16_t)pdr->terminus_handle,
(uint16_t)pdr->sensor_id);
auxNameSensorMapping sensorNameMapping;
const uint8_t* ptr = pdr->names;
for ([[maybe_unused]] auto i :
std::views::iota(0, (int)pdr->sensor_count))
{
const uint8_t nameStringCount = static_cast<uint8_t>(*ptr);
auxNameList nameLists{};
ptr += sizeof(uint8_t);
for ([[maybe_unused]] auto j :
std::views::iota(0, (int)nameStringCount))
{
std::string nameLanguageTag(reinterpret_cast<const char*>(ptr),
0, PLDM_STR_UTF_8_MAX_LEN);
ptr += nameLanguageTag.size() + sizeof(nullTerminator);
std::u16string u16NameString(
reinterpret_cast<const char16_t*>(ptr), 0,
PLDM_STR_UTF_16_MAX_LEN);
ptr += (u16NameString.size() + sizeof(nullTerminator)) *
sizeof(uint16_t);
std::transform(u16NameString.cbegin(), u16NameString.cend(),
u16NameString.begin(),
[](uint16_t utf16) { return be16toh(utf16); });
std::string nameString =
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
char16_t>{}
.to_bytes(u16NameString);
nameLists.emplace_back(
std::make_tuple(nameLanguageTag, nameString));
}
if (!nameLists.size())
{
continue;
}
sensorNameMapping.emplace_back(nameLists);
}
if (!sensorNameMapping.size())
{
std::cerr << "Failed to find Aux Name of sensor Key " << get<0>(key)
<< ":" << get<1>(key) << "in mapping table." << std::endl;
continue;
}
if (_auxNameMaps.find(key) != _auxNameMaps.end())
{
std::cerr << "Aux Name Key : " << get<0>(key) << ":" << get<1>(key)
<< " existed in mapping table." << std::endl;
continue;
}
_auxNameMaps[key] = sensorNameMapping;
}
return;
}
/** @brief Start timer to get sensor info
*/
void TerminusHandler::startSensorsPolling()
{
readCount = 0;
continuePollSensor = true;
std::function<void()> pollCallback(
std::bind(&TerminusHandler::pollSensors, this));
std::function<void()> readCallback(
std::bind(&TerminusHandler::readSensor, this));
try
{
_timer.restart(std::chrono::milliseconds(POLL_SENSOR_TIMER_INTERVAL));
_timer2.restart(
std::chrono::milliseconds(SLEEP_BETWEEN_GET_SENSOR_READING));
}
catch (const std::exception& e)
{
std::cerr << "Error in sysfs polling loop" << std::endl;
throw;
}
return;
}
/** @brief Stop timer to get sensor info
*/
void TerminusHandler::stopSensorsPolling()
{
continuePollSensor = false;
_timer.setEnabled(false);
_timer2.setEnabled(false);
_timer3.setEnabled(false);
// Set sensors values to Nan and Functional property to false for FANs speeds to be driven max
invalidateAllSensors();
}
void TerminusHandler::removeUnavailableSensor(
const std::vector<sensor_key>& vKeys)
{
for (const auto& key : vKeys)
{
if (_state.find(key) != _state.end())
{
_state.erase(key);
}
if (_sensorObjects.find(key) != _sensorObjects.end())
{
std::unique_ptr<PldmSensor>& sensorObj = _sensorObjects[key];
bus.emit_object_removed(sensorObj->getSensorPath().c_str());
_sensorObjects.erase(key);
}
if (_stateSensorObjects.find(key) != _stateSensorObjects.end())
{
std::unique_ptr<PldmStateSensor>& sensorObj =
_stateSensorObjects[key];
bus.emit_object_removed(sensorObj->getSensorPath().c_str());
_stateSensorObjects.erase(key);
}
}
updateSensorKeys();
return;
}
void TerminusHandler::removeEffecterFromPollingList(
const std::vector<sensor_key>& vKeys)
{
for (const auto& key : vKeys)
{
if (_state.find(key) != _state.end())
{
_state.erase(key);
}
}
updateSensorKeys();
return;
}
void TerminusHandler::removeMCTPEndpoint()
{
std::string eidObject =
"/au/com/codeconstruct/mctp1/networks/1/endpoints/" +
std::to_string(eid);
std::cerr << "Removing " << eidObject << std::endl;
try
{
auto method = bus.new_method_call(
"au.com.codeconstruct.MCTP1", eidObject.c_str(),
"au.com.codeconstruct.MCTP.Endpoint1", "Remove");
// There is no input and no outpt for Remove method.
// The MCTP endpoint may already has been removed so we can ignore
// error.
[[maybe_unused]] auto reply = bus.call(method, dbusTimeout);
}
catch (const std::exception& e)
{
std::cerr << "Error in remove " << eidObject << " : " << e.what()
<< std::endl;
}
}
static constexpr int stopPollingThreshold = 3;
void TerminusHandler::sensorPollingTimeoutHandler()
{
timeoutCount++;
if (timeoutCount >= stopPollingThreshold)
{
stopSensorsPolling();
// Remove our MCTP endpoint and mctp-i2c.service will keep retry to
// re-setup MCTP endpoint. terminus_manager will delay the erase of this
// terminus until the same MCTP endpoint is added.
removeMCTPEndpoint();
}
}
/** @brief Start reading the sensors info process
*/
void TerminusHandler::pollSensors()
{
if (!isTerminusOn())
{
return;
}
if (!continuePollSensor)
{
return;
}
if (!createdDbusObject)
{
return;
}
if (pollingSensors)
{
std::cerr
<< std::format(
"Warning: eid: {}, readCount:{} Last sensor polling is not DONE.",
eid, readCount)
<< std::endl;
return;
}
#ifdef DESTROY_UNAVALIABLE_SENSOR
if (unavailableSensorKeys.size())
{
removeUnavailableSensor(std::move(unavailableSensorKeys));
unavailableSensorKeys.clear();
}
#endif
this->sensorKey = sensorKeys.begin();
pollingSensors = true;
readCount++;
return;
}
/** @brief Start reading the sensors info process
*/
void TerminusHandler::readSensor()
{
if (!createdDbusObject)
{
return;
}
if (!continuePollSensor)
{
return;
}
if (!pollingSensors)
{
return;
}
if (sendingPldmCommand)
{
return;
}
if (this->sensorKey == sensorKeys.begin() && debugPollSensor)
{
startTime = std::chrono::system_clock::now();
std::cerr << std::format("[tid:{} {}] Start new pollSensor at {}\n",
devInfo.tid, readCount,
getCurrentSystemTime());
/* Stop print polling debug after 50 rounds */
if (readCount > 50)
{
debugPollSensor = false;
}
}
if (this->sensorKey != sensorKeys.end())
{
getSensorReading(get<1>(*(this->sensorKey)),
get<2>(*(this->sensorKey)));
}
else
{
pollingSensors = false;
if (debugPollSensor)
{
std::chrono::duration<double> elapsed =
std::chrono::system_clock::now() - startTime;
std::cerr << std::format("[tid:{} {}] Finish one pollsensor round"
" for {} sensors after {} at {}\n",
devInfo.tid, readCount, sensorKeys.size(),
elapsed, getCurrentSystemTime());
}
}
return;
}
bool verifySensorFunctionalStatus(const uint8_t& pdrType,
const uint8_t& operationState)
{
if (pdrType == PLDM_COMPACT_NUMERIC_SENSOR_PDR ||
pdrType == PLDM_NUMERIC_SENSOR_PDR)
{
/* enabled */
if (operationState != PLDM_SENSOR_ENABLED)
{
return false;
}
}
else if (pdrType == PLDM_NUMERIC_EFFECTER_PDR)
{
/* enabled-updatePending, enabled-noUpdatePending */
if ((operationState != EFFECTER_OPER_STATE_ENABLED_UPDATEPENDING) &&
(operationState != EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING))
{
return false;
}
}
return true;
}
bool verifySensorAvailableStatus(const uint8_t& pdrType,
const uint8_t& operationState)
{
if (pdrType == PLDM_COMPACT_NUMERIC_SENSOR_PDR ||
pdrType == PLDM_NUMERIC_SENSOR_PDR)
{
/* unavailable */
if (operationState == PLDM_SENSOR_UNAVAILABLE)
{
return false;
}
}
else if (pdrType == PLDM_NUMERIC_EFFECTER_PDR)
{
/* unavailable */
if (operationState == EFFECTER_OPER_STATE_UNAVAILABLE)
{
return false;
}
}
return true;
}
void TerminusHandler::processStateSensorReading(mctp_eid_t,
const pldm_msg* response,
size_t respMsgLen)
{
uint8_t eid = std::get<0>(*(this->sensorKey));
uint16_t sid = std::get<1>(*(this->sensorKey));
if (response == nullptr || !respMsgLen)
{
std::cerr << "Failed to receive response for the GetStateSensorReading"
<< " command of eid:sensor " << unsigned(eid) << ":" << sid
<< std::endl;
pldm::flightrecorder::FlightRecorder::GetInstance().increaseError(eid);
pldm::flightrecorder::FlightRecorder::GetInstance().printEidHistory(eid);
/* Go to next sensor */
this->sensorKey++;
sendingPldmCommand = false;
sensorPollingTimeoutHandler();
return;
}
timeoutCount = 0;
int rc = PLDM_ERROR;
uint8_t cc = 0;
get_sensor_state_field fields[8]{};
uint8_t compositeSensorCount = 0;
rc = decode_get_state_sensor_readings_resp(response, respMsgLen, &cc,
&compositeSensorCount, fields);
std::unique_ptr<PldmStateSensor>& sensorObj =
_stateSensorObjects[*(this->sensorKey)];
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode get sensor value: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc) << " "
<< unsigned(eid) << ":" << sid << std::endl;
}
else
{
sensorObj->updateStateReading(compositeSensorCount, fields);
}
this->sensorKey++;
sendingPldmCommand = false;
return;
}
void TerminusHandler::invalidateAllSensors()
{
// Numeric sensors
for (auto& entry : _sensorObjects)
{
entry.second->setFunctionalStatus(false);
entry.second->updateValue(std::numeric_limits<double>::quiet_NaN());
}
// State sensors
for (auto& entry : _stateSensorObjects)
{
entry.second->invalidateStateSensor();
}
}
/** @brief Callback function to process the response data after send the
* getSensorReading request thru PLDM
*/
void TerminusHandler::processSensorReading(mctp_eid_t, const pldm_msg* response,
size_t respMsgLen)
{
if (response == nullptr || !respMsgLen)
{
auto sid = std::get<1>(*(this->sensorKey));
std::cerr << "Failed to receive response for the GetSensorReading"
<< " command of eid:sensor " << unsigned(eid) << ":" << sid
<< std::endl;
pldm::flightrecorder::FlightRecorder::GetInstance().increaseError(eid);
pldm::flightrecorder::FlightRecorder::GetInstance().printEidHistory(
eid);
std::unique_ptr<PldmSensor>& sensorObj =
_sensorObjects[*(this->sensorKey)];
sensorObj->updateValue(std::numeric_limits<double>::quiet_NaN());
sensorObj->setFunctionalStatus(false);
/* Go to next sensor */
this->sensorKey++;
sensorPollingTimeoutHandler();
}
else
{
timeoutCount = 0;
int rc = PLDM_ERROR;
uint8_t pdr_type = std::get<2>(*(this->sensorKey));
union_range_field_format presentReading;
uint8_t cc = 0;
uint8_t dataSize = PLDM_SENSOR_DATA_SIZE_SINT32;
uint8_t operationalState = PLDM_SENSOR_ENABLED;
uint8_t eventMessEn;
uint8_t presentState;
uint8_t previousState;
uint8_t eventState;
union_range_field_format pendingValue;
SensorValueType sensorValue =
std::numeric_limits<double>::quiet_NaN();
if (pdr_type == PLDM_COMPACT_NUMERIC_SENSOR_PDR ||
pdr_type == PLDM_NUMERIC_SENSOR_PDR)
{
rc = decode_get_sensor_reading_resp(
response, respMsgLen, &cc, &dataSize, &operationalState,
&eventMessEn, &presentState, &previousState, &eventState,
reinterpret_cast<uint8_t*>(&presentReading));
}
else if (pdr_type == PLDM_NUMERIC_EFFECTER_PDR)
{
rc = decode_get_numeric_effecter_value_resp(
response, respMsgLen, &cc, &dataSize, &operationalState,
reinterpret_cast<uint8_t*>(&pendingValue),
reinterpret_cast<uint8_t*>(&presentReading));
}
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
auto sid = std::get<1>(*(this->sensorKey));
std::cerr << "Failed to decode get sensor value: "
<< "rc=" << unsigned(rc) << ",cc=" << unsigned(cc) << " "
<< unsigned(eid) << ":" << sid << std::endl;
sensorValue = std::numeric_limits<double>::quiet_NaN();
operationalState = PLDM_SENSOR_DISABLED;
}
else
{
switch (dataSize)
{
case PLDM_SENSOR_DATA_SIZE_UINT8:
sensorValue = static_cast<double>(presentReading.value_u8);
break;
case PLDM_SENSOR_DATA_SIZE_SINT8:
sensorValue = static_cast<double>(presentReading.value_s8);
break;
case PLDM_SENSOR_DATA_SIZE_UINT16:
sensorValue =
static_cast<double>(presentReading.value_u16);
break;
case PLDM_SENSOR_DATA_SIZE_SINT16:
sensorValue =
static_cast<double>(presentReading.value_s16);
break;
case PLDM_SENSOR_DATA_SIZE_UINT32:
sensorValue =
static_cast<double>(presentReading.value_u32);
break;
case PLDM_SENSOR_DATA_SIZE_SINT32:
sensorValue =
static_cast<double>(presentReading.value_s32);
break;
default:
sensorValue = std::numeric_limits<double>::quiet_NaN();
break;
}
}
std::unique_ptr<PldmSensor>& sensorObj =
_sensorObjects[*(this->sensorKey)];
bool functional = verifySensorFunctionalStatus(
std::get<2>(*(this->sensorKey)), operationalState);
[[maybe_unused]] bool available = verifySensorAvailableStatus(
std::get<2>(*(this->sensorKey)), operationalState);
#ifdef DESTROY_UNAVALIABLE_SENSOR
/* the CompactNumericSensor is unavailable */
if (!available)
{
unavailableSensorKeys.push_back(*(this->sensorKey));
}
#endif
if (sensorObj)
{
/* unavailable */
if (!functional)
{
sensorValue = std::numeric_limits<double>::quiet_NaN();
}
sensorObj->setFunctionalStatus(functional);
sensorObj->updateValue(sensorValue);
}
this->sensorKey++;
}
sendingPldmCommand = false;
return;
}
/** @brief Send the getSensorReading request to get sensor info
*/
void TerminusHandler::getSensorReading(uint16_t sensor_id, uint8_t pdr_type)
{
sendingPldmCommand = true;
uint8_t req_byte = PLDM_GET_SENSOR_READING_REQ_BYTES;
if (pdr_type == PLDM_NUMERIC_EFFECTER_PDR)
{
req_byte = PLDM_GET_NUMERIC_EFFECTER_VALUE_REQ_BYTES;
}
else if (pdr_type == PLDM_STATE_SENSOR_PDR)
{
req_byte = PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES;
}
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + req_byte);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
uint8_t rearmEventState = 0;
auto instanceId = instanceIdDb.next(eid);
int rc = PLDM_ERROR;
if (pdr_type == PLDM_COMPACT_NUMERIC_SENSOR_PDR ||
pdr_type == PLDM_NUMERIC_SENSOR_PDR)
{
rc = encode_get_sensor_reading_req(instanceId, sensor_id,
rearmEventState, request);
}
else if (pdr_type == PLDM_NUMERIC_EFFECTER_PDR)
{
rc = encode_get_numeric_effecter_value_req(instanceId, sensor_id,
request);
}
else if (pdr_type == PLDM_STATE_SENSOR_PDR)
{
bitfield8_t sensor_rearm{0};
rc = encode_get_state_sensor_readings_req(instanceId, sensor_id,
sensor_rearm, 0, request);
}
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to reading sensor/effecter, rc = " << rc
<< std::endl;
return;
}
uint8_t cmd = PLDM_GET_SENSOR_READING;
if (pdr_type == PLDM_STATE_SENSOR_PDR)
{
// We use a different callback to process state sensor reading
cmd = PLDM_GET_STATE_SENSOR_READINGS;
rc = handler->registerRequest(
eid, instanceId, PLDM_PLATFORM, cmd, std::move(requestMsg),
std::move(std::bind_front(
&TerminusHandler::processStateSensorReading, this)));
if (rc)
{
std::cerr << "Failed to send reading state sensor request"
<< std::endl;
}
return;
}
else
{
if (pdr_type == PLDM_COMPACT_NUMERIC_SENSOR_PDR ||
pdr_type == PLDM_NUMERIC_SENSOR_PDR)
{
cmd = PLDM_GET_SENSOR_READING;
}
else if (pdr_type == PLDM_NUMERIC_EFFECTER_PDR)
{
cmd = PLDM_GET_NUMERIC_EFFECTER_VALUE;
}
rc = handler->registerRequest(
eid, instanceId, PLDM_PLATFORM, cmd, std::move(requestMsg),
std::move(
std::bind_front(&TerminusHandler::processSensorReading, this)));
if (rc)
{
std::cerr
<< "Failed to send reading numeric sensor/effecter request"
<< std::endl;
}
return;
}
}
void TerminusHandler::updateSensorKeys()
{
for(auto it = _state.begin(); it != _state.end(); ++it) {
sensorKeys.push_back(it->first);
}
}
void TerminusHandler::stopTerminusHandler()
{
stopTerminusPolling = true;
stopSensorsPolling();
}
void TerminusHandler::addEventMsg(uint8_t tid, uint8_t eventId,
uint8_t eventType, uint8_t eventClass)
{
if (tid != devInfo.tid)
return;
#ifdef AMPERE
if (eventId == 200)
{
stopSensorsPolling();
}
#endif
if (eventDataHndl)
eventDataHndl->addEventMsg(eventId, eventType, eventClass);
#ifdef AMPERE
if (eventId == 200)
{
// Stop hang dectection service
if (system("systemctl stop ampere-sysfw-hang-handler.service"))
{
error("Failed to call stop hand-detection service");
}
}
#endif
}
requester::Coroutine TerminusHandler::setNumericEffecterValue(uint16_t effecterId,
uint8_t effecterDataSize, const uint8_t* effecterValue)
{
uint8_t instanceId = instanceIdDb.next(eid);
Request requestMsg(sizeof(pldm_msg_hdr) + PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 3);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
size_t payloadLength = PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES;
if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT16 ||
effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT16)
{
payloadLength = PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 1;
}
if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT32 ||
effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT32)
{
payloadLength = PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 3;
}
auto rc = encode_set_numeric_effecter_value_req(instanceId, effecterId, effecterDataSize, effecterValue, request,
payloadLength);
if (rc != PLDM_SUCCESS)
{
instanceIdDb.free(eid, instanceId);
std::cerr << "Failed to set numeric effecter value, rc = " << rc
<< std::endl;
co_return rc;
}
Response responseMsg{};
rc = co_await requester::sendRecvPldmMsg(*handler, eid, requestMsg,
responseMsg);
if (rc)
{
std::cerr << "Failed to send sendRecvPldmMsg, EID=" << unsigned(eid)
<< ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_PLATFORM)
<< ", cmd= " << unsigned(PLDM_SET_NUMERIC_EFFECTER_VALUE)
<< ", rc=" << rc << std::endl;
;
co_return rc;
}
uint8_t cc = 0;
payloadLength = responseMsg.size() - sizeof(struct pldm_msg_hdr);
auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
if (response == nullptr || !payloadLength)
{
std::cerr << "No response received for sendRecvPldmMsg, EID="
<< unsigned(eid) << ", instanceId=" << unsigned(instanceId)
<< ", type=" << unsigned(PLDM_PLATFORM)
<< ", cmd= " << unsigned(PLDM_SET_NUMERIC_EFFECTER_VALUE)
<< ", rc=" << rc << std::endl;
;
co_return rc;
}
rc = decode_set_numeric_effecter_value_resp(response, payloadLength, &cc);
if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
{
std::cerr << "Failed to decode_set_numeric_effecter_value_resp"
<< ", rc=" << unsigned(rc) << " cc=" << unsigned(cc)
<< std::endl;
co_return cc;
}
co_return cc;
}
/**
* @brief Start waiting for MPro recovery from impactless update.
* @details MPro's state is read by executing ampere_request_mpro_state script.
* If FW_BOOT_OK asserts within 60s, wait for MCTP interface. If MCTP interface
* from MPro is ready within 60s, restart sensor and event polling and hang
* detection service. Otherwise, do nothing.
*/
void TerminusHandler::waitForMProRecovery()
{
std::string socket(&eidToName.second[1], 1);
std::string command = "/usr/sbin/ampere_request_mpro_state.sh";
if (!std::filesystem::exists(command))
{
error("IMPACTLESS UPDATE: Script to wait for MPro recovery does not exist");
return;
}
command += " " + socket + " ";
MProState stateRequest;
switch (mProState)
{
case MProState::MProQuiesce: /* MPro is in quiesce mode, wait for FW_BOOT_OK to deassert.
Request to read FW_BOOT_OK state */
stateRequest = MProState::MProDown;
command += "fwboot";
break;
case MProState::MProDown: /* Request to read FW_BOOT_OK state */
stateRequest = MProState::MProUp;
command += "fwboot";
break;
case MProState::MProUp: /* Request to read MCTP interface state */
stateRequest = MProState::MCTPReady;
command += "mctpinf";
break;
case MProState::MCTPReady: /* Temporarily: MPro has recovered - Return */
_timer4.setEnabled(false);
return;
case MProState::MProReady: /* MPro has recovered - Return */
_timer4.setEnabled(false);
return;
default:
error("IMPACTLESS UPDATE: Invalid Mpro State Request");
_timer4.setEnabled(false);
return;
}
std::stringstream strStream;
std::string description = "IMPACTLESS UPDATE: ";
std::string status = exec(command.c_str());
if (status.find("0") != std::string::npos) /* MPro's requested status is on */
{
if (stateRequest == MProState::MProDown)
{
if (fwUpdateFailed)
{
// MC State returns Impactless Update has failed,
// resume operation
fwUpdateFailed = false;
mProState = MProState::MProReady;
_timer4.setEnabled(false);
restartSensorAndEventPolling();
return;
}
// else: wait for FW_BOOT_OK to deassert
}
else if (stateRequest == MProState::MProUp)
{
mProState = stateRequest;
countNum = 0;
strStream << "TID " << unsigned(devInfo.tid) << " - FW_BOOT_OK asserted";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereEvent";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
}
else if (stateRequest == MProState::MCTPReady)
{
// TODO [Chau Ly]: In the future, might wait some seconds
// after MTCP interface is ready before resuming actions to MPro.
mProState = stateRequest;
mProState = MProState::MProReady;
strStream << "TID " << unsigned(devInfo.tid) << " - MPro MCTP Interface is ready";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereEvent";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
_timer4.setEnabled(false);
restartSensorAndEventPolling();
return;
}
}
else /* MPro's requested status is not on */
{
if (stateRequest == MProState::MProDown) /* FW_BOOT_OK has deasserted */
{
strStream << "TID " << unsigned(devInfo.tid) << " - FW_BOOT_OK desserted";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereEvent";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
mProState = stateRequest;
}
else
{
if (countNum >= (uint16_t)(IMPACTLESS_UPDATE_MPRO_RECOVERY_TIMEOUT_MS/IMPACTLESS_UPDATE_TIMER_INTERVAL_MS))
{
strStream << "TID " << unsigned(devInfo.tid) << " - Timeout waiting for MPro recovery";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereCritical";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
_timer4.setEnabled(false);
return;
}
countNum++;
}
}
_timer4.restartOnce(std::chrono::milliseconds(IMPACTLESS_UPDATE_TIMER_INTERVAL_MS)); //100ms
return;
}
/**
* @brief Wait to retrieve normal operation after impactless update.
* @details Acknowledge impactless firmware update to MPro by
* setting value to effecter MC Control to MPro. From now,
* start a timer to wait for MPro recovery by watching FW_BOOT_OK GPIO
* assertion and MCTP Interface from MPro.
*/
requester::Coroutine TerminusHandler::waitForImpactlessUpdateRecovery()
{
// Acknowledgement of firmware update
/*
* Bit 31:9 | Reserved
* Bit 8 | Acknowledgement of firmware update
* Bit 7:0 | 1 - Enabled (default); 3 - Shutdown (All MCs are halted).
*/
uint32_t MCControlEffecterValue = (0x00000001 | 0x00000100);
uint8_t* valuePtr = (uint8_t*)&MCControlEffecterValue;
auto rc = co_await setNumericEffecterValue(MCControlEffecterID, PLDM_EFFECTER_DATA_SIZE_UINT32, valuePtr);
if (rc != PLDM_SUCCESS)
{
// [Chau Ly] Should we restart everything immediately???
restartSensorAndEventPolling();
std::cerr << "IMPACTLESS UPDATE: Failed to acknowledge impactless update\n";
co_return rc;
}
std::stringstream strStream;
std::string description = "";
description += "IMPACTLESS UPDATE: ";
strStream << "TID " << unsigned(devInfo.tid) << " - BMC Acknowledged";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereEvent";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
mProState = MProState::MProQuiesce;
_timer4.restartOnce(std::chrono::milliseconds(IMPACTLESS_UPDATE_TIMER_INTERVAL_MS)); //100ms
co_return rc;
}
/**
* @brief Start waiting for RAS polling completion to:
* 1. Stop sensor polling
* 2. Stop event polling
* 3. Write to MC Control Effecter (effecterId = 254) to acknowledge host firmware update
*/
void TerminusHandler::waitForRASPollingFinished()
{
if (!eventDataHndl)
{
return;
}
if (eventDataHndl->areBMCRASQueuesEmpty() && eventDataHndl->areMProRASQueuesEmpty())
{
std::cerr << "DEBUG: Polling all remaining RAS is finished after "
<< unsigned(countNum*IMPACTLESS_UPDATE_TIMER_INTERVAL_MS/1000) << " seconds \n";
stopSensorsPolling();
eventDataHndl->stopEventSignalPolling();
eventDataHndl->inQuiesceMode(false);
countNum = 0;
// Stop hang dectection service
if (system("systemctl stop ampere-sysfw-hang-handler.service"))
{
error("Failed to call stop hand-detection service");
}
[[maybe_unused]] auto co = waitForImpactlessUpdateRecovery();
return;
}
else
{
// Polling RAS is not finished within timeout
if (countNum >= (uint16_t)(IMPACTLESS_UPDATE_FINISH_RAS_TIMEOUT_MS/IMPACTLESS_UPDATE_TIMER_INTERVAL_MS))
{
eventDataHndl->inQuiesceMode(false);
countNum = 0;
//Log to REDFISH
std::stringstream strStream;
std::string description = "";
description += "IMPACTLESS UPDATE: ";
strStream << "TID " << unsigned(devInfo.tid) << " - Quiesce mode FAILED, Polling RAS is not done within timer";
description += strStream.str();
if (!description.empty())
{
std::string REDFISH_MESSAGE_ID = "OpenBMC.0.1.AmpereEvent";
sd_journal_send("MESSAGE=%s", description.c_str(),
"REDFISH_MESSAGE_ID=%s",
REDFISH_MESSAGE_ID.c_str(),
"REDFISH_MESSAGE_ARGS=%s",
description.c_str(), NULL);
}
return;
}
else
{
countNum++;
}
}
_timer3.restartOnce(std::chrono::milliseconds(IMPACTLESS_UPDATE_TIMER_INTERVAL_MS)); //100ms
return;
}
/** @brief Enter quiesce mode after polling all remaining RAS events
* @details Stop hang detection service, sensor and event polling
* after finishing polling the remaining RAS events. First, start
* a timer to wait for RAS polling completion.
*/
void TerminusHandler::startQuiesceMode()
{
eventDataHndl->inQuiesceMode(true);
_timer3.restartOnce(std::chrono::milliseconds(IMPACTLESS_UPDATE_TIMER_INTERVAL_MS)); //100ms
}
/** @brief Restart sensor and event polling
*/
void TerminusHandler::restartSensorAndEventPolling()
{
startSensorsPolling();
eventDataHndl->startEventSignalPolling();
// Start hang dectection service
if (system("systemctl start ampere-sysfw-hang-handler.service"))
{
error("Failed to call call stop hand-detection service");
}
}
void TerminusHandler::createEntityAssociationMap(const PDRList& associationPDRs)
{
for (const auto& pdr : associationPDRs)
{
auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
size_t numEntities{};
pldm_entity* entities = nullptr;
PLDMEntity parent{entityPdr->container.entity_type,
entityPdr->container.entity_instance_num,
entityPdr->container.entity_container_id};
// Use the libpldm C API to parse the entity assocaition PDR
pldm_entity_association_pdr_extract(pdr.data(), pdr.size(),
&numEntities, &entities);
// The first entity is the container Entity, child starts from 1
for (size_t i = 1; i < numEntities; i++)
{
PLDMEntity child{entities[i].entity_type,
entities[i].entity_instance_num,
entities[i].entity_container_id};
// parent contains child
entityAssociationMap[parent].insert(child);
}
// Free resources
free(entities);
}
}
void TerminusHandler::createNicAndPortInventory()
{
for (const auto& [entity, childs] : entityAssociationMap)
{
if (entity.entityType != PLDM_ENTITY_NETWORK_CONTROLLER)
{
continue;
}
// Suport one chassis X multiple terminus X multiple NIC inst topology
std::string nicName = std::format("{}_NIC_{}", terminusName, entity.instNum);
std::string nicInventoryPath = chassisInventoryPath + "/" + nicName;
std::shared_ptr<NicInventory> nicInventory =
std::make_shared<NicInventory>(
bus, entity, nicName, nicInventoryPath, chassisInventoryPath);
entityToInventoryMap[entity] = nicInventory;
for (const PLDMEntity& child : childs)
{
if (child.entityType != PLDM_ENTITY_ETHERNET)
{
continue;
}
const std::string portName = std::format("Port_{}", child.instNum);
const std::string portInventoryPath = nicInventoryPath + "/" +
portName;
std::shared_ptr<PortInventory> portInventory =
std::make_shared<PortInventory>(
bus, child, portName, portInventoryPath, nicInventoryPath);
entityToInventoryMap[child] = portInventory;
}
}
}
} // namespace terminus
} // namespace pldm