diff options
-rw-r--r-- | include/ipmid/handler.hpp | 106 | ||||
-rw-r--r-- | include/ipmid/registration.hpp | 79 | ||||
-rw-r--r-- | ipmid-new.cpp | 125 |
3 files changed, 309 insertions, 1 deletions
diff --git a/include/ipmid/handler.hpp b/include/ipmid/handler.hpp index 203dcad..0c65436 100644 --- a/include/ipmid/handler.hpp +++ b/include/ipmid/handler.hpp @@ -354,6 +354,94 @@ class IpmiHandler<ipmid_callback_t> final : public HandlerBase }; /** + * @brief Legacy IPMI OEM handler class + * + * Legacy IPMI OEM handlers will resolve into this class, which will behave the + * same way as the legacy IPMI queue, passing in a big buffer for the request + * and a big buffer for the response. + * + * As soon as all the handlers have been rewritten, this class will be marked as + * deprecated and eventually removed. + */ +template <> +class IpmiHandler<oem::Handler> final : public HandlerBase +{ + public: + explicit IpmiHandler(const oem::Handler& handler) : handler_(handler) + { + } + + private: + oem::Handler handler_; + + /** @brief call the registered handler with the request + * + * This is called from the running queue context after it has already + * created a request object that contains all the information required to + * execute the ipmi command. This function will return the response object + * pointer that owns the response object that will ultimately get sent back + * to the requester. + * + * Because this is the legacy variety of IPMI handler, this function does + * not really have to do much other than pass the payload to the callback + * and return response to the caller. + * + * @param request a shared_ptr to a Request object + * + * @return a shared_ptr to a Response object + */ + message::Response::ptr + executeCallback(message::Request::ptr request) override + { + message::Response::ptr response = request->makeResponse(); + size_t len = request->payload.size(); + // allocate a big response buffer here + response->payload.resize( + getChannelMaxTransferSize(request->ctx->channel)); + + Cc ccRet{ccSuccess}; + try + { + ccRet = handler_(request->ctx->cmd, request->payload.data(), + response->payload.data(), &len); + } + catch (const std::exception& e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Legacy OEM Handler failed to catch exception", + phosphor::logging::entry("EXCEPTION=%s", e.what()), + phosphor::logging::entry("NETFN=%x", request->ctx->netFn), + phosphor::logging::entry("CMD=%x", request->ctx->cmd)); + return errorResponse(request, ccUnspecifiedError); + } + catch (...) + { + std::exception_ptr eptr; + try + { + eptr = std::current_exception(); + if (eptr) + { + std::rethrow_exception(eptr); + } + } + catch (const std::exception& e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Handler failed to catch exception", + phosphor::logging::entry("EXCEPTION=%s", e.what()), + phosphor::logging::entry("NETFN=%x", request->ctx->netFn), + phosphor::logging::entry("CMD=%x", request->ctx->cmd)); + return errorResponse(request, ccUnspecifiedError); + } + } + response->cc = ccRet; + response->payload.resize(len); + return response; + } +}; + +/** * @brief create a legacy IPMI handler class and return a shared_ptr * * The queue uses a map of pointers to do the lookup. This function returns the @@ -371,6 +459,24 @@ inline auto makeLegacyHandler(const ipmid_callback_t& handler) return ptr; } +/** + * @brief create a legacy IPMI OEM handler class and return a shared_ptr + * + * The queue uses a map of pointers to do the lookup. This function returns the + * shared_ptr that owns the Handler object. + * + * This is called internally via the Router::registerHandler method. + * + * @param handler the function pointer to the callback + * + * @return A shared_ptr to the created handler object + */ +inline auto makeLegacyHandler(oem::Handler&& handler) +{ + HandlerBase::ptr ptr( + new IpmiHandler<oem::Handler>(std::forward<oem::Handler>(handler))); + return ptr; +} #endif // ALLOW_DEPRECATED_API /** diff --git a/include/ipmid/registration.hpp b/include/ipmid/registration.hpp index 151aca1..af89bc8 100644 --- a/include/ipmid/registration.hpp +++ b/include/ipmid/registration.hpp @@ -27,6 +27,11 @@ namespace impl // IPMI command handler registration implementation bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv, ::ipmi::HandlerBase::ptr handler); +bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv, + ::ipmi::HandlerBase::ptr handler); +bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, + ::ipmi::HandlerBase::ptr handler); + } // namespace impl /** @@ -53,6 +58,80 @@ bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv, return impl::registerHandler(prio, netFn, cmd, priv, h); } +/** + * @brief register a IPMI OEM group handler + * + * From IPMI 2.0 spec Network Function Codes Table (Row 2Ch): + * The first data byte position in requests and responses under this network + * function identifies the defining body that specifies command functionality. + * Software assumes that the command and completion code field positions will + * hold command and completion code values. + * + * The following values are used to identify the defining body: + * 00h PICMG - PCI Industrial Computer Manufacturer’s Group. (www.picmg.com) + * 01h DMTF Pre-OS Working Group ASF Specification (www.dmtf.org) + * 02h Server System Infrastructure (SSI) Forum (www.ssiforum.org) + * 03h VITA Standards Organization (VSO) (www.vita.com) + * DCh DCMI Specifications (www.intel.com/go/dcmi) + * all other Reserved + * + * When this network function is used, the ID for the defining body occupies + * the first data byte in a request, and the second data byte (following the + * completion code) in a response. + * + * @tparam Handler - implicitly specified callback function type + * @param prio - priority at which to register; see api.hpp + * @param netFn - the IPMI net function number to register + * @param cmd - the IPMI command number to register + * @param priv - the IPMI user privilige required for this command + * @param handler - the callback function that will handle this request + * + * @return bool - success of registering the handler + * + */ +template <typename Handler> +void registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv, + Handler&& handler) +{ + auto h = ipmi::makeHandler(handler); + impl::registerGroupHandler(prio, group, cmd, priv, h); +} + +/** + * @brief register a IPMI OEM IANA handler + * + * From IPMI spec Network Function Codes Table (Row 2Eh): + * The first three data bytes of requests and responses under this network + * function explicitly identify the OEM or non-IPMI group that specifies the + * command functionality. While the OEM or non-IPMI group defines the + * functional semantics for the cmd and remaining data fields, the cmd field + * is required to hold the same value in requests and responses for a given + * operation in order to be supported under the IPMI message handling and + * transport mechanisms. + * + * When this network function is used, the IANA Enterprise Number for the + * defining body occupies the first three data bytes in a request, and the + * first three data bytes following the completion code position in a + * response. + * + * @tparam Handler - implicitly specified callback function type + * @param prio - priority at which to register; see api.hpp + * @param netFn - the IPMI net function number to register + * @param cmd - the IPMI command number to register + * @param priv - the IPMI user privilige required for this command + * @param handler - the callback function that will handle this request + * + * @return bool - success of registering the handler + * + */ +template <typename Handler> +void registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, + Handler&& handler) +{ + auto h = ipmi::makeHandler(handler); + impl::registerOemHandler(prio, iana, cmd, priv, h); +} + } // namespace ipmi #ifdef ALLOW_DEPRECATED_API diff --git a/ipmid-new.cpp b/ipmid-new.cpp index fb57666..a9aff36 100644 --- a/ipmid-new.cpp +++ b/ipmid-new.cpp @@ -125,6 +125,16 @@ static std::unordered_map<unsigned int, /* key is NetFn/Cmd */ HandlerTuple> handlerMap; +/* special map for decoding Group registered commands (NetFn 2Ch) */ +static std::unordered_map<unsigned int, /* key is Group/Cmd (NetFn is 2Ch) */ + HandlerTuple> + groupHandlerMap; + +/* special map for decoding OEM registered commands (NetFn 2Eh) */ +static std::unordered_map<unsigned int, /* key is Iana/Cmd (NetFn is 2Eh) */ + HandlerTuple> + oemHandlerMap; + namespace impl { /* common function to register all standard IPMI handlers */ @@ -152,6 +162,42 @@ bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv, return false; } +/* common function to register all Group IPMI handlers */ +bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv, + HandlerBase::ptr handler) +{ + // create key and value for this handler + unsigned int netFnCmd = makeCmdKey(group, cmd); + HandlerTuple item(prio, priv, handler); + + // consult the handler map and look for a match + auto& mapCmd = groupHandlerMap[netFnCmd]; + if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio) + { + mapCmd = item; + return true; + } + return false; +} + +/* common function to register all OEM IPMI handlers */ +bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, + HandlerBase::ptr handler) +{ + // create key and value for this handler + unsigned int netFnCmd = makeCmdKey(iana, cmd); + HandlerTuple item(prio, priv, handler); + + // consult the handler map and look for a match + auto& mapCmd = oemHandlerMap[netFnCmd]; + if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio) + { + mapCmd = item; + return true; + } + return false; +} + } // namespace impl message::Response::ptr executeIpmiCommandCommon( @@ -187,10 +233,56 @@ message::Response::ptr executeIpmiCommandCommon( return errorResponse(request, ccInvalidCommand); } +message::Response::ptr executeIpmiGroupCommand(message::Request::ptr request) +{ + // look up the group for this request + Group group; + if (0 != request->unpack(group)) + { + return errorResponse(request, ccReqDataLenInvalid); + } + // The handler will need to unpack group as well; we just need it for lookup + request->payload.reset(); + message::Response::ptr response = + executeIpmiCommandCommon(groupHandlerMap, group, request); + // if the handler should add the group; executeIpmiCommandCommon does not + if (response->cc != ccSuccess && response->payload.size() == 0) + { + response->pack(group); + } + return response; +} + +message::Response::ptr executeIpmiOemCommand(message::Request::ptr request) +{ + // look up the iana for this request + Iana iana; + if (0 != request->unpack(iana)) + { + return errorResponse(request, ccReqDataLenInvalid); + } + request->payload.reset(); + message::Response::ptr response = + executeIpmiCommandCommon(oemHandlerMap, iana, request); + // if the handler should add the iana; executeIpmiCommandCommon does not + if (response->cc != ccSuccess && response->payload.size() == 0) + { + response->pack(iana); + } + return response; +} + message::Response::ptr executeIpmiCommand(message::Request::ptr request) { NetFn netFn = request->ctx->netFn; - // TODO: handler OEM and group OEM commands here + if (netFnGroup == netFn) + { + return executeIpmiGroupCommand(request); + } + else if (netFnOem == netFn) + { + return executeIpmiOemCommand(request); + } return executeIpmiCommandCommon(handlerMap, netFn, request); } @@ -365,6 +457,37 @@ void ipmi_register_callback(ipmi_netfn_t netFn, ipmi_cmd_t cmd, ipmi::impl::registerHandler(ipmi::prioOpenBmcBase, netFn, cmd, realPriv, h); } +namespace oem +{ + +class LegacyRouter : public oem::Router +{ + public: + virtual ~LegacyRouter() + { + } + + /// Enable message routing to begin. + void activate() override + { + } + + void registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler) override + { + auto h = ipmi::makeLegacyHandler(std::forward<Handler>(handler)); + ipmi::impl::registerOemHandler(ipmi::prioOpenBmcBase, oen, cmd, + ipmi::Privilege::Admin, h); + } +}; +static LegacyRouter legacyRouter; + +Router* mutableRouter() +{ + return &legacyRouter; +} + +} // namespace oem + /* legacy alternative to executionEntry */ void handleLegacyIpmiCommand(sdbusplus::message::message& m) { |