diff options
| author | Vernon Mauery <vernon.mauery@linux.intel.com> | 2018-10-08 12:05:16 -0700 |
|---|---|---|
| committer | Vernon Mauery <vernon.mauery@linux.intel.com> | 2019-02-25 14:27:21 -0800 |
| commit | e7329c71f3d22e010c38a7f738e81ab78330038e (patch) | |
| tree | 8cb355cbde96d7a341f1c707c7f0e8c334f88e58 /include/ipmid/message | |
| parent | 1bb0c7fc55b21bb40b7afb567f2f938f15411ca1 (diff) | |
| download | phosphor-host-ipmid-e7329c71f3d22e010c38a7f738e81ab78330038e.tar.gz phosphor-host-ipmid-e7329c71f3d22e010c38a7f738e81ab78330038e.zip | |
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>
Diffstat (limited to 'include/ipmid/message')
| -rw-r--r-- | include/ipmid/message/pack.hpp | 222 | ||||
| -rw-r--r-- | include/ipmid/message/types.hpp | 110 | ||||
| -rw-r--r-- | include/ipmid/message/unpack.hpp | 340 |
3 files changed, 672 insertions, 0 deletions
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 |

