summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVernon Mauery <vernon.mauery@linux.intel.com>2018-10-08 12:05:18 -0700
committerVernon Mauery <vernon.mauery@linux.intel.com>2019-02-25 14:27:21 -0800
commitf984a01ffd001740bed3f9207912c1e1ad41dc90 (patch)
tree6bcd99f02fbfb66e5ebe128fd6cca17a951e307c
parent240b186cf7fa639de698104d6853b73c81f4ec39 (diff)
downloadphosphor-host-ipmid-f984a01ffd001740bed3f9207912c1e1ad41dc90.tar.gz
phosphor-host-ipmid-f984a01ffd001740bed3f9207912c1e1ad41dc90.zip
ipmid: Add in Native OEM and Group OEM support
Add full support for the IPMI OEM NetFn and the Group OEM support as defined in the IPMI 2.0 specification. For now the legacy OEM router mechanism is still supported. Change-Id: I8cc999489000c6e0daf5aad3579838e6f499ba47 Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
-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