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