#pragma once #include "common/utils.hpp" #include "event_parser.hpp" #include "fru.hpp" #include "host-bmc/dbus_to_event_handler.hpp" #include "host-bmc/host_pdr_handler.hpp" #include "libpldmresponder/pdr.hpp" #include "libpldmresponder/pdr_utils.hpp" #include "libpldmresponder/platform_config.hpp" #include "oem_handler.hpp" #include "pldmd/handler.hpp" #include #include #include #include #include #include PHOSPHOR_LOG2_USING; namespace pldm { namespace responder { namespace platform { using generatePDR = std::function; using EffecterId = uint16_t; using DbusObjMaps = std::map>; using DbusPath = std::string; using EffecterObjs = std::vector; using EventType = uint8_t; using EventHandler = std::function; using EventHandlers = std::vector; using EventMap = std::map; using AssociatedEntityMap = std::map; class Handler : public CmdHandler { public: Handler(const pldm::utils::DBusHandler* dBusIntf, uint8_t eid, pldm::InstanceIdDb* instanceIdDb, const fs::path& pdrJsonDir, pldm_pdr* repo, HostPDRHandler* hostPDRHandler, pldm::state_sensor::DbusToPLDMEvent* dbusToPLDMEventHandler, fru::Handler* fruHandler, pldm::responder::platform_config::Handler* platformConfigHandler, pldm::requester::Handler* handler, sdeventplus::Event& event, bool buildPDRLazily = false, const std::optional& addOnHandlersMap = std::nullopt) : eid(eid), instanceIdDb(instanceIdDb), pdrRepo(repo), hostPDRHandler(hostPDRHandler), dbusToPLDMEventHandler(dbusToPLDMEventHandler), fruHandler(fruHandler), dBusIntf(dBusIntf), platformConfigHandler(platformConfigHandler), handler(handler), event(event), pdrJsonDir(pdrJsonDir), pdrCreated(false), pdrJsonsDir({pdrJsonDir}) { if (!buildPDRLazily) { generateTerminusLocatorPDR(pdrRepo); generate(*dBusIntf, pdrJsonsDir, pdrRepo); pdrCreated = true; } handlers.emplace( PLDM_GET_PDR, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->getPDR(request, payloadLength); }); handlers.emplace( PLDM_SET_NUMERIC_EFFECTER_VALUE, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->setNumericEffecterValue(request, payloadLength); }); handlers.emplace( PLDM_GET_NUMERIC_EFFECTER_VALUE, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->getNumericEffecterValue(request, payloadLength); }); handlers.emplace( PLDM_SET_STATE_EFFECTER_STATES, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->setStateEffecterStates(request, payloadLength); }); handlers.emplace( PLDM_PLATFORM_EVENT_MESSAGE, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->platformEventMessage(request, payloadLength); }); handlers.emplace( PLDM_GET_STATE_SENSOR_READINGS, [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { return this->getStateSensorReadings(request, payloadLength); }); // Default handler for PLDM Events eventHandlers[PLDM_SENSOR_EVENT].emplace_back( [this](const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) { return this->sensorEvent(request, payloadLength, formatVersion, tid, eventDataOffset); }); eventHandlers[PLDM_PDR_REPOSITORY_CHG_EVENT].emplace_back( [this](const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) { return this->pldmPDRRepositoryChgEvent( request, payloadLength, formatVersion, tid, eventDataOffset); }); // Additional OEM event handlers for PLDM events, append it to the // standard handlers if (addOnHandlersMap) { auto addOnHandlers = addOnHandlersMap.value(); for (EventMap::iterator iter = addOnHandlers.begin(); iter != addOnHandlers.end(); ++iter) { auto search = eventHandlers.find(iter->first); if (search != eventHandlers.end()) { search->second.insert(std::end(search->second), std::begin(iter->second), std::end(iter->second)); } else { eventHandlers.emplace(iter->first, iter->second); } } } } pdr_utils::Repo& getRepo() { return this->pdrRepo; } /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the * Id. If the same id is added, the previous dbusObjs will * be "over-written". * * @param[in] Id - effecter/sensor id * @param[in] dbusObj - list of D-Bus object structure and list of D-Bus * property value to attribute value * @param[in] typeId - the type id of enum */ void addDbusObjMaps( uint16_t id, std::tuple dbusObj, pldm::responder::pdr_utils::TypeId typeId = pldm::responder::pdr_utils::TypeId::PLDM_EFFECTER_ID); /** @brief Retrieve an id -> D-Bus objects mapping * * @param[in] Id - id * @param[in] typeId - the type id of enum * * @return std::tuple - * list of D-Bus object structure and list of D-Bus property value * to attribute value */ const std::tuple& getDbusObjMaps( uint16_t id, pldm::responder::pdr_utils::TypeId typeId = pldm::responder::pdr_utils::TypeId::PLDM_EFFECTER_ID) const; uint16_t getNextEffecterId() { return ++nextEffecterId; } uint16_t getNextSensorId() { return ++nextSensorId; } /** @brief Parse PDR JSONs and build PDR repository * * @param[in] dBusIntf - The interface object * @param[in] dir - directory housing platform specific PDR JSON files * @param[in] repo - instance of concrete implementation of Repo */ void generate(const pldm::utils::DBusHandler& dBusIntf, const std::vector& dir, pldm::responder::pdr_utils::Repo& repo); /** @brief Parse PDR JSONs and build state effecter PDR repository * * @param[in] json - platform specific PDR JSON files * @param[in] repo - instance of state effecter implementation of Repo */ void generateStateEffecterRepo(const pldm::utils::Json& json, pldm::responder::pdr_utils::Repo& repo); /** @brief map of PLDM event type to EventHandlers * */ EventMap eventHandlers; /* @brief Method to set the oem platform handler in platform handler class * * @param[in] handler - oem platform handler */ inline void setOemPlatformHandler(pldm::responder::oem_platform::Handler* handler) { oemPlatformHandler = handler; } /* @brief Method to register event handlers * * @param[in] handler - oem event handlers */ inline void registerEventHandlers(EventType eventId, EventHandlers handlers) { // Try to emplace the eventId with an empty vector if it doesn't exist auto [iter, inserted] = eventHandlers.try_emplace(eventId, EventHandlers{}); for (const auto& handler : handlers) { iter->second.emplace_back(handler); } } /** @brief Handler for GetPDR * * @param[in] request - Request message payload * @param[in] payloadLength - Request payload length * @param[out] Response - Response message written here */ Response getPDR(const pldm_msg* request, size_t payloadLength); /** @brief Handler for setNumericEffecterValue * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @return Response - PLDM Response message */ Response setNumericEffecterValue(const pldm_msg* request, size_t payloadLength); /** @brief Handler for getNumericEffecterValue * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @return Response - PLDM Response message */ Response getNumericEffecterValue(const pldm_msg* request, size_t payloadLength); /** @brief Handler for getStateSensorReadings * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @return Response - PLDM Response message */ Response getStateSensorReadings(const pldm_msg* request, size_t payloadLength); /** @brief Handler for setStateEffecterStates * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @return Response - PLDM Response message */ Response setStateEffecterStates(const pldm_msg* request, size_t payloadLength); /** @brief Handler for PlatformEventMessage * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @return Response - PLDM Response message */ Response platformEventMessage(const pldm_msg* request, size_t payloadLength); /** @brief Handler for event class Sensor event * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @param[in] formatVersion - Version of the event format * @param[in] tid - Terminus ID of the event's originator * @param[in] eventDataOffset - Offset of the event data in the request * message * @return PLDM completion code */ int sensorEvent(const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset); /** @brief Handler for pldmPDRRepositoryChgEvent * * @param[in] request - Request message * @param[in] payloadLength - Request payload length * @param[in] formatVersion - Version of the event format * @param[in] tid - Terminus ID of the event's originator * @param[in] eventDataOffset - Offset of the event data in the request * message * @return PLDM completion code */ int pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset); /** @brief Handler for extracting the PDR handles from changeEntries * * @param[in] changeEntryData - ChangeEntry data from changeRecord * @param[in] changeEntryDataSize - total size of changeEntryData * @param[in] numberOfChangeEntries - total number of changeEntries to * extract * @param[out] pdrRecordHandles - std::vector where the extracted PDR * handles are placed * @return PLDM completion code */ int getPDRRecordHandles(const ChangeEntry* changeEntryData, size_t changeEntryDataSize, size_t numberOfChangeEntries, PDRRecordHandles& pdrRecordHandles); /** @brief Function to set the effecter requested by pldm requester * @param[in] dBusIntf - The interface object * @param[in] effecterId - Effecter ID sent by the requester to act on * @param[in] stateField - The state field data for each of the states, * equal to composite effecter count in number * @return - Success or failure in setting the states. Returns failure in * terms of PLDM completion codes if at least one state fails to be set */ template int setStateEffecterStatesHandler( const DBusInterface& dBusIntf, uint16_t effecterId, const std::vector& stateField) { using namespace pldm::responder::pdr; using namespace pldm::utils; using StateSetNum = uint8_t; state_effecter_possible_states* states = nullptr; pldm_state_effecter_pdr* pdr = nullptr; uint8_t compEffecterCnt = stateField.size(); std::unique_ptr stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy); if (!stateEffecterPdrRepo) { throw std::runtime_error( "Failed to instantiate state effecter PDR repository"); } pldm::responder::pdr_utils::Repo stateEffecterPDRs( stateEffecterPdrRepo.get()); getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR); if (stateEffecterPDRs.empty()) { error("Failed to get record by PDR type"); return PLDM_PLATFORM_INVALID_EFFECTER_ID; } pldm::responder::pdr_utils::PdrEntry pdrEntry{}; auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry); while (pdrRecord) { pdr = reinterpret_cast(pdrEntry.data); if (pdr->effecter_id != effecterId) { pdr = nullptr; pdrRecord = stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry); continue; } states = reinterpret_cast( pdr->possible_states); if (compEffecterCnt > pdr->composite_effecter_count) { error( "The requester sent wrong composite effecter count '{COMPOSITE_EFFECTER_COUNT}' for the effecter ID '{EFFECTERID}'.", "COMPOSITE_EFFECTER_COUNT", compEffecterCnt, "EFFECTERID", effecterId); return PLDM_ERROR_INVALID_DATA; } break; } if (!pdr) { return PLDM_PLATFORM_INVALID_EFFECTER_ID; } int rc = PLDM_SUCCESS; try { const auto& [dbusMappings, dbusValMaps] = effecterDbusObjMaps.at(effecterId); for (uint8_t currState = 0; currState < compEffecterCnt; ++currState) { std::vector allowed{}; // computation is based on table 79 from DSP0248 v1.1.1 uint8_t bitfieldIndex = stateField[currState].effecter_state / 8; uint8_t bit = stateField[currState].effecter_state - (8 * bitfieldIndex); if (states->possible_states_size < bitfieldIndex || !(states->states[bitfieldIndex].byte & (1 << bit))) { error( "Invalid state set value for effecter ID '{EFFECTERID}', effecter state '{EFFECTER_STATE}', composite effecter ID '{COMPOSITE_EFFECTER_ID}' and path '{PATH}'.", "EFFECTERID", effecterId, "EFFECTER_STATE", stateField[currState].effecter_state, "COMPOSITE_EFFECTER_COUNT", currState, "PATH", dbusMappings[currState].objectPath); rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE; break; } const DBusMapping& dbusMapping = dbusMappings[currState]; const pldm::responder::pdr_utils::StatestoDbusVal& dbusValToMap = dbusValMaps[currState]; if (stateField[currState].set_request == PLDM_REQUEST_SET) { try { dBusIntf.setDbusProperty( dbusMapping, dbusValToMap.at( stateField[currState].effecter_state)); } catch (const std::exception& e) { error( "Failed to set property '{PROPERTY}' of interface '{INTERFACE}' at path '{PATH}', error - {ERROR}", "PROPERTY", dbusMapping.propertyName, "DBUS_INTF", dbusMapping.interface, "DBUS_OBJ_PATH", dbusMapping.objectPath, "ERROR", e); return PLDM_ERROR; } } uint8_t* nextState = reinterpret_cast(states) + sizeof(state_effecter_possible_states) - sizeof(states->states) + (states->possible_states_size * sizeof(states->states)); states = reinterpret_cast( nextState); } } catch (const std::out_of_range& e) { error( "The effecter ID '{EFFECTERID}' does not exist, error - {ERROR}.", "EFFECTERID", effecterId, "ERROR", e); } return rc; } /** @brief Build BMC Terminus Locator PDR * * @param[in] repo - instance of concrete implementation of Repo */ void generateTerminusLocatorPDR(pldm::responder::pdr_utils::Repo& repo); /** @brief Get std::map associated with the entity * key: object path * value: pldm_entity * * @return std::map */ inline const AssociatedEntityMap& getAssociateEntityMap() const { if (fruHandler == nullptr) { throw InternalFailure(); } return fruHandler->getAssociateEntityMap(); } /** @brief update the sensor cache map * @param[in] sensorId - sensor id that needs an update * @param[in] sensorRearm - rearm value within the sensor * @param[in] value - value that needs to be cached */ inline void updateSensorCache(pldm::pdr::SensorID sensorId, size_t sensorRearm, uint8_t value) { if (dbusToPLDMEventHandler) { dbusToPLDMEventHandler->updateSensorCacheMaps(sensorId, sensorRearm, value); } } /** @brief process the actions that needs to be performed after a GetPDR * call is received * @param[in] source - sdeventplus event source */ void _processPostGetPDRActions(sdeventplus::source::EventBase& source); /** @brief Method for setEventreceiver */ void setEventReceiver(); private: uint8_t eid; InstanceIdDb* instanceIdDb; pdr_utils::Repo pdrRepo; uint16_t nextEffecterId{}; uint16_t nextSensorId{}; DbusObjMaps effecterDbusObjMaps{}; DbusObjMaps sensorDbusObjMaps{}; HostPDRHandler* hostPDRHandler; pldm::state_sensor::DbusToPLDMEvent* dbusToPLDMEventHandler; fru::Handler* fruHandler; const pldm::utils::DBusHandler* dBusIntf; pldm::responder::oem_platform::Handler* oemPlatformHandler = nullptr; pldm::responder::platform_config::Handler* platformConfigHandler; pldm::requester::Handler* handler; sdeventplus::Event& event; fs::path pdrJsonDir; bool pdrCreated; std::vector pdrJsonsDir; std::unique_ptr deferredGetPDREvent; }; /** @brief Function to check if a sensor falls in OEM range * A sensor is considered to be oem if either of entity * type or state set or both falls in oem range * * @param[in] handler - the interface object * @param[in] sensorId - sensor id * @param[in] sensorRearmCount - sensor rearm count * @param[out] compSensorCnt - composite sensor count * @param[out] entityType - entity type * @param[out] entityInstance - entity instance number * @param[out] stateSetId - state set id * @param[out] containerId - container id * * @return true if the sensor is OEM. All out parameters are invalid * for a non OEM sensor */ bool isOemStateSensor(Handler& handler, uint16_t sensorId, uint8_t sensorRearmCount, uint8_t& compSensorCnt, uint16_t& entityType, uint16_t& entityInstance, uint16_t& stateSetId, uint16_t& containerId); /** @brief Function to check if an effecter falls in OEM range * An effecter is considered to be oem if either of entity * type or state set or both falls in oem range * * @param[in] handler - the interface object * @param[in] effecterId - effecter id * @param[in] compEffecterCnt - composite effecter count * @param[out] entityType - entity type * @param[out] entityInstance - entity instance number * @param[out] stateSetId - state set id * * @return true if the effecter is OEM. All out parameters are invalid * for a non OEM effecter */ bool isOemStateEffecter(Handler& handler, uint16_t effecterId, uint8_t compEffecterCnt, uint16_t& entityType, uint16_t& entityInstance, uint16_t& stateSetId); } // namespace platform } // namespace responder } // namespace pldm