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