ipmid: Compiler-generated unpacking and packing of messages
handler.hpp has the templated wrapping bits for ipmi command handler
callbacks implemented.
message.hpp has the serialization/deserialization of the ipmi data
stream into packed tuples for functions.
message/pack.hpp and message/unpack.hpp contain the actual serialization
and deserialization of types.
Change-Id: If997f8768c8488ab6ac022526a5ef9a1bce57fcb
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/Makefile.am b/Makefile.am
index bb7bdbf..890b9f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,8 @@
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
-DBOOST_ERROR_CODE_HEADER_ONLY \
-DBOOST_SYSTEM_NO_DEPRECATED \
+ -DBOOST_COROUTINES_NO_DEPRECATION_WARNING \
+ -DBOOST_ASIO_DISABLE_THREADS \
-DBOOST_ALL_NO_LIB
ipmid_CXXFLAGS = $(COMMON_CXX)
diff --git a/include/Makefile.am b/include/Makefile.am
index 50f54d0..4c0e899 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,4 +1,11 @@
nobase_include_HEADERS = \
+ ipmid/api.hpp \
+ ipmid/handler.hpp \
+ ipmid/message.hpp \
+ ipmid/message/pack.hpp \
+ ipmid/message/types.hpp \
+ ipmid/message/unpack.hpp \
+ ipmid/registration.hpp \
ipmid/api.h \
ipmid/iana.hpp \
ipmid/oemopenbmc.hpp \
diff --git a/include/ipmid/api.hpp b/include/ipmid/api.hpp
new file mode 100644
index 0000000..47dc04d
--- /dev/null
+++ b/include/ipmid/api.hpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#define ALLOW_DEPRECATED_API 1
+
+#include <ipmid/iana.hpp>
+#include <ipmid/message/types.hpp>
+#include <optional>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+/* NOTE:
+ *
+ * This is intended for native C++ use. For the legacy C api, include
+ * ipmid-api.h for a reduced functionality. Note that the C api is now marked
+ * as deprecated and will be removed once all the internal users of it have
+ * been updated to use the new C++ api.
+ */
+
+namespace ipmi
+{
+
+using Iana = oem::Number;
+
+using Group = uint8_t;
+constexpr Group groupPICMG = 0x00;
+constexpr Group groupDMTG = 0x01;
+constexpr Group groupSSI = 0x02;
+constexpr Group groupVSO = 0x03;
+constexpr Group groupDCMI = 0xDC;
+
+/*
+ * Set the priority as the lowest number that is necessary so
+ * it is possible that others can override it if desired.
+ * This may be linked to what level of integration the handler
+ * is being created at.
+ */
+constexpr int prioOpenBmcBase = 10;
+constexpr int prioOemBase = 20;
+constexpr int prioOdmBase = 30;
+constexpr int prioCustomBase = 40;
+constexpr int prioMax = 50;
+
+/*
+ * Channel IDs pulled from the IPMI 2.0 specification
+ */
+constexpr int channelPrimaryIpmb = 0x00;
+// 0x01-0x0B Implementation specific
+// Implementation specific channel numbers are specified
+// by a configuration file external to ipmid
+// 0x0C-0x0D reserved
+constexpr int channelCurrentIface = 0x0E; // 'Present I/F'
+constexpr int channelSystemIface = 0x0F;
+
+/*
+ * Specifies the minimum privilege level required to execute the command
+ * This means the command can be executed at a given privilege level or higher
+ * privilege level. Those commands which can be executed via system interface
+ * only should use SYSTEM_INTERFACE
+ */
+enum class Privilege : uint8_t
+{
+ None = 0x00,
+ Callback,
+ User,
+ Operator,
+ Admin,
+ Oem,
+};
+
+// IPMI Net Function number as specified by IPMI V2.0 spec.
+using NetFn = uint8_t;
+
+// IPMI Command for a Net Function number as specified by IPMI V2.0 spec.
+using Cmd = uint8_t;
+
+// ipmi function return the status code
+using Cc = uint8_t;
+
+// These are the command network functions, the response
+// network functions are the function + 1. So to determine
+// the proper network function which issued the command
+// associated with a response, subtract 1.
+// Note: these will be left shifted when combined with the LUN
+constexpr NetFn netFnChassis = 0x00;
+constexpr NetFn netFnBridge = 0x02;
+constexpr NetFn netFnSensor = 0x04;
+constexpr NetFn netFnApp = 0x06;
+constexpr NetFn netFnFirmware = 0x08;
+constexpr NetFn netFnStorage = 0x0A;
+constexpr NetFn netFnTransport = 0x0C;
+// reserved 0Eh..28h
+constexpr NetFn netFnGroup = 0x2C;
+constexpr NetFn netFnOem = 0x2E;
+constexpr NetFn netFnOemOne = 0x30;
+constexpr NetFn netFnOemTwo = 0x32;
+constexpr NetFn netFnOemThree = 0x34;
+constexpr NetFn netFnOemFour = 0x36;
+constexpr NetFn netFnOemFive = 0x38;
+constexpr NetFn netFnOemSix = 0x3A;
+constexpr NetFn netFnOemSeven = 0x3C;
+constexpr NetFn netFnOemEight = 0x3E;
+
+// IPMI commands for net functions. Callbacks using this should be careful to
+// parse arguments to the sub-functions and can take advantage of the built-in
+// message handling mechanism to create custom routing
+constexpr Cmd cmdWildcard = 0xFF;
+
+// IPMI standard completion codes specified by the IPMI V2.0 spec.
+//
+// This might have been an enum class, but that would make it hard for
+// OEM- and command-specific completion codes to be added elsewhere.
+//
+// Custom completion codes can be defined in individual modules for
+// command specific errors in the 0x80-0xBE range
+//
+// Alternately, OEM completion codes are in the 0x01-0x7E range
+constexpr Cc ccSuccess = 0x00;
+constexpr Cc ccBusy = 0xC0;
+constexpr Cc ccInvalidCommand = 0xC1;
+constexpr Cc ccInvalidCommandOnLun = 0xC2;
+constexpr Cc ccTimeout = 0xC2;
+constexpr Cc ccOutOfSpace = 0xC2;
+constexpr Cc ccInvalidReservationId = 0xC5;
+constexpr Cc ccReqDataTruncated = 0xC6;
+constexpr Cc ccReqDataLenInvalid = 0xC7;
+constexpr Cc ccReqDataLenExceeded = 0xC8;
+constexpr Cc ccParmOutOfRange = 0xC9;
+constexpr Cc ccRetBytesUnavailable = 0xCA;
+constexpr Cc ccSensorInvalid = 0xCB;
+constexpr Cc ccInvalidFieldRequest = 0xCC;
+constexpr Cc ccIllegalCommand = 0xCD;
+constexpr Cc ccResponseError = 0xCE;
+constexpr Cc ccDuplicateRequest = 0xCF;
+constexpr Cc ccCmdFailSdrMode = 0xD0;
+constexpr Cc ccCmdFailFwUpdMode = 0xD1;
+constexpr Cc ccCmdFailInitAgent = 0xD2;
+constexpr Cc ccDestinationUnavailable = 0xD3;
+constexpr Cc ccInsufficientPrivilege = 0xD4;
+constexpr Cc ccCommandNotAvailable = 0xD5;
+constexpr Cc ccCommandDisabled = 0xD6;
+constexpr Cc ccUnspecifiedError = 0xFF;
+
+/* ipmi often has two return types:
+ * 1. Failure: CC is non-zero; no trailing data
+ * 2. Success: CC is zero; trailing data (usually a fixed type)
+ *
+ * using ipmi::response(cc, ...), it will automatically always pack
+ * the correct type for the response without having to explicitly type out all
+ * the parameters that the function would return.
+ *
+ * To enable this feature, you just define the ipmi function as returning an
+ * ipmi::RspType which has the optional trailing data built in, with your types
+ * defined as parameters.
+ */
+
+template <typename... RetTypes>
+using RspType = std::tuple<ipmi::Cc, std::optional<std::tuple<RetTypes...>>>;
+
+/**
+ * @brief helper function to create an IPMI response tuple
+ *
+ * IPMI handlers all return a tuple with two parts: a completion code and an
+ * optional tuple containing the rest of the data to return. This helper
+ * function makes it easier by constructing that out of an arbitrary number of
+ * arguments.
+ *
+ * @param cc - the completion code for the response
+ * @param args... - the optional list of values to return
+ *
+ * @return a standard IPMI return type (as described above)
+ */
+template <typename... Args>
+static inline auto response(ipmi::Cc cc, Args&&... args)
+{
+ return std::make_tuple(cc, std::make_optional(std::make_tuple(args...)));
+}
+static inline auto response(ipmi::Cc cc)
+{
+ return std::make_tuple(cc, std::nullopt);
+}
+
+/**
+ * @brief helper function to create an IPMI success response tuple
+ *
+ * IPMI handlers all return a tuple with two parts: a completion code and an
+ * optional tuple containing the rest of the data to return. This helper
+ * function makes it easier by constructing that out of an arbitrary number of
+ * arguments. Because it is a success response, this automatically packs
+ * the completion code, without needing to explicitly pass it in.
+ *
+ * @param args... - the optional list of values to return
+ *
+ * @return a standard IPMI return type (as described above)
+ */
+template <typename... Args>
+static inline auto responseSuccess(Args&&... args)
+{
+ return std::make_tuple(ipmi::ccSuccess,
+ std::make_optional(std::make_tuple(args...)));
+}
+static inline auto responseSuccess()
+{
+ return std::make_tuple(ipmi::ccSuccess, std::nullopt);
+}
+
+} // namespace ipmi
+
+// any client can interact with the main asio service
+std::shared_ptr<boost::asio::io_service> getIoService();
+
+// any client can interact with the main sdbus
+std::shared_ptr<sdbusplus::asio::connection> getSdBus();
+
+/**
+ * @brief post some work to the async exection queue
+ *
+ * The IPMI daemon runs an async exection queue; this allows any function to
+ * pass in work to be executed in that context
+ *
+ * @tparam WorkFn - a function of type void(void)
+ * @param work - the callback function to be executed
+ */
+template <typename WorkFn>
+static inline void post_work(WorkFn work)
+{
+ getIoService()->post(std::forward<WorkFn>(work));
+}
diff --git a/include/ipmid/handler.hpp b/include/ipmid/handler.hpp
new file mode 100644
index 0000000..203dcad
--- /dev/null
+++ b/include/ipmid/handler.hpp
@@ -0,0 +1,396 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <algorithm>
+#include <boost/asio/spawn.hpp>
+#include <boost/callable_traits.hpp>
+#include <cstdint>
+#include <exception>
+#include <ipmid/api.hpp>
+#include <ipmid/message.hpp>
+#include <memory>
+#include <optional>
+#include <phosphor-logging/log.hpp>
+#include <tuple>
+#include <user_channel/channel_layer.hpp>
+#include <utility>
+
+#ifdef ALLOW_DEPRECATED_API
+#include <ipmid/api.h>
+
+#include <ipmid/oemrouter.hpp>
+#endif /* ALLOW_DEPRECATED_API */
+
+namespace ipmi
+{
+
+template <typename... Args>
+static inline message::Response::ptr
+ errorResponse(message::Request::ptr request, ipmi::Cc cc, Args&&... args)
+{
+ message::Response::ptr response = request->makeResponse();
+ auto payload = std::make_tuple(cc, args...);
+ response->pack(payload);
+ return response;
+}
+static inline message::Response::ptr
+ errorResponse(message::Request::ptr request, ipmi::Cc cc)
+{
+ message::Response::ptr response = request->makeResponse();
+ response->pack(cc);
+ return response;
+}
+
+/**
+ * @brief Handler base class for dealing with IPMI request/response
+ *
+ * The subclasses are all templated so they can provide access to any type
+ * of command callback functions.
+ */
+class HandlerBase
+{
+ public:
+ using ptr = std::shared_ptr<HandlerBase>;
+
+ /** @brief wrap the call to 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.
+ *
+ * This is a non-virtual function wrapper to the virtualized executeCallback
+ * function that actually does the work. This is required because of how
+ * templates and virtualization work together.
+ *
+ * @param request a shared_ptr to a Request object
+ *
+ * @return a shared_ptr to a Response object
+ */
+ message::Response::ptr call(message::Request::ptr request)
+ {
+ return executeCallback(request);
+ }
+
+ private:
+ /** @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.
+ *
+ * @param request a shared_ptr to a Request object
+ *
+ * @return a shared_ptr to a Response object
+ */
+ virtual message::Response::ptr
+ executeCallback(message::Request::ptr request) = 0;
+};
+
+/**
+ * @brief Main IPMI handler class
+ *
+ * New IPMI handlers will resolve into this class, which will read the signature
+ * of the registering function, attempt to extract the appropriate arguments
+ * from a request, pass the arguments to the function, and then pack the
+ * response of the function back into an IPMI response.
+ */
+template <typename Handler>
+class IpmiHandler final : public HandlerBase
+{
+ public:
+ explicit IpmiHandler(Handler&& handler) :
+ handler_(std::forward<Handler>(handler))
+ {
+ }
+
+ private:
+ 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 new variety of IPMI handler, this is the function
+ * that attempts to extract the requested parameters in order to pass them
+ * onto the callback function and then packages up the response into a plain
+ * old vector to pass back 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();
+
+ using CallbackSig = boost::callable_traits::args_t<Handler>;
+ using InputArgsType = typename utility::DecayTuple<CallbackSig>::type;
+ using UnpackArgsType = typename utility::StripFirstArgs<
+ utility::NonIpmiArgsCount<InputArgsType>::size(),
+ InputArgsType>::type;
+ using ResultType = boost::callable_traits::return_type_t<Handler>;
+
+ UnpackArgsType unpackArgs;
+ ipmi::Cc unpackError = request->unpack(unpackArgs);
+ if (unpackError != ipmi::ccSuccess)
+ {
+ response->cc = unpackError;
+ return response;
+ }
+ /* callbacks can contain an optional first argument of one of:
+ * 1) boost::asio::yield_context
+ * 2) ipmi::Context::ptr
+ * 3) ipmi::message::Request::ptr
+ *
+ * If any of those is part of the callback signature as the first
+ * argument, it will automatically get packed into the parameter pack
+ * here.
+ *
+ * One more special optional argument is an ipmi::message::Payload.
+ * This argument can be in any position, though logically it makes the
+ * most sense if it is the last. If this class is included in the
+ * handler signature, it will allow for the handler to unpack optional
+ * parameters. For example, the Set LAN Configuration Parameters
+ * command takes variable length (and type) values for each of the LAN
+ * parameters. This means that the only fixed data is the channel and
+ * parameter selector. All the remaining data can be extracted using
+ * the Payload class and the unpack API available to the Payload class.
+ */
+ std::optional<InputArgsType> inputArgs;
+ if constexpr (std::tuple_size<InputArgsType>::value > 0)
+ {
+ if constexpr (std::is_same<std::tuple_element_t<0, InputArgsType>,
+ boost::asio::yield_context>::value)
+ {
+ inputArgs.emplace(std::tuple_cat(
+ std::forward_as_tuple(*(request->ctx->yield)),
+ std::move(unpackArgs)));
+ }
+ else if constexpr (std::is_same<
+ std::tuple_element_t<0, InputArgsType>,
+ ipmi::Context::ptr>::value)
+ {
+ inputArgs.emplace(
+ std::tuple_cat(std::forward_as_tuple(request->ctx),
+ std::move(unpackArgs)));
+ }
+ else if constexpr (std::is_same<
+ std::tuple_element_t<0, InputArgsType>,
+ ipmi::message::Request::ptr>::value)
+ {
+ inputArgs.emplace(std::tuple_cat(std::forward_as_tuple(request),
+ std::move(unpackArgs)));
+ }
+ else
+ {
+ // no special parameters were requested (but others were)
+ inputArgs.emplace(std::move(unpackArgs));
+ }
+ }
+ else
+ {
+ // no parameters were requested
+ inputArgs = std::move(unpackArgs);
+ }
+ ResultType result;
+ try
+ {
+ // execute the registered callback function and get the
+ // ipmi::RspType<>
+ result = std::apply(handler_, *inputArgs);
+ }
+ 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);
+ }
+ 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 = std::get<0>(result);
+ auto payload = std::get<1>(result);
+ // check for optional payload
+ if (payload)
+ {
+ response->pack(*payload);
+ }
+ return response;
+ }
+};
+
+#ifdef ALLOW_DEPRECATED_API
+/**
+ * @brief Legacy IPMI handler class
+ *
+ * Legacy IPMI 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<ipmid_callback_t> final : public HandlerBase
+{
+ public:
+ explicit IpmiHandler(const ipmid_callback_t& handler) : handler_(handler)
+ {
+ }
+
+ private:
+ ipmid_callback_t 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->netFn, request->ctx->cmd,
+ request->payload.data(), response->payload.data(),
+ &len, nullptr);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Legacy 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
+ * shared_ptr that owns the Handler object.
+ *
+ * This is called internally via the ipmi_register_callback function.
+ *
+ * @param handler the function pointer to the callback
+ *
+ * @return A shared_ptr to the created handler object
+ */
+inline auto makeLegacyHandler(const ipmid_callback_t& handler)
+{
+ HandlerBase::ptr ptr(new IpmiHandler<ipmid_callback_t>(handler));
+ return ptr;
+}
+
+#endif // ALLOW_DEPRECATED_API
+
+/**
+ * @brief create an IPMI 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 ipmi::registerHandler function.
+ *
+ * @param handler the function pointer to the callback
+ *
+ * @return A shared_ptr to the created handler object
+ */
+template <typename Handler>
+inline auto makeHandler(Handler&& handler)
+{
+ HandlerBase::ptr ptr(
+ new IpmiHandler<Handler>(std::forward<Handler>(handler)));
+ return ptr;
+}
+
+} // namespace ipmi
diff --git a/include/ipmid/message.hpp b/include/ipmid/message.hpp
new file mode 100644
index 0000000..e628ff0
--- /dev/null
+++ b/include/ipmid/message.hpp
@@ -0,0 +1,633 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <algorithm>
+#include <boost/asio/spawn.hpp>
+#include <cstdint>
+#include <ipmid/message/types.hpp>
+#include <memory>
+#include <phosphor-logging/log.hpp>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace ipmi
+{
+
+struct Context
+{
+ using ptr = std::shared_ptr<Context>;
+
+ Context() = default;
+
+ Context(NetFn netFn, Cmd cmd, int channel, int userId, Privilege priv,
+ boost::asio::yield_context* yield = nullptr) :
+ netFn(netFn),
+ cmd(cmd), channel(channel), userId(userId), priv(priv), yield(yield)
+ {
+ }
+
+ // normal IPMI context (what call is this, from whence it came...)
+ NetFn netFn = 0;
+ Cmd cmd = 0;
+ int channel = 0;
+ int userId = 0;
+ Privilege priv = Privilege::None;
+ // if non-null, use this to do blocking asynchronous asio calls
+ boost::asio::yield_context* yield = nullptr;
+};
+
+namespace message
+{
+
+namespace details
+{
+
+template <typename A>
+struct UnpackSingle;
+
+template <typename T>
+using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>;
+
+template <typename A>
+struct PackSingle;
+
+template <typename T>
+using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>;
+
+// size to hold 64 bits plus one (possibly-)partial byte
+static constexpr size_t bitStreamSize = ((sizeof(uint64_t) + 1) * CHAR_BIT);
+
+} // namespace details
+
+/**
+ * @brief a payload class that provides a mechanism to pack and unpack data
+ *
+ * When a new request is being executed, the Payload class is responsible for
+ * attempting to unpack all the required arguments from the incoming blob. For
+ * variable-length functions, it is possible to have function signature have a
+ * Payload object, which will then allow the remaining data to be extracted as
+ * needed.
+ *
+ * When creating a response, the parameters returned from the callback use a
+ * newly created payload object to pack all the parameters into a buffer that is
+ * then returned to the requester.
+ *
+ * These interfaces make calls into the message/pack.hpp and message/unpack.hpp
+ * functions.
+ */
+struct Payload
+{
+ Payload() = default;
+ Payload(const Payload&) = default;
+ Payload& operator=(const Payload&) = default;
+ Payload(Payload&&) = default;
+ Payload& operator=(Payload&&) = default;
+
+ explicit Payload(std::vector<uint8_t>&& data) :
+ raw(std::move(data)), unpackCheck(false)
+ {
+ }
+
+ ~Payload()
+ {
+ using namespace phosphor::logging;
+ if (trailingOk && !unpackCheck && !fullyUnpacked())
+ {
+ log<level::ERR>("Failed to check request for full unpack");
+ }
+ }
+
+ /******************************************************************
+ * raw vector access
+ *****************************************************************/
+ /**
+ * @brief return the size of the underlying raw buffer
+ */
+ size_t size() const
+ {
+ return raw.size();
+ }
+ /**
+ * @brief resize the underlying raw buffer to a new size
+ *
+ * @param sz - new size for the buffer
+ */
+ void resize(size_t sz)
+ {
+ raw.resize(sz);
+ }
+ /**
+ * @brief return a pointer to the underlying raw buffer
+ */
+ uint8_t* data()
+ {
+ return raw.data();
+ }
+ /**
+ * @brief return a const pointer to the underlying raw buffer
+ */
+ const uint8_t* data() const
+ {
+ return raw.data();
+ }
+
+ /******************************************************************
+ * Response operations
+ *****************************************************************/
+ /**
+ * @brief append a series of bytes to the buffer
+ *
+ * @tparam T - the type pointer to return; must be compatible to a byte
+ *
+ * @param begin - a pointer to the beginning of the series
+ * @param end - a pointer to the end of the series
+ */
+ template <typename T>
+ void append(T* begin, T* end)
+ {
+ static_assert(
+ std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
+ std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
+ std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
+ "begin and end must be signed or unsigned byte pointers");
+ // this interface only allows full-byte access; pack in partial bytes
+ drain();
+ raw.insert(raw.end(), reinterpret_cast<const uint8_t*>(begin),
+ reinterpret_cast<const uint8_t*>(end));
+ }
+
+ /**
+ * @brief append a series of bits to the buffer
+ *
+ * Only the lowest @count order of bits will be appended, with the most
+ * significant of those bits getting appended first.
+ *
+ * @param count - number of bits to append
+ * @param bits - a byte with count significant bits to append
+ */
+ void appendBits(size_t count, uint8_t bits)
+ {
+ // drain whole bytes out
+ drain(true);
+
+ // add in the new bits as the higher-order bits, filling LSBit first
+ fixed_uint_t<details::bitStreamSize> tmp = bits;
+ tmp <<= bitCount;
+ bitStream |= tmp;
+ bitCount += count;
+
+ // drain any whole bytes we have appended
+ drain(true);
+ }
+
+ /**
+ * @brief empty out the bucket and pack it as bytes LSB-first
+ *
+ * @param wholeBytesOnly - if true, only the whole bytes will be drained
+ */
+ void drain(bool wholeBytesOnly = false)
+ {
+ while (bitCount > 0)
+ {
+ uint8_t retVal;
+ if (bitCount < CHAR_BIT)
+ {
+ if (wholeBytesOnly)
+ {
+ break;
+ }
+ }
+ size_t bitsOut = std::min(static_cast<size_t>(CHAR_BIT), bitCount);
+ retVal = static_cast<uint8_t>(bitStream);
+ raw.push_back(retVal);
+ bitStream >>= bitsOut;
+ bitCount -= bitsOut;
+ }
+ }
+
+ // base empty pack
+ int pack()
+ {
+ return 0;
+ }
+
+ /**
+ * @brief pack arbitrary values (of any supported type) into the buffer
+ *
+ * @tparam Arg - the type of the first argument
+ * @tparam Args - the type of the optional remaining arguments
+ *
+ * @param arg - the first argument to pack
+ * @param args... - the optional remaining arguments to pack
+ *
+ * @return int - non-zero on pack errors
+ */
+ template <typename Arg, typename... Args>
+ int pack(Arg&& arg, Args&&... args)
+ {
+ int packRet =
+ details::PackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
+ if (packRet)
+ {
+ return packRet;
+ }
+ packRet = pack(std::forward<Args>(args)...);
+ drain();
+ return packRet;
+ }
+
+ /**
+ * @brief pack a tuple of values (of any supported type) into the buffer
+ *
+ * This will pack the elements of the tuple as if each one was passed in
+ * individually, as if passed into the above variadic function.
+ *
+ * @tparam Types - the implicitly declared list of the tuple element types
+ *
+ * @param t - the tuple of values to pack
+ *
+ * @return int - non-zero on pack errors
+ */
+ template <typename... Types>
+ int pack(std::tuple<Types...>& t)
+ {
+ return std::apply([this](Types&... args) { return pack(args...); }, t);
+ }
+
+ /******************************************************************
+ * Request operations
+ *****************************************************************/
+ /**
+ * @brief pop a series of bytes from the raw buffer
+ *
+ * @tparam T - the type pointer to return; must be compatible to a byte
+ *
+ * @param count - the number of bytes to return
+ *
+ * @return - a tuple of pointers (begin,begin+count)
+ */
+ template <typename T>
+ auto pop(size_t count)
+ {
+ static_assert(
+ std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
+ std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
+ std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
+ "T* must be signed or unsigned byte pointers");
+ // this interface only allows full-byte access; skip partial bits
+ if (bitCount)
+ {
+ // WARN on unused bits?
+ discardBits();
+ }
+ if (count <= (raw.size() - rawIndex))
+ {
+ auto range = std::make_tuple(
+ reinterpret_cast<T*>(raw.data() + rawIndex),
+ reinterpret_cast<T*>(raw.data() + rawIndex + count));
+ rawIndex += count;
+ return range;
+ }
+ unpackError = true;
+ return std::make_tuple(reinterpret_cast<T*>(NULL),
+ reinterpret_cast<T*>(NULL));
+ }
+
+ /**
+ * @brief fill bit stream with at least count bits for consumption
+ *
+ * @param count - number of bit needed
+ *
+ * @return - unpackError
+ */
+ bool fillBits(size_t count)
+ {
+ // add more bits to the top end of the bitstream
+ // so we consume bits least-significant first
+ if (count > (details::bitStreamSize - CHAR_BIT))
+ {
+ unpackError = true;
+ return unpackError;
+ }
+ while (bitCount < count)
+ {
+ if (rawIndex < raw.size())
+ {
+ fixed_uint_t<details::bitStreamSize> tmp = raw[rawIndex++];
+ tmp <<= bitCount;
+ bitStream |= tmp;
+ bitCount += CHAR_BIT;
+ }
+ else
+ {
+ // raw has run out of bytes to pop
+ unpackError = true;
+ return unpackError;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief consume count bits from bitstream (must call fillBits first)
+ *
+ * @param count - number of bit needed
+ *
+ * @return - count bits from stream
+ */
+ uint8_t popBits(size_t count)
+ {
+ if (bitCount < count)
+ {
+ unpackError = true;
+ return 0;
+ }
+ // consume bits low-order bits first
+ auto bits = bitStream.convert_to<uint8_t>();
+ bits &= ((1 << count) - 1);
+ bitStream >>= count;
+ bitCount -= count;
+ return bits;
+ }
+
+ /**
+ * @brief discard all partial bits
+ */
+ void discardBits()
+ {
+ bitStream = 0;
+ bitCount = 0;
+ }
+
+ /**
+ * @brief fully reset the unpack stream
+ */
+ void reset()
+ {
+ discardBits();
+ rawIndex = 0;
+ unpackError = false;
+ }
+
+ /**
+ * @brief check to see if the stream has been fully unpacked
+ *
+ * @return bool - true if the stream has been unpacked and has no errors
+ */
+ bool fullyUnpacked()
+ {
+ unpackCheck = true;
+ return raw.size() == rawIndex && bitCount == 0 && !unpackError;
+ }
+
+ // base empty unpack
+ int unpack()
+ {
+ return 0;
+ }
+
+ /**
+ * @brief unpack arbitrary values (of any supported type) from the buffer
+ *
+ * @tparam Arg - the type of the first argument
+ * @tparam Args - the type of the optional remaining arguments
+ *
+ * @param arg - the first argument to unpack
+ * @param args... - the optional remaining arguments to unpack
+ *
+ * @return int - non-zero for unpack error
+ */
+ template <typename Arg, typename... Args>
+ int unpack(Arg&& arg, Args&&... args)
+ {
+ int unpackRet =
+ details::UnpackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
+ if (unpackRet)
+ {
+ unpackError = true;
+ return unpackRet;
+ }
+ return unpack(std::forward<Args>(args)...);
+ }
+
+ /**
+ * @brief unpack a tuple of values (of any supported type) from the buffer
+ *
+ * This will unpack the elements of the tuple as if each one was passed in
+ * individually, as if passed into the above variadic function.
+ *
+ * @tparam Types - the implicitly declared list of the tuple element types
+ *
+ * @param t - the tuple of values to unpack
+ *
+ * @return int - non-zero on unpack error
+ */
+ template <typename... Types>
+ int unpack(std::tuple<Types...>& t)
+ {
+ // roll back checkpoint so that unpacking a tuple is atomic
+ size_t priorBitCount = bitCount;
+ size_t priorIndex = rawIndex;
+ fixed_uint_t<details::bitStreamSize> priorBits = bitStream;
+
+ int ret =
+ std::apply([this](Types&... args) { return unpack(args...); }, t);
+ if (ret)
+ {
+ bitCount = priorBitCount;
+ bitStream = priorBits;
+ rawIndex = priorIndex;
+ }
+
+ return ret;
+ }
+
+ // partial bytes in the form of bits
+ fixed_uint_t<details::bitStreamSize> bitStream;
+ size_t bitCount = 0;
+ std::vector<uint8_t> raw;
+ size_t rawIndex = 0;
+ bool trailingOk = false;
+ bool unpackCheck = true;
+ bool unpackError = false;
+};
+
+/**
+ * @brief high-level interface to an IPMI response
+ *
+ * Make it easy to just pack in the response args from the callback into a
+ * buffer that goes back to the requester.
+ */
+struct Response
+{
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor to avoid nullptrs.
+ * Allowed:
+ * - Copy operations.
+ * - Move operations.
+ * - Destructor.
+ */
+ Response() = delete;
+ Response(const Response&) = default;
+ Response& operator=(const Response&) = default;
+ Response(Response&&) = default;
+ Response& operator=(Response&&) = default;
+ ~Response() = default;
+
+ using ptr = std::shared_ptr<Response>;
+
+ explicit Response(Context::ptr& context) :
+ payload(), ctx(context), cc(ccSuccess)
+ {
+ }
+
+ /**
+ * @brief pack arbitrary values (of any supported type) into the payload
+ *
+ * @tparam Args - the type of the optional arguments
+ *
+ * @param args... - the optional arguments to pack
+ *
+ * @return int - non-zero on pack errors
+ */
+ template <typename... Args>
+ int pack(Args&&... args)
+ {
+ return payload.pack(std::forward<Args>(args)...);
+ }
+
+ /**
+ * @brief pack a tuple of values (of any supported type) into the payload
+ *
+ * This will pack the elements of the tuple as if each one was passed in
+ * individually, as if passed into the above variadic function.
+ *
+ * @tparam Types - the implicitly declared list of the tuple element types
+ *
+ * @param t - the tuple of values to pack
+ *
+ * @return int - non-zero on pack errors
+ */
+ template <typename... Types>
+ int pack(std::tuple<Types...>& t)
+ {
+ return payload.pack(t);
+ }
+
+ Payload payload;
+ Context::ptr ctx;
+ Cc cc;
+};
+
+/**
+ * @brief high-level interface to an IPMI request
+ *
+ * Make it easy to unpack the buffer into the request args for the callback.
+ */
+struct Request
+{
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor to avoid nullptrs.
+ * Allowed:
+ * - Copy operations.
+ * - Move operations.
+ * - Destructor.
+ */
+ Request() = delete;
+ Request(const Request&) = default;
+ Request& operator=(const Request&) = default;
+ Request(Request&&) = default;
+ Request& operator=(Request&&) = default;
+ ~Request() = default;
+
+ using ptr = std::shared_ptr<Request>;
+
+ explicit Request(Context::ptr context, std::vector<uint8_t>&& d) :
+ payload(std::forward<std::vector<uint8_t>>(d)), ctx(context)
+ {
+ }
+
+ /**
+ * @brief unpack arbitrary values (of any supported type) from the payload
+ *
+ * @tparam Args - the type of the optional arguments
+ *
+ * @param args... - the optional arguments to unpack
+ *
+ * @return int - non-zero for unpack error
+ */
+ template <typename... Args>
+ int unpack(Args&&... args)
+ {
+ int unpackRet = payload.unpack(std::forward<Args>(args)...);
+ if (unpackRet == ipmi::ccSuccess)
+ {
+ if (!payload.trailingOk)
+ {
+ if (!payload.fullyUnpacked())
+ {
+ // not all bits were consumed by requested parameters
+ return ipmi::ccReqDataLenInvalid;
+ }
+ payload.unpackCheck = false;
+ }
+ }
+ return unpackRet;
+ }
+
+ /**
+ * @brief unpack a tuple of values (of any supported type) from the payload
+ *
+ * This will unpack the elements of the tuple as if each one was passed in
+ * individually, as if passed into the above variadic function.
+ *
+ * @tparam Types - the implicitly declared list of the tuple element types
+ *
+ * @param t - the tuple of values to unpack
+ *
+ * @return int - non-zero on unpack error
+ */
+ template <typename... Types>
+ int unpack(std::tuple<Types...>& t)
+ {
+ return std::apply([this](Types&... args) { return unpack(args...); },
+ t);
+ }
+
+ /** @brief Create a response message that corresponds to this request
+ *
+ * @return A shared_ptr to the response message created
+ */
+ Response::ptr makeResponse()
+ {
+ return std::make_shared<Response>(ctx);
+ }
+
+ Payload payload;
+ Context::ptr ctx;
+};
+
+} // namespace message
+
+} // namespace ipmi
+
+// include packing and unpacking of types
+#include <ipmid/message/pack.hpp>
+#include <ipmid/message/unpack.hpp>
diff --git a/include/ipmid/message/pack.hpp b/include/ipmid/message/pack.hpp
new file mode 100644
index 0000000..e6bbbce
--- /dev/null
+++ b/include/ipmid/message/pack.hpp
@@ -0,0 +1,222 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <array>
+#include <ipmid/message/types.hpp>
+#include <memory>
+#include <phosphor-logging/log.hpp>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace ipmi
+{
+
+namespace message
+{
+
+namespace details
+{
+
+/**************************************
+ * ipmi return type helpers
+ **************************************/
+
+template <typename NumericType, size_t byteIndex = 0>
+void PackBytes(uint8_t* pointer, const NumericType& i)
+{
+ if constexpr (byteIndex < sizeof(NumericType))
+ {
+ *pointer = static_cast<uint8_t>(i >> (8 * byteIndex));
+ PackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
+ }
+}
+
+template <typename NumericType, size_t byteIndex = 0>
+void PackBytesUnaligned(Payload& p, const NumericType& i)
+{
+ if constexpr (byteIndex < sizeof(NumericType))
+ {
+ p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex)));
+ PackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
+ }
+}
+
+/** @struct PackSingle
+ * @brief Utility to pack a single C++ element into a Payload
+ *
+ * User-defined types are expected to specialize this template in order to
+ * get their functionality.
+ *
+ * @tparam S - Type of element to pack.
+ */
+template <typename T>
+struct PackSingle
+{
+ /** @brief Do the operation to pack element.
+ *
+ * @param[in] p - Payload to pack into.
+ * @param[out] t - The reference to pack item into.
+ */
+ static int op(Payload& p, T& t)
+ {
+ // if not on a byte boundary, must pack values LSbit/LSByte first
+ if (p.bitCount)
+ {
+ PackBytesUnaligned<T>(p, t);
+ }
+ else
+ {
+ // copy in bits to vector....
+ p.raw.resize(p.raw.size() + sizeof(T));
+ uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T);
+ PackBytes<T>(out, t);
+ }
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::string
+ * represented as a UCSD-Pascal style string
+ */
+template <>
+struct PackSingle<std::string>
+{
+ static int op(Payload& p, std::string& t)
+ {
+ // check length first
+ uint8_t len;
+ if (t.length() > std::numeric_limits<decltype(len)>::max())
+ {
+ using namespace phosphor::logging;
+ log<level::ERR>("long string truncated on IPMI message pack");
+ return 1;
+ }
+ len = static_cast<uint8_t>(t.length());
+ PackSingle<uint8_t>::op(p, len);
+ p.append(t.c_str(), t.c_str() + t.length());
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for fixed_uint_t types
+ */
+template <unsigned N>
+struct PackSingle<fixed_uint_t<N>>
+{
+ static int op(Payload& p, fixed_uint_t<N>& t)
+ {
+ size_t count = N;
+ static_assert(N <= (details::bitStreamSize - CHAR_BIT));
+ uint64_t bits = t;
+ while (count > 0)
+ {
+ size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT));
+ p.appendBits(appendCount, static_cast<uint8_t>(bits));
+ bits >>= CHAR_BIT;
+ count -= appendCount;
+ }
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for bool. */
+template <>
+struct PackSingle<bool>
+{
+ static int op(Payload& p, bool& b)
+ {
+ p.appendBits(1, b);
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::bitset<N> */
+template <size_t N>
+struct PackSingle<std::bitset<N>>
+{
+ static int op(Payload& p, std::bitset<N>& t)
+ {
+ size_t count = N;
+ static_assert(N <= (details::bitStreamSize - CHAR_BIT));
+ unsigned long long bits = t.to_ullong();
+ while (count > 0)
+ {
+ size_t appendCount = std::min(count, size_t(CHAR_BIT));
+ p.appendBits(appendCount, static_cast<uint8_t>(bits));
+ bits >>= CHAR_BIT;
+ count -= appendCount;
+ }
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::array<T, N> */
+template <typename T, size_t N>
+struct PackSingle<std::array<T, N>>
+{
+ static int op(Payload& p, std::array<T, N>& t)
+ {
+ int ret = 0;
+ for (auto& v : t)
+ {
+ int ret = PackSingle<T>::op(p, v);
+ if (ret)
+ {
+ break;
+ }
+ }
+ return ret;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::vector<T> */
+template <typename T>
+struct PackSingle<std::vector<T>>
+{
+ static int op(Payload& p, std::vector<T>& t)
+ {
+ int ret = 0;
+ for (auto& v : t)
+ {
+ int ret = PackSingle<T>::op(p, v);
+ if (ret)
+ {
+ break;
+ }
+ }
+ return ret;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::vector<uint8_t> */
+template <>
+struct PackSingle<std::vector<uint8_t>>
+{
+ static int op(Payload& p, std::vector<uint8_t>& t)
+ {
+ p.raw.reserve(p.raw.size() + t.size());
+ p.raw.insert(p.raw.end(), t.begin(), t.end());
+ return 0;
+ }
+};
+
+} // namespace details
+
+} // namespace message
+
+} // namespace ipmi
diff --git a/include/ipmid/message/types.hpp b/include/ipmid/message/types.hpp
new file mode 100644
index 0000000..b79ddba
--- /dev/null
+++ b/include/ipmid/message/types.hpp
@@ -0,0 +1,110 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <bitset>
+#include <boost/multiprecision/cpp_int.hpp>
+#include <ipmid/utility.hpp>
+#include <tuple>
+
+// unsigned fixed-bit sizes
+template <unsigned N>
+using fixed_uint_t =
+ boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
+ N, N, boost::multiprecision::unsigned_magnitude,
+ boost::multiprecision::unchecked, void>>;
+// signed fixed-bit sizes
+template <unsigned N>
+using fixed_int_t =
+ boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
+ N, N, boost::multiprecision::signed_magnitude,
+ boost::multiprecision::unchecked, void>>;
+
+using uint1_t = fixed_uint_t<1>;
+using uint2_t = fixed_uint_t<2>;
+using uint3_t = fixed_uint_t<3>;
+using uint4_t = fixed_uint_t<4>;
+using uint5_t = fixed_uint_t<5>;
+using uint6_t = fixed_uint_t<6>;
+using uint7_t = fixed_uint_t<7>;
+// native uint8_t
+using uint9_t = fixed_uint_t<9>;
+using uint10_t = fixed_uint_t<10>;
+using uint11_t = fixed_uint_t<11>;
+using uint12_t = fixed_uint_t<12>;
+using uint13_t = fixed_uint_t<13>;
+using uint14_t = fixed_uint_t<14>;
+using uint15_t = fixed_uint_t<15>;
+// native uint16_t
+using uint24_t = fixed_uint_t<24>;
+
+// signed fixed-bit sizes
+using int2_t = fixed_int_t<2>;
+using int3_t = fixed_int_t<3>;
+using int4_t = fixed_int_t<4>;
+using int5_t = fixed_int_t<5>;
+using int6_t = fixed_int_t<6>;
+using int7_t = fixed_int_t<7>;
+// native int8_t
+using int9_t = fixed_int_t<9>;
+using int10_t = fixed_int_t<10>;
+using int11_t = fixed_int_t<11>;
+using int12_t = fixed_int_t<12>;
+using int13_t = fixed_int_t<13>;
+using int14_t = fixed_int_t<14>;
+using int15_t = fixed_int_t<15>;
+// native int16_t
+using int24_t = fixed_int_t<24>;
+
+// bool is more efficient than a uint1_t
+using bit = bool;
+
+// Mechanism for going from uint7_t, int7_t, or std::bitset<7> to 7 bits
+// use nrFixedBits<uint7_t> or nrFixedBits<decltype(u7)>
+namespace types
+{
+namespace details
+{
+
+template <size_t N>
+struct Size
+{
+ static constexpr size_t value = N;
+};
+
+template <unsigned Bits>
+constexpr auto getNrBits(const fixed_int_t<Bits>&) -> Size<Bits>;
+template <unsigned Bits>
+constexpr auto getNrBits(const fixed_uint_t<Bits>&) -> Size<Bits>;
+template <size_t Bits>
+constexpr auto getNrBits(const std::bitset<Bits>&) -> Size<Bits>;
+
+} // namespace details
+
+/**
+ * @brief mechanism to get N from a type like fixed_int_t<N>
+ *
+ * helper template to extract N from a fixed_(u)int_t variable
+ *
+ * @tparam T - a type of fixed_int_t<N> or fixed_unint_t<N>
+ *
+ * @return size_t - evaluates to a constexpr size_t of N
+ */
+template <typename T>
+constexpr auto nrFixedBits =
+ decltype(details::getNrBits(std::declval<T>()))::value;
+
+} // namespace types
diff --git a/include/ipmid/message/unpack.hpp b/include/ipmid/message/unpack.hpp
new file mode 100644
index 0000000..d96928f
--- /dev/null
+++ b/include/ipmid/message/unpack.hpp
@@ -0,0 +1,340 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <array>
+#include <ipmid/message/types.hpp>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace ipmi
+{
+
+namespace message
+{
+
+namespace details
+{
+
+/**************************************
+ * ipmi return type helpers
+ **************************************/
+
+template <typename NumericType, size_t byteIndex = 0>
+void UnpackBytes(uint8_t* pointer, NumericType& i)
+{
+ if constexpr (byteIndex < sizeof(NumericType))
+ {
+ i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex);
+ UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
+ }
+}
+
+template <typename NumericType, size_t byteIndex = 0>
+void UnpackBytesUnaligned(Payload& p, NumericType& i)
+{
+ if constexpr (byteIndex < sizeof(NumericType))
+ {
+ i |= static_cast<NumericType>(p.popBits(CHAR_BIT))
+ << (CHAR_BIT * byteIndex);
+ UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
+ }
+}
+
+/** @struct UnpackSingle
+ * @brief Utility to unpack a single C++ element from a Payload
+ *
+ * User-defined types are expected to specialize this template in order to
+ * get their functionality.
+ *
+ * @tparam T - Type of element to unpack.
+ */
+template <typename T>
+struct UnpackSingle
+{
+ /** @brief Do the operation to unpack element.
+ *
+ * @param[in] p - Payload to unpack from.
+ * @param[out] t - The reference to unpack item into.
+ */
+ static int op(Payload& p, T& t)
+ {
+ if constexpr (std::is_fundamental<T>::value)
+ {
+ t = 0;
+ if (p.bitCount)
+ {
+ if (p.fillBits(CHAR_BIT * sizeof(t)))
+ {
+ return 1;
+ }
+ UnpackBytesUnaligned<T>(p, t);
+ }
+ else
+ {
+ // copy out bits from vector....
+ if (p.raw.size() < (p.rawIndex + sizeof(t)))
+ {
+ return 1;
+ }
+ auto iter = p.raw.data() + p.rawIndex;
+ t = 0;
+ UnpackBytes<T>(iter, t);
+ p.rawIndex += sizeof(t);
+ }
+ return 0;
+ }
+ else
+ {
+ if constexpr (utility::is_tuple<T>::value)
+ {
+ bool priorError = p.unpackError;
+ size_t priorIndex = p.rawIndex;
+ // more stuff to unroll if partial bytes are out
+ size_t priorBitCount = p.bitCount;
+ fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
+ int ret = p.unpack(t);
+ if (ret != 0)
+ {
+ t = T();
+ p.rawIndex = priorIndex;
+ p.bitStream = priorBits;
+ p.bitCount = priorBitCount;
+ p.unpackError = priorError;
+ }
+ return 0;
+ }
+ }
+ }
+};
+
+/** @struct UnpackSingle
+ * @brief Utility to unpack a single C++ element from a Payload
+ *
+ * Specialization to unpack std::string represented as a
+ * UCSD-Pascal style string
+ */
+template <>
+struct UnpackSingle<std::string>
+{
+ static int op(Payload& p, std::string& t)
+ {
+ // pop len first
+ if (p.rawIndex > (p.raw.size() - sizeof(uint8_t)))
+ {
+ return 1;
+ }
+ uint8_t len = p.raw[p.rawIndex++];
+ // check to see that there are n bytes left
+ auto [first, last] = p.pop<char>(len);
+ if (first == last)
+ {
+ return 1;
+ }
+ t.reserve(last - first);
+ t.insert(0, first, (last - first));
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for fixed_uint_t types
+ */
+template <unsigned N>
+struct UnpackSingle<fixed_uint_t<N>>
+{
+ static int op(Payload& p, fixed_uint_t<N>& t)
+ {
+ static_assert(N <= (details::bitStreamSize - CHAR_BIT));
+ constexpr size_t count = N;
+ // acquire enough bits in the stream to fulfill the Payload
+ if (p.fillBits(count))
+ {
+ return -1;
+ }
+ fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
+ t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>();
+ p.bitStream >>= count;
+ p.bitCount -= count;
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for bool. */
+template <>
+struct UnpackSingle<bool>
+{
+ static int op(Payload& p, bool& b)
+ {
+ // acquire enough bits in the stream to fulfill the Payload
+ if (p.fillBits(1))
+ {
+ return -1;
+ }
+ b = static_cast<bool>(p.bitStream & 0x01);
+ // clear bits from stream
+ p.bitStream >>= 1;
+ p.bitCount -= 1;
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::bitset<N>
+ */
+template <size_t N>
+struct UnpackSingle<std::bitset<N>>
+{
+ static int op(Payload& p, std::bitset<N>& t)
+ {
+ static_assert(N <= (details::bitStreamSize - CHAR_BIT));
+ size_t count = N;
+ // acquire enough bits in the stream to fulfill the Payload
+ if (p.fillBits(count))
+ {
+ return -1;
+ }
+ fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
+ t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
+ p.bitStream >>= count;
+ p.bitCount -= count;
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::optional<T> */
+template <typename T>
+struct UnpackSingle<std::optional<T>>
+{
+ static int op(Payload& p, std::optional<T>& t)
+ {
+ bool priorError = p.unpackError;
+ size_t priorIndex = p.rawIndex;
+ // more stuff to unroll if partial bytes are out
+ size_t priorBitCount = p.bitCount;
+ fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
+ t.emplace();
+ int ret = UnpackSingle<T>::op(p, *t);
+ if (ret != 0)
+ {
+ t.reset();
+ p.rawIndex = priorIndex;
+ p.bitStream = priorBits;
+ p.bitCount = priorBitCount;
+ p.unpackError = priorError;
+ }
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::array<T, N> */
+template <typename T, size_t N>
+struct UnpackSingle<std::array<T, N>>
+{
+ static int op(Payload& p, std::array<T, N>& t)
+ {
+ int ret = 0;
+ size_t priorIndex = p.rawIndex;
+ for (auto& v : t)
+ {
+ ret = UnpackSingle<T>::op(p, v);
+ if (ret)
+ {
+ p.rawIndex = priorIndex;
+ t = std::array<T, N>();
+ break;
+ }
+ }
+ return ret;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::array<uint8_t> */
+template <size_t N>
+struct UnpackSingle<std::array<uint8_t, N>>
+{
+ static int op(Payload& p, std::array<uint8_t, N>& t)
+ {
+ if (p.raw.size() - p.rawIndex < N)
+ {
+ t.fill(0);
+ return -1;
+ }
+ // copy out the bytes
+ std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
+ t.begin());
+ p.rawIndex += N;
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::vector<T> */
+template <typename T>
+struct UnpackSingle<std::vector<T>>
+{
+ static int op(Payload& p, std::vector<T>& t)
+ {
+ int ret = 0;
+ while (p.rawIndex < p.raw.size())
+ {
+ t.emplace_back();
+ ret = UnpackSingle<T>::op(p, t.back());
+ if (ret)
+ {
+ t.pop_back();
+ break;
+ }
+ }
+ return ret;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
+template <>
+struct UnpackSingle<std::vector<uint8_t>>
+{
+ static int op(Payload& p, std::vector<uint8_t>& t)
+ {
+ // copy out the remainder of the message
+ t.reserve(p.raw.size() - p.rawIndex);
+ t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
+ p.rawIndex = p.raw.size();
+ return 0;
+ }
+};
+
+/** @brief Specialization of UnpackSingle for Payload */
+template <>
+struct UnpackSingle<Payload>
+{
+ static int op(Payload& p, Payload& t)
+ {
+ // mark that this payload is being included in the args
+ p.trailingOk = true;
+ t = p;
+ // reset the unpacking flags so it can be properly checked
+ t.trailingOk = false;
+ t.unpackCheck = true;
+ t.unpackError = false;
+ return 0;
+ }
+};
+
+} // namespace details
+
+} // namespace message
+
+} // namespace ipmi
diff --git a/include/ipmid/registration.hpp b/include/ipmid/registration.hpp
new file mode 100644
index 0000000..151aca1
--- /dev/null
+++ b/include/ipmid/registration.hpp
@@ -0,0 +1,327 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <ipmid/api.hpp>
+#include <ipmid/handler.hpp>
+
+namespace ipmi
+{
+
+namespace impl
+{
+
+// IPMI command handler registration implementation
+bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv,
+ ::ipmi::HandlerBase::ptr handler);
+} // namespace impl
+
+/**
+ * @brief main IPMI handler registration function
+ *
+ * This function should be used to register all new-style IPMI handler
+ * functions. This function just passes the callback to makeHandler, which
+ * creates a new wrapper object that will automatically extract the appropriate
+ * parameters for the callback function as well as pack up the response.
+ *
+ * @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>
+bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv,
+ Handler&& handler)
+{
+ auto h = ipmi::makeHandler(std::forward<Handler>(handler));
+ return impl::registerHandler(prio, netFn, cmd, priv, h);
+}
+
+} // namespace ipmi
+
+#ifdef ALLOW_DEPRECATED_API
+/**
+ * @brief legacy IPMI handler registration function
+ *
+ * This function should be used to register all legacy IPMI handler
+ * functions. This function just behaves just as the legacy registration
+ * mechanism did, silently replacing any existing handler with a new one.
+ *
+ * @param netFn - the IPMI net function number to register
+ * @param cmd - the IPMI command number to register
+ * @param context - ignored
+ * @param handler - the callback function that will handle this request
+ * @param priv - the IPMI user privilige required for this command
+ */
+// [[deprecated("Use ipmi::registerHandler() instead")]]
+void ipmi_register_callback(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_context_t context, ipmid_callback_t handler,
+ ipmi_cmd_privilege_t priv);
+
+#endif /* ALLOW_DEPRECATED_API */
+
+// IPMI 2.0 and DCMI 1.5 standard commands, namespaced by NetFn
+// OEM and non-standard commands should be defined where they are used
+namespace ipmi
+{
+namespace app
+{
+// 0x00 reserved
+constexpr Cmd cmdGetDeviceId = 0x01;
+constexpr Cmd cmdColdReset = 0x02;
+constexpr Cmd cmdWarmReset = 0x03;
+constexpr Cmd cmdGetSelfTestResults = 0x04;
+constexpr Cmd cmdManufacturingTestOn = 0x05;
+constexpr Cmd cmdSetAcpiPowerState = 0x06;
+constexpr Cmd cmdGetAcpiPowerState = 0x07;
+constexpr Cmd cmdGetDeviceGuid = 0x08;
+constexpr Cmd cmdGetNetFnSupport = 0x09;
+constexpr Cmd cmdGetCmdSupport = 0x0A;
+constexpr Cmd cmdGetCmdSubFnSupport = 0x0B;
+constexpr Cmd cmdGetConfigurableCmds = 0x0C;
+constexpr Cmd cmdGetConfigurableCmdSubFns = 0x0D;
+// 0x0E-0x21 unassigned
+constexpr Cmd cmdResetWatchdogTimer = 0x22;
+// 0x23 unassigned
+constexpr Cmd cmdSetWatchdogTimer = 0x24;
+constexpr Cmd cmdGetWatchdogTimer = 0x25;
+// 0x26-0x2D unassigned
+constexpr Cmd cmdSetBmcGlobalEnables = 0x2E;
+constexpr Cmd cmdGetBmcGlobalEnables = 0x2F;
+constexpr Cmd cmdClearMessageFlags = 0x30;
+constexpr Cmd cmdGetMessageFlags = 0x31;
+constexpr Cmd cmdEnableMessageChannelRcv = 0x32;
+constexpr Cmd cmdGetMessage = 0x33;
+constexpr Cmd cmdSendMessage = 0x34;
+constexpr Cmd cmdReadEventMessageBuffer = 0x35;
+constexpr Cmd cmdGetBtIfaceCapabilities = 0x36;
+constexpr Cmd cmdGetSystemGuid = 0x37;
+constexpr Cmd cmdGetChannelAuthCapabilities = 0x38;
+constexpr Cmd cmdGetSessionChallenge = 0x39;
+constexpr Cmd cmdActivateSession = 0x3A;
+constexpr Cmd cmdSetSessionPrivilegeLevel = 0x3B;
+constexpr Cmd cmdCloseSession = 0x3C;
+constexpr Cmd cmdGetSessionInfo = 0x3D;
+// 0x3E unassigned
+constexpr Cmd cmdGetAuthCode = 0x3F;
+constexpr Cmd cmdSetChannelAccess = 0x40;
+constexpr Cmd cmdGetChannelAccess = 0x41;
+constexpr Cmd cmdGetChannelInfoCommand = 0x42;
+constexpr Cmd cmdSetUserAccessCommand = 0x43;
+constexpr Cmd cmdGetUserAccessCommand = 0x44;
+constexpr Cmd cmdSetUserName = 0x45;
+constexpr Cmd cmdGetUserNameCommand = 0x46;
+constexpr Cmd cmdSetUserPasswordCommand = 0x47;
+constexpr Cmd cmdActivatePayload = 0x48;
+constexpr Cmd cmdDeactivatePayload = 0x49;
+constexpr Cmd cmdGetPayloadActivationStatus = 0x4A;
+constexpr Cmd cmdGetPayloadInstanceInfo = 0x4B;
+constexpr Cmd cmdSetUserPayloadAccess = 0x4C;
+constexpr Cmd cmdGetUserPayloadAccess = 0x4D;
+constexpr Cmd cmdGetChannelPayloadSupport = 0x4E;
+constexpr Cmd cmdGetChannelPayloadVersion = 0x4F;
+constexpr Cmd cmdGetChannelOemPayloadInfo = 0x50;
+// 0x51 unassigned
+constexpr Cmd cmdMasterWriteRead = 0x52;
+// 0x53 unassigned
+constexpr Cmd cmdGetChannelCipherSuites = 0x54;
+constexpr Cmd cmdSuspendResumePayloadEnc = 0x55;
+constexpr Cmd cmdSetChannelSecurityKeys = 0x56;
+constexpr Cmd cmdGetSystemIfCapabilities = 0x57;
+constexpr Cmd cmdSetSystemInfoParameters = 0x58;
+constexpr Cmd cmdGetSystemInfoParameters = 0x59;
+// 0x5A-0x5F unassigned
+constexpr Cmd cmdSetCommandEnables = 0x60;
+constexpr Cmd cmdGetCommandEnables = 0x61;
+constexpr Cmd cmdSetCommandSubFnEnables = 0x62;
+constexpr Cmd cmdGetCommandSubFnEnables = 0x63;
+constexpr Cmd cmdGetOemNetFnIanaSupport = 0x64;
+// 0x65-0xff unassigned
+} // namespace app
+
+namespace chassis
+{
+constexpr Cmd cmdGetChassisCapabilities = 0x00;
+constexpr Cmd cmdGetChassisStatus = 0x01;
+constexpr Cmd cmdChassisControl = 0x02;
+constexpr Cmd cmdChassisReset = 0x03;
+constexpr Cmd cmdChassisIdentify = 0x04;
+constexpr Cmd cmdSetChassisCapabilities = 0x05;
+constexpr Cmd cmdSetPowerRestorePolicy = 0x06;
+constexpr Cmd cmdGetSystemRestartCause = 0x07;
+constexpr Cmd cmdSetSystemBootOptions = 0x08;
+constexpr Cmd cmdGetSystemBootOptions = 0x09;
+constexpr Cmd cmdSetFrontPanelButtonEnables = 0x0A;
+constexpr Cmd cmdSetPowerCycleInterval = 0x0B;
+// 0x0C-0x0E unassigned
+constexpr Cmd cmdGetPohCounter = 0x0F;
+// 0x10-0xFF unassigned
+} // namespace chassis
+
+namespace sensor_event
+{
+constexpr Cmd cmdSetEventReceiver = 0x00;
+constexpr Cmd cmdGetEventReceiver = 0x01;
+constexpr Cmd cmdPlatformEvent = 0x02;
+// 0x03-0x0F unassigned
+constexpr Cmd cmdGetPefCapabilities = 0x10;
+constexpr Cmd cmdArmPefPostponeTimer = 0x11;
+constexpr Cmd cmdSetPefConfigurationParams = 0x12;
+constexpr Cmd cmdGetPefConfigurationParams = 0x13;
+constexpr Cmd cmdSetLastProcessedEventId = 0x14;
+constexpr Cmd cmdGetLastProcessedEventId = 0x15;
+constexpr Cmd cmdAlertImmediate = 0x16;
+constexpr Cmd cmdPetAcknowledge = 0x17;
+constexpr Cmd cmdGetDeviceSdrInfo = 0x20;
+constexpr Cmd cmdGetDeviceSdr = 0x21;
+constexpr Cmd cmdReserveDeviceSdrRepository = 0x22;
+constexpr Cmd cmdGetSensorReadingFactors = 0x23;
+constexpr Cmd cmdSetSensorHysteresis = 0x24;
+constexpr Cmd cmdGetSensorHysteresis = 0x25;
+constexpr Cmd cmdSetSensorThreshold = 0x26;
+constexpr Cmd cmdGetSensorThreshold = 0x27;
+constexpr Cmd cmdSetSensorEventEnable = 0x28;
+constexpr Cmd cmdGetSensorEventEnable = 0x29;
+constexpr Cmd cmdRearmSensorEvents = 0x2A;
+constexpr Cmd cmdGetSensorEventStatus = 0x2B;
+constexpr Cmd cmdGetSensorReading = 0x2D;
+constexpr Cmd cmdSetSensorType = 0x2E;
+constexpr Cmd cmdGetSensorType = 0x2F;
+constexpr Cmd cmdSetSensorReadingAndEvtSts = 0x30;
+// 0x31-0xFF unassigned
+} // namespace sensor_event
+
+namespace storage
+{
+// 0x00-0x0F unassigned
+constexpr Cmd cmdGetFruInventoryAreaInfo = 0x10;
+constexpr Cmd cmdReadFruData = 0x11;
+constexpr Cmd cmdWriteFruData = 0x12;
+// 0x13-0x1F unassigned
+constexpr Cmd cmdGetSdrRepositoryInfo = 0x20;
+constexpr Cmd cmdGetSdrRepositoryAllocInfo = 0x21;
+constexpr Cmd cmdReserveSdrRepository = 0x22;
+constexpr Cmd cmdGetSdr = 0x23;
+constexpr Cmd cmdAddSdr = 0x24;
+constexpr Cmd cmdPartialAddSdr = 0x25;
+constexpr Cmd cmdDeleteSdr = 0x26;
+constexpr Cmd cmdClearSdrRepository = 0x27;
+constexpr Cmd cmdGetSdrRepositoryTime = 0x28;
+constexpr Cmd cmdSetSdrRepositoryTime = 0x29;
+constexpr Cmd cmdEnterSdrRepoUpdateMode = 0x2A;
+constexpr Cmd cmdExitSdrReposUpdateMode = 0x2B;
+constexpr Cmd cmdRunInitializationAgent = 0x2C;
+// 0x2D-0x3F unassigned
+constexpr Cmd cmdGetSelInfo = 0x40;
+constexpr Cmd cmdGetSelAllocationInfo = 0x41;
+constexpr Cmd cmdReserveSel = 0x42;
+constexpr Cmd cmdGetSelEntry = 0x43;
+constexpr Cmd cmdAddSelEntry = 0x44;
+constexpr Cmd cmdPartialAddSelEntry = 0x45;
+constexpr Cmd cmdDeleteSelEntry = 0x46;
+constexpr Cmd cmdClearSel = 0x47;
+constexpr Cmd cmdGetSelTime = 0x48;
+constexpr Cmd cmdSetSelTime = 0x49;
+constexpr Cmd cmdGetAuxiliaryLogStatus = 0x5A;
+constexpr Cmd cmdSetAuxiliaryLogStatus = 0x5B;
+constexpr Cmd cmdGetSelTimeUtcOffset = 0x5C;
+constexpr Cmd cmdSetSelTimeUtcOffset = 0x5D;
+// 0x5E-0xFF unassigned
+} // namespace storage
+
+namespace transport
+{
+constexpr Cmd cmdSetLanConfigParameters = 0x01;
+constexpr Cmd cmdGetLanConfigParameters = 0x02;
+constexpr Cmd cmdSuspendBmcArps = 0x03;
+constexpr Cmd cmdGetIpUdpRmcpStatistics = 0x04;
+constexpr Cmd cmdSetSerialModemConfig = 0x10;
+constexpr Cmd cmdGetSerialModemConfig = 0x11;
+constexpr Cmd cmdSetSerialModemMux = 0x12;
+constexpr Cmd cmdGetTapResponseCodes = 0x13;
+constexpr Cmd cmdSetPppUdpProxyTransmitData = 0x14;
+constexpr Cmd cmdGetPppUdpProxyTransmitData = 0x15;
+constexpr Cmd cmdSendPppUdpProxyPacket = 0x16;
+constexpr Cmd cmdGetPppUdpProxyReceiveData = 0x17;
+constexpr Cmd cmdSerialModemConnActive = 0x18;
+constexpr Cmd cmdCallback = 0x19;
+constexpr Cmd cmdSetUserCallbackOptions = 0x1A;
+constexpr Cmd cmdGetUserCallbackOptions = 0x1B;
+constexpr Cmd cmdSetSerialRoutingMux = 0x1C;
+constexpr Cmd cmdSolActivating = 0x20;
+constexpr Cmd cmdSetSolConfigParameters = 0x21;
+constexpr Cmd cmdGetSolConfigParameters = 0x22;
+constexpr Cmd cmdForwardedCommand = 0x30;
+constexpr Cmd cmdSetForwardedCommands = 0x31;
+constexpr Cmd cmdGetForwardedCommands = 0x32;
+constexpr Cmd cmdEnableForwardedCommands = 0x33;
+} // namespace transport
+
+namespace bridge
+{
+constexpr Cmd cmdGetBridgeState = 0x00;
+constexpr Cmd cmdSetBridgeState = 0x01;
+constexpr Cmd cmdGetIcmbAddress = 0x02;
+constexpr Cmd cmdSetIcmbAddress = 0x03;
+constexpr Cmd cmdSetBridgeProxyAddress = 0x04;
+constexpr Cmd cmdGetBridgeStatistics = 0x05;
+constexpr Cmd cmdGetIcmbCapabilities = 0x06;
+constexpr Cmd cmdClearBridgeStatistics = 0x08;
+constexpr Cmd cmdGetBridgeProxyAddress = 0x09;
+constexpr Cmd cmdGetIcmbConnectorInfo = 0x0A;
+constexpr Cmd cmdGetIcmbConnectionId = 0x0B;
+constexpr Cmd cmdSendIcmbConnectionId = 0x0C;
+constexpr Cmd cmdPrepareForDiscovery = 0x10;
+constexpr Cmd cmdGetAddresses = 0x11;
+constexpr Cmd cmdSetDiscovered = 0x12;
+constexpr Cmd cmdGetChassisDeviceId = 0x13;
+constexpr Cmd cmdSetChassisDeviceId = 0x14;
+constexpr Cmd cmdBridgeRequest = 0x20;
+constexpr Cmd cmdBridgeMessage = 0x21;
+// 0x22-0x2F unassigned
+constexpr Cmd cmdGetEventCount = 0x30;
+constexpr Cmd cmdSetEventDestination = 0x31;
+constexpr Cmd cmdSetEventReceptionState = 0x32;
+constexpr Cmd cmdSendIcmbEventMessage = 0x33;
+constexpr Cmd cmdGetEventDestination = 0x34;
+constexpr Cmd cmdGetEventReceptionState = 0x35;
+// 0xC0-0xFE OEM Commands
+constexpr Cmd cmdErrorReport = 0xFF;
+} // namespace bridge
+
+namespace dcmi
+{
+constexpr Cmd cmdGetDcmiCapabilitiesInfo = 0x01;
+constexpr Cmd cmdGetPowerReading = 0x02;
+constexpr Cmd cmdGetPowerLimit = 0x03;
+constexpr Cmd cmdSetPowerLimit = 0x04;
+constexpr Cmd cmdActDeactivatePwrLimit = 0x05;
+constexpr Cmd cmdGetAssetTag = 0x06;
+constexpr Cmd cmdGetDcmiSensorInfo = 0x07;
+constexpr Cmd cmdSetAssetTag = 0x08;
+constexpr Cmd cmdGetMgmtCntlrIdString = 0x09;
+constexpr Cmd cmdSetMgmtCntlrIdString = 0x0A;
+constexpr Cmd cmdSetThermalLimit = 0x0B;
+constexpr Cmd cmdGetThermalLimit = 0x0C;
+constexpr Cmd cmdGetTemperatureReadings = 0x10;
+constexpr Cmd cmdSetDcmiConfigParameters = 0x12;
+constexpr Cmd cmdGetDcmiConfigParameters = 0x13;
+} // namespace dcmi
+
+} // namespace ipmi
diff --git a/include/ipmid/utility.hpp b/include/ipmid/utility.hpp
new file mode 100644
index 0000000..3a36434
--- /dev/null
+++ b/include/ipmid/utility.hpp
@@ -0,0 +1,210 @@
+/**
+ * Copyright © 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <boost/asio/spawn.hpp>
+#include <boost/callable_traits.hpp>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+namespace ipmi
+{
+
+struct Context;
+
+namespace utility
+{
+
+/**
+ * @brief a utility template to extract the args after N from a tuple
+ *
+ * Given a tuple of type <T1, ...TN, TN+1, ...>, provide type = tuple<TN+1,...>
+ */
+template <std::size_t N, typename FirstArg, typename... Rest>
+struct StripFirstArgs;
+
+template <std::size_t N, typename FirstArg, typename... Rest>
+struct StripFirstArgs<N, std::tuple<FirstArg, Rest...>>
+ : StripFirstArgs<N - 1, std::tuple<Rest...>>
+{
+};
+
+template <typename FirstArg, typename... Rest>
+struct StripFirstArgs<0, std::tuple<FirstArg, Rest...>>
+{
+ using type = std::tuple<FirstArg, Rest...>;
+};
+template <std::size_t N>
+struct StripFirstArgs<N, std::tuple<>>
+{
+ using type = std::tuple<>;
+};
+
+/**
+ * @brief a utility template to extract the remaining args from a tuple
+ *
+ * Given a tuple of type <T1, T2,...>, provide type = tuple<T2,...>
+ */
+template <typename T>
+using StripFirstArg = StripFirstArgs<1, T>;
+
+/**
+ * @brief a utility template to find the number of non-special arguments
+ *
+ * Given a tuple, count the args after the first special args
+ */
+template <typename FirstArg, typename... Rest>
+struct NonIpmiArgsCount;
+
+template <>
+struct NonIpmiArgsCount<std::tuple<>>
+{
+ constexpr static std::size_t size()
+ {
+ return 0;
+ }
+};
+template <typename FirstArg, typename... OtherArgs>
+struct NonIpmiArgsCount<std::tuple<FirstArg, OtherArgs...>>
+{
+ constexpr static std::size_t size()
+ {
+ if constexpr (std::is_same<FirstArg, ipmi::Context>::value ||
+ std::is_same<FirstArg, boost::asio::yield_context>::value)
+ {
+ return 1 + NonIpmiArgsCount<std::tuple<OtherArgs...>>::size();
+ }
+ else
+ {
+ return NonIpmiArgsCount<std::tuple<OtherArgs...>>::size();
+ }
+ }
+};
+
+/**
+ * @brief a utility template to find the type of the first arg
+ *
+ * Given a tuple, provide the type of the first element
+ */
+template <typename T>
+struct GetFirstArg
+{
+ using type = void;
+};
+
+template <typename FirstArg, typename... Rest>
+struct GetFirstArg<std::tuple<FirstArg, Rest...>>
+{
+ using type = FirstArg;
+};
+
+/**
+ * @brief a utility template to remove const and reference from types
+ *
+ * Given a tuple, provide the type of the first element
+ */
+template <typename... Args>
+struct DecayTuple;
+
+template <typename... Args>
+struct DecayTuple<std::tuple<Args...>>
+{
+ using type = std::tuple<typename std::decay<Args>::type...>;
+};
+
+/** @brief Convert T[N] to T* if is_same<Tbase,T>
+ *
+ * @tparam Tbase - The base type expected.
+ * @tparam T - The type to convert.
+ */
+template <typename Tbase, typename T>
+using ArrayToPtr_t = typename std::conditional_t<
+ std::is_array<T>::value,
+ std::conditional_t<std::is_same<Tbase, std::remove_extent_t<T>>::value,
+ std::add_pointer_t<std::remove_extent_t<T>>, T>,
+ T>;
+
+/** @brief Downcast type submembers.
+ *
+ * This allows std::tuple and std::pair members to be downcast to their
+ * non-const, nonref versions of themselves to limit duplication in template
+ * specializations
+ *
+ * 1. Remove references.
+ * 2. Remove 'const' and 'volatile'.
+ * 3. Convert 'char[N]' to 'char*'.
+ */
+template <typename T>
+struct DowncastMembers
+{
+ using type = T;
+};
+template <typename... Args>
+struct DowncastMembers<std::pair<Args...>>
+{
+ using type = std::pair<utility::ArrayToPtr_t<
+ char, std::remove_cv_t<std::remove_reference_t<Args>>>...>;
+};
+
+template <typename... Args>
+struct DowncastMembers<std::tuple<Args...>>
+{
+ using type = std::tuple<utility::ArrayToPtr_t<
+ char, std::remove_cv_t<std::remove_reference_t<Args>>>...>;
+};
+
+template <typename T>
+using DowncastMembers_t = typename DowncastMembers<T>::type;
+
+/** @brief Convert some C++ types to others for 'TypeId' conversion purposes.
+ *
+ * Similar C++ types have the same dbus type-id, so 'downcast' those to limit
+ * duplication in TypeId template specializations.
+ *
+ * 1. Remove references.
+ * 2. Remove 'const' and 'volatile'.
+ * 3. Convert 'char[N]' to 'char*'.
+ */
+template <typename T>
+struct TypeIdDowncast
+{
+ using type = utility::ArrayToPtr_t<
+ char, DowncastMembers_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
+};
+
+template <typename T>
+using TypeIdDowncast_t = typename TypeIdDowncast<T>::type;
+
+/** @brief Detect if a type is a tuple
+ *
+ */
+template <typename>
+struct is_tuple : std::false_type
+{
+};
+
+template <typename... T>
+struct is_tuple<std::tuple<T...>> : std::true_type
+{
+};
+
+} // namespace utility
+
+} // namespace ipmi
diff --git a/user_channel/Makefile.am b/user_channel/Makefile.am
index 5ec2104..460b18e 100644
--- a/user_channel/Makefile.am
+++ b/user_channel/Makefile.am
@@ -8,6 +8,8 @@
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
-DBOOST_ERROR_CODE_HEADER_ONLY \
-DBOOST_SYSTEM_NO_DEPRECATED \
+ -DBOOST_COROUTINES_NO_DEPRECATION_WARNING \
+ -DBOOST_ASIO_DISABLE_THREADS \
-DBOOST_ALL_NO_LIB
lib_LTLIBRARIES = libuserlayer.la libchannellayer.la