#include "config.h" #include "host_pdr_handler.hpp" #include "libpldm/requester/pldm.h" #include #include #include namespace pldm { using namespace pldm::utils; using namespace sdbusplus::bus::match::rules; using Json = nlohmann::json; namespace fs = std::filesystem; constexpr auto fruJson = "host_frus.json"; const Json emptyJson{}; const std::vector emptyJsonList{}; 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, Requester& requester, pldm::requester::Handler* handler, bool verbose) : mctp_fd(mctp_fd), mctp_eid(mctp_eid), event(event), repo(repo), stateSensorHandler(eventsJsonsDir), entityTree(entityTree), bmcEntityTree(bmcEntityTree), requester(requester), handler(handler), verbose(verbose) { 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()) { std::cerr << "Parsing Host FRU json file failed" << std::endl; } 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) { std::cerr << "Parsing Host FRU json file failed, exception = " << e.what() << std::endl; } } hostOffMatch = std::make_unique( pldm::utils::DBusHandler::getBus(), propertiesChanged("/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host"), [this, repo, entityTree, bmcEntityTree](sdbusplus::message::message& 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(value); if (propVal == "xyz.openbmc_project.State.Host.HostState.Off") { pldm_pdr_remove_remote_pdrs(repo); pldm_entity_association_tree_destroy_root(entityTree); pldm_entity_association_tree_copy_root(bmcEntityTree, entityTree); this->sensorMap.clear(); } } }); } void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles) { pdrRecordHandles.clear(); 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( 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 requestMsg(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES); auto request = reinterpret_cast(requestMsg.data()); uint32_t recordHandle{}; if (!nextRecordHandle) { if (!pdrRecordHandles.empty()) { recordHandle = pdrRecordHandles.front(); pdrRecordHandles.pop_front(); } } else { recordHandle = nextRecordHandle; } auto instanceId = requester.getInstanceId(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) { requester.markFree(mctp_eid, instanceId); std::cerr << "Failed to encode_get_pdr_req, rc = " << rc << std::endl; return; } if (verbose) { std::cout << "Sending Msg:" << std::endl; printBuffer(requestMsg, verbose); } rc = handler->registerRequest( mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR, std::move(requestMsg), std::move(std::bind_front(&HostPDRHandler::processHostPDRs, this))); if (rc) { std::cerr << "Failed to send the GetPDR request to Host \n"; } } int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry, pdr::EventState state) { auto rc = stateSensorHandler.eventAction(entry, state); if (rc != PLDM_SUCCESS) { std::cerr << "Failed to fetch and update D-bus property, rc = " << rc << std::endl; return rc; } return PLDM_SUCCESS; } bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent) { auto found = parents.find(type); if (found != parents.end()) { parent.entity_type = found->second.entity_type; parent.entity_instance_num = found->second.entity_instance_num; return true; } return false; } void HostPDRHandler::mergeEntityAssociations(const std::vector& pdr) { size_t numEntities{}; pldm_entity* entities = nullptr; bool merged = false; auto entityPdr = reinterpret_cast( const_cast(pdr.data()) + sizeof(pldm_pdr_hdr)); pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities, &entities); for (size_t i = 0; i < numEntities; ++i) { pldm_entity parent{}; if (getParent(entities[i].entity_type, parent)) { auto node = pldm_entity_association_tree_find(entityTree, &parent); if (node) { pldm_entity_association_tree_add(entityTree, &entities[i], 0xFFFF, node, entityPdr->association_type); merged = true; } } } free(entities); if (merged) { // Update our PDR repo with the merged entity association PDRs pldm_entity_association_pdr_add(entityTree, repo, true); } } void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector&& 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 eventDataOps{PLDM_RECORDS_ADDED}; std::vector numsOfChangeEntries(1); std::vector> 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 eventDataVec{}; eventDataVec.resize(maxSize); auto eventData = reinterpret_cast( 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) { std::cerr << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = " << rc << std::endl; return; } auto instanceId = requester.getInstanceId(mctp_eid); std::vector requestMsg(sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + actualSize); auto request = reinterpret_cast(requestMsg.data()); rc = encode_platform_event_message_req( instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(), actualSize, request, actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES); if (rc != PLDM_SUCCESS) { requester.markFree(mctp_eid, instanceId); std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc << std::endl; return; } auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) { if (response == nullptr || !respMsgLen) { std::cerr << "Failed to receive response for the PDR repository " "changed event" << "\n"; return; } uint8_t completionCode{}; uint8_t status{}; auto responsePtr = reinterpret_cast(response); auto rc = decode_platform_event_message_resp( responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &completionCode, &status); if (rc || completionCode) { std::cerr << "Failed to decode_platform_event_message_resp: " << "rc=" << rc << ", cc=" << static_cast(completionCode) << std::endl; } }; rc = handler->registerRequest( mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PDR_REPOSITORY_CHG_EVENT, std::move(requestMsg), std::move(platformEventMessageResponseHandler)); if (rc) { std::cerr << "Failed to send the PDR repository changed event request" << "\n"; } } void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs, const TLPDRMap& tlpdrInfo) { for (const auto& pdr : stateSensorPDRs) { SensorEntry sensorEntry{}; const auto& [terminusHandle, sensorID, sensorInfo] = responder::pdr_utils::parseStateSensorPDR(pdr); sensorEntry.sensorID = sensorID; try { sensorEntry.terminusID = 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 TLPDRMap tlpdrInfo{}; uint32_t nextRecordHandle{}; 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"; return; } auto rc = decode_get_pdr_resp( response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode, &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount, nullptr, 0, &transferCRC); std::vector responsePDRMsg; responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr)); memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr)); if (verbose) { std::cout << "Receiving Msg:" << std::endl; printBuffer(responsePDRMsg, verbose); } if (rc != PLDM_SUCCESS) { std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc << std::endl; return; } else { std::vector 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=" << rc << ", cc=" << static_cast(completionCode) << std::endl; return; } else { auto pdrHdr = reinterpret_cast(pdr.data()); if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION) { this->mergeEntityAssociations(pdr); merged = true; } else { if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR) { auto tlpdr = reinterpret_cast( pdr.data()); tlpdrInfo.emplace( static_cast( tlpdr->terminus_handle), static_cast(tlpdr->tid)); } else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR) { stateSensorPDRs.emplace_back(pdr); } pldm_pdr_add(repo, pdr.data(), respCount, 0, true); } } } if (!nextRecordHandle) { /*received last record*/ this->parseStateSensorPDRs(stateSensorPDRs, tlpdrInfo); stateSensorPDRs.clear(); tlpdrInfo.clear(); if (merged) { merged = false; deferredPDRRepoChgEvent = std::make_unique( event, std::bind( std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)), this, std::placeholders::_1)); } } else { deferredFetchPDREvent = std::make_unique( 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(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(); } this->getHostPDR(nextRecordHandle); } } // namespace pldm