| #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 |