summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/ipmid/handler.hpp106
-rw-r--r--include/ipmid/registration.hpp79
-rw-r--r--ipmid-new.cpp125
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)
{
OpenPOWER on IntegriCloud