xref: /openbmc/phosphor-host-ipmid/include/ipmid/message/unpack.hpp (revision 9706bc8d269accb99ec9ada035838f28db441f97)
1e7329c71SVernon Mauery /**
2e7329c71SVernon Mauery  * Copyright © 2018 Intel Corporation
3e7329c71SVernon Mauery  *
4e7329c71SVernon Mauery  * Licensed under the Apache License, Version 2.0 (the "License");
5e7329c71SVernon Mauery  * you may not use this file except in compliance with the License.
6e7329c71SVernon Mauery  * You may obtain a copy of the License at
7e7329c71SVernon Mauery  *
8e7329c71SVernon Mauery  *     http://www.apache.org/licenses/LICENSE-2.0
9e7329c71SVernon Mauery  *
10e7329c71SVernon Mauery  * Unless required by applicable law or agreed to in writing, software
11e7329c71SVernon Mauery  * distributed under the License is distributed on an "AS IS" BASIS,
12e7329c71SVernon Mauery  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e7329c71SVernon Mauery  * See the License for the specific language governing permissions and
14e7329c71SVernon Mauery  * limitations under the License.
15e7329c71SVernon Mauery  */
16e7329c71SVernon Mauery #pragma once
17e7329c71SVernon Mauery 
18*9706bc8dSJayanth Othayoth #include <ipmid/message.hpp>
19e7329c71SVernon Mauery #include <ipmid/message/types.hpp>
20fbc6c9d7SPatrick Williams 
21fbc6c9d7SPatrick Williams #include <array>
22e7329c71SVernon Mauery #include <optional>
23c329ceeaSWilly Tu #include <span>
24e7329c71SVernon Mauery #include <string>
25e7329c71SVernon Mauery #include <tuple>
26e7329c71SVernon Mauery #include <vector>
27e7329c71SVernon Mauery 
28e7329c71SVernon Mauery namespace ipmi
29e7329c71SVernon Mauery {
30e7329c71SVernon Mauery 
31e7329c71SVernon Mauery namespace message
32e7329c71SVernon Mauery {
33e7329c71SVernon Mauery 
34e7329c71SVernon Mauery namespace details
35e7329c71SVernon Mauery {
36e7329c71SVernon Mauery 
37e7329c71SVernon Mauery /**************************************
38e7329c71SVernon Mauery  * ipmi return type helpers
39e7329c71SVernon Mauery  **************************************/
40e7329c71SVernon Mauery 
41e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
UnpackBytes(uint8_t * pointer,NumericType & i)42e7329c71SVernon Mauery void UnpackBytes(uint8_t* pointer, NumericType& i)
43e7329c71SVernon Mauery {
44e7329c71SVernon Mauery     if constexpr (byteIndex < sizeof(NumericType))
45e7329c71SVernon Mauery     {
46e7329c71SVernon Mauery         i |= static_cast<NumericType>(*pointer) << (CHAR_BIT * byteIndex);
47e7329c71SVernon Mauery         UnpackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
48e7329c71SVernon Mauery     }
49e7329c71SVernon Mauery }
50e7329c71SVernon Mauery 
51e7329c71SVernon Mauery template <typename NumericType, size_t byteIndex = 0>
UnpackBytesUnaligned(Payload & p,NumericType & i)52e7329c71SVernon Mauery void UnpackBytesUnaligned(Payload& p, NumericType& i)
53e7329c71SVernon Mauery {
54e7329c71SVernon Mauery     if constexpr (byteIndex < sizeof(NumericType))
55e7329c71SVernon Mauery     {
56e7329c71SVernon Mauery         i |= static_cast<NumericType>(p.popBits(CHAR_BIT))
57e7329c71SVernon Mauery              << (CHAR_BIT * byteIndex);
58e7329c71SVernon Mauery         UnpackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
59e7329c71SVernon Mauery     }
60e7329c71SVernon Mauery }
61e7329c71SVernon Mauery 
62e7329c71SVernon Mauery /** @struct UnpackSingle
63e7329c71SVernon Mauery  *  @brief Utility to unpack a single C++ element from a Payload
64e7329c71SVernon Mauery  *
65e7329c71SVernon Mauery  *  User-defined types are expected to specialize this template in order to
66e7329c71SVernon Mauery  *  get their functionality.
67e7329c71SVernon Mauery  *
68e7329c71SVernon Mauery  *  @tparam T - Type of element to unpack.
69e7329c71SVernon Mauery  */
70e7329c71SVernon Mauery template <typename T>
71e7329c71SVernon Mauery struct UnpackSingle
72e7329c71SVernon Mauery {
73e7329c71SVernon Mauery     /** @brief Do the operation to unpack element.
74e7329c71SVernon Mauery      *
75e7329c71SVernon Mauery      *  @param[in] p - Payload to unpack from.
76e7329c71SVernon Mauery      *  @param[out] t - The reference to unpack item into.
77e7329c71SVernon Mauery      */
opipmi::message::details::UnpackSingle78e7329c71SVernon Mauery     static int op(Payload& p, T& t)
79e7329c71SVernon Mauery     {
80e7329c71SVernon Mauery         if constexpr (std::is_fundamental<T>::value)
81e7329c71SVernon Mauery         {
82e7329c71SVernon Mauery             t = 0;
83e7329c71SVernon Mauery             if (p.bitCount)
84e7329c71SVernon Mauery             {
85e7329c71SVernon Mauery                 if (p.fillBits(CHAR_BIT * sizeof(t)))
86e7329c71SVernon Mauery                 {
87e7329c71SVernon Mauery                     return 1;
88e7329c71SVernon Mauery                 }
89e7329c71SVernon Mauery                 UnpackBytesUnaligned<T>(p, t);
90e7329c71SVernon Mauery             }
91e7329c71SVernon Mauery             else
92e7329c71SVernon Mauery             {
93e7329c71SVernon Mauery                 // copy out bits from vector....
94e7329c71SVernon Mauery                 if (p.raw.size() < (p.rawIndex + sizeof(t)))
95e7329c71SVernon Mauery                 {
96e7329c71SVernon Mauery                     return 1;
97e7329c71SVernon Mauery                 }
98e7329c71SVernon Mauery                 auto iter = p.raw.data() + p.rawIndex;
99e7329c71SVernon Mauery                 t = 0;
100e7329c71SVernon Mauery                 UnpackBytes<T>(iter, t);
101e7329c71SVernon Mauery                 p.rawIndex += sizeof(t);
102e7329c71SVernon Mauery             }
103e7329c71SVernon Mauery             return 0;
104e7329c71SVernon Mauery         }
105a3dd7661SVernon Mauery         else if constexpr (utility::is_tuple<T>::value)
106e7329c71SVernon Mauery         {
107e7329c71SVernon Mauery             bool priorError = p.unpackError;
108e7329c71SVernon Mauery             size_t priorIndex = p.rawIndex;
109e7329c71SVernon Mauery             // more stuff to unroll if partial bytes are out
110e7329c71SVernon Mauery             size_t priorBitCount = p.bitCount;
111e7329c71SVernon Mauery             fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
112e7329c71SVernon Mauery             int ret = p.unpack(t);
113e7329c71SVernon Mauery             if (ret != 0)
114e7329c71SVernon Mauery             {
115e7329c71SVernon Mauery                 t = T();
116e7329c71SVernon Mauery                 p.rawIndex = priorIndex;
117e7329c71SVernon Mauery                 p.bitStream = priorBits;
118e7329c71SVernon Mauery                 p.bitCount = priorBitCount;
119e7329c71SVernon Mauery                 p.unpackError = priorError;
120e7329c71SVernon Mauery             }
121caabc36bSVernon Mauery             return ret;
122e7329c71SVernon Mauery         }
123a3dd7661SVernon Mauery         else
124a3dd7661SVernon Mauery         {
125a3dd7661SVernon Mauery             static_assert(
126a3dd7661SVernon Mauery                 utility::dependent_false<T>::value,
127a3dd7661SVernon Mauery                 "Attempt to unpack a type that has no IPMI unpack operation");
128e7329c71SVernon Mauery         }
129e7329c71SVernon Mauery     }
130e7329c71SVernon Mauery };
131e7329c71SVernon Mauery 
132e7329c71SVernon Mauery /** @struct UnpackSingle
133e7329c71SVernon Mauery  *  @brief Utility to unpack a single C++ element from a Payload
134e7329c71SVernon Mauery  *
135e7329c71SVernon Mauery  *  Specialization to unpack std::string represented as a
136e7329c71SVernon Mauery  *  UCSD-Pascal style string
137e7329c71SVernon Mauery  */
138e7329c71SVernon Mauery template <>
139e7329c71SVernon Mauery struct UnpackSingle<std::string>
140e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle141e7329c71SVernon Mauery     static int op(Payload& p, std::string& t)
142e7329c71SVernon Mauery     {
143e7329c71SVernon Mauery         // pop len first
144e7329c71SVernon Mauery         if (p.rawIndex > (p.raw.size() - sizeof(uint8_t)))
145e7329c71SVernon Mauery         {
146e7329c71SVernon Mauery             return 1;
147e7329c71SVernon Mauery         }
148e7329c71SVernon Mauery         uint8_t len = p.raw[p.rawIndex++];
149e7329c71SVernon Mauery         // check to see that there are n bytes left
150e7329c71SVernon Mauery         auto [first, last] = p.pop<char>(len);
151e7329c71SVernon Mauery         if (first == last)
152e7329c71SVernon Mauery         {
153e7329c71SVernon Mauery             return 1;
154e7329c71SVernon Mauery         }
155e7329c71SVernon Mauery         t.reserve(last - first);
156e7329c71SVernon Mauery         t.insert(0, first, (last - first));
157e7329c71SVernon Mauery         return 0;
158e7329c71SVernon Mauery     }
159e7329c71SVernon Mauery };
160e7329c71SVernon Mauery 
161e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for fixed_uint_t types
162e7329c71SVernon Mauery  */
163b4905919STim Lee template <bitcount_t N>
164e7329c71SVernon Mauery struct UnpackSingle<fixed_uint_t<N>>
165e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle166e7329c71SVernon Mauery     static int op(Payload& p, fixed_uint_t<N>& t)
167e7329c71SVernon Mauery     {
168e7329c71SVernon Mauery         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
169e7329c71SVernon Mauery         constexpr size_t count = N;
170e7329c71SVernon Mauery         // acquire enough bits in the stream to fulfill the Payload
171e7329c71SVernon Mauery         if (p.fillBits(count))
172e7329c71SVernon Mauery         {
173e7329c71SVernon Mauery             return -1;
174e7329c71SVernon Mauery         }
175e7329c71SVernon Mauery         fixed_uint_t<details::bitStreamSize> bitmask = ((1 << count) - 1);
176e7329c71SVernon Mauery         t = (p.bitStream & bitmask).convert_to<fixed_uint_t<N>>();
177e7329c71SVernon Mauery         p.bitStream >>= count;
178e7329c71SVernon Mauery         p.bitCount -= count;
179e7329c71SVernon Mauery         return 0;
180e7329c71SVernon Mauery     }
181e7329c71SVernon Mauery };
182e7329c71SVernon Mauery 
183e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for bool. */
184e7329c71SVernon Mauery template <>
185e7329c71SVernon Mauery struct UnpackSingle<bool>
186e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle187e7329c71SVernon Mauery     static int op(Payload& p, bool& b)
188e7329c71SVernon Mauery     {
189e7329c71SVernon Mauery         // acquire enough bits in the stream to fulfill the Payload
190e7329c71SVernon Mauery         if (p.fillBits(1))
191e7329c71SVernon Mauery         {
192e7329c71SVernon Mauery             return -1;
193e7329c71SVernon Mauery         }
194e7329c71SVernon Mauery         b = static_cast<bool>(p.bitStream & 0x01);
195e7329c71SVernon Mauery         // clear bits from stream
196e7329c71SVernon Mauery         p.bitStream >>= 1;
197e7329c71SVernon Mauery         p.bitCount -= 1;
198e7329c71SVernon Mauery         return 0;
199e7329c71SVernon Mauery     }
200e7329c71SVernon Mauery };
201e7329c71SVernon Mauery 
202e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::bitset<N>
203e7329c71SVernon Mauery  */
204e7329c71SVernon Mauery template <size_t N>
205e7329c71SVernon Mauery struct UnpackSingle<std::bitset<N>>
206e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle207e7329c71SVernon Mauery     static int op(Payload& p, std::bitset<N>& t)
208e7329c71SVernon Mauery     {
209e7329c71SVernon Mauery         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
210e7329c71SVernon Mauery         size_t count = N;
211e7329c71SVernon Mauery         // acquire enough bits in the stream to fulfill the Payload
212e7329c71SVernon Mauery         if (p.fillBits(count))
213e7329c71SVernon Mauery         {
214e7329c71SVernon Mauery             return -1;
215e7329c71SVernon Mauery         }
2160d49e479SWilliam A. Kennington III         fixed_uint_t<details::bitStreamSize> bitmask =
2170d49e479SWilliam A. Kennington III             ~fixed_uint_t<details::bitStreamSize>(0) >>
2180d49e479SWilliam A. Kennington III             (details::bitStreamSize - count);
219e7329c71SVernon Mauery         t |= (p.bitStream & bitmask).convert_to<unsigned long long>();
220e7329c71SVernon Mauery         p.bitStream >>= count;
221e7329c71SVernon Mauery         p.bitCount -= count;
222e7329c71SVernon Mauery         return 0;
223e7329c71SVernon Mauery     }
224e7329c71SVernon Mauery };
225e7329c71SVernon Mauery 
226e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::optional<T> */
227e7329c71SVernon Mauery template <typename T>
228e7329c71SVernon Mauery struct UnpackSingle<std::optional<T>>
229e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle230e7329c71SVernon Mauery     static int op(Payload& p, std::optional<T>& t)
231e7329c71SVernon Mauery     {
232e7329c71SVernon Mauery         bool priorError = p.unpackError;
233e7329c71SVernon Mauery         size_t priorIndex = p.rawIndex;
234e7329c71SVernon Mauery         // more stuff to unroll if partial bytes are out
235e7329c71SVernon Mauery         size_t priorBitCount = p.bitCount;
236e7329c71SVernon Mauery         fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
237202702b0SJonathan Doman         T value;
238202702b0SJonathan Doman         int ret = UnpackSingle<T>::op(p, value);
239e7329c71SVernon Mauery         if (ret != 0)
240e7329c71SVernon Mauery         {
241e7329c71SVernon Mauery             t.reset();
242e7329c71SVernon Mauery             p.rawIndex = priorIndex;
243e7329c71SVernon Mauery             p.bitStream = priorBits;
244e7329c71SVernon Mauery             p.bitCount = priorBitCount;
245e7329c71SVernon Mauery             p.unpackError = priorError;
246e7329c71SVernon Mauery         }
247202702b0SJonathan Doman         else
248202702b0SJonathan Doman         {
249202702b0SJonathan Doman             t.emplace(std::move(value));
250202702b0SJonathan Doman         }
251e7329c71SVernon Mauery         return 0;
252e7329c71SVernon Mauery     }
253e7329c71SVernon Mauery };
254e7329c71SVernon Mauery 
255e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::array<T, N> */
256e7329c71SVernon Mauery template <typename T, size_t N>
257e7329c71SVernon Mauery struct UnpackSingle<std::array<T, N>>
258e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle259e7329c71SVernon Mauery     static int op(Payload& p, std::array<T, N>& t)
260e7329c71SVernon Mauery     {
261e7329c71SVernon Mauery         int ret = 0;
262e7329c71SVernon Mauery         size_t priorIndex = p.rawIndex;
263e7329c71SVernon Mauery         for (auto& v : t)
264e7329c71SVernon Mauery         {
265e7329c71SVernon Mauery             ret = UnpackSingle<T>::op(p, v);
266e7329c71SVernon Mauery             if (ret)
267e7329c71SVernon Mauery             {
268e7329c71SVernon Mauery                 p.rawIndex = priorIndex;
269e7329c71SVernon Mauery                 t = std::array<T, N>();
270e7329c71SVernon Mauery                 break;
271e7329c71SVernon Mauery             }
272e7329c71SVernon Mauery         }
273e7329c71SVernon Mauery         return ret;
274e7329c71SVernon Mauery     }
275e7329c71SVernon Mauery };
276e7329c71SVernon Mauery 
277e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::array<uint8_t> */
278e7329c71SVernon Mauery template <size_t N>
279e7329c71SVernon Mauery struct UnpackSingle<std::array<uint8_t, N>>
280e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle281e7329c71SVernon Mauery     static int op(Payload& p, std::array<uint8_t, N>& t)
282e7329c71SVernon Mauery     {
283e7329c71SVernon Mauery         if (p.raw.size() - p.rawIndex < N)
284e7329c71SVernon Mauery         {
285e7329c71SVernon Mauery             t.fill(0);
286e7329c71SVernon Mauery             return -1;
287e7329c71SVernon Mauery         }
288e7329c71SVernon Mauery         // copy out the bytes
289e7329c71SVernon Mauery         std::copy(p.raw.begin() + p.rawIndex, p.raw.begin() + p.rawIndex + N,
290e7329c71SVernon Mauery                   t.begin());
291e7329c71SVernon Mauery         p.rawIndex += N;
292e7329c71SVernon Mauery         return 0;
293e7329c71SVernon Mauery     }
294e7329c71SVernon Mauery };
295e7329c71SVernon Mauery 
296e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::vector<T> */
297e7329c71SVernon Mauery template <typename T>
298e7329c71SVernon Mauery struct UnpackSingle<std::vector<T>>
299e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle300e7329c71SVernon Mauery     static int op(Payload& p, std::vector<T>& t)
301e7329c71SVernon Mauery     {
302e7329c71SVernon Mauery         while (p.rawIndex < p.raw.size())
303e7329c71SVernon Mauery         {
304e7329c71SVernon Mauery             t.emplace_back();
305caabc36bSVernon Mauery             if (UnpackSingle<T>::op(p, t.back()))
306e7329c71SVernon Mauery             {
307e7329c71SVernon Mauery                 t.pop_back();
308e7329c71SVernon Mauery                 break;
309e7329c71SVernon Mauery             }
310e7329c71SVernon Mauery         }
311caabc36bSVernon Mauery         // unpacking a vector is always successful:
312caabc36bSVernon Mauery         // either stuff was unpacked successfully (return 0)
313caabc36bSVernon Mauery         // or stuff was not unpacked, but should still return
314caabc36bSVernon Mauery         // success because an empty vector or a not-fully-unpacked
315caabc36bSVernon Mauery         // payload is not a failure.
316caabc36bSVernon Mauery         return 0;
317e7329c71SVernon Mauery     }
318e7329c71SVernon Mauery };
319e7329c71SVernon Mauery 
320e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for std::vector<uint8_t> */
321e7329c71SVernon Mauery template <>
322e7329c71SVernon Mauery struct UnpackSingle<std::vector<uint8_t>>
323e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle324e7329c71SVernon Mauery     static int op(Payload& p, std::vector<uint8_t>& t)
325e7329c71SVernon Mauery     {
326e7329c71SVernon Mauery         // copy out the remainder of the message
327e7329c71SVernon Mauery         t.reserve(p.raw.size() - p.rawIndex);
328e7329c71SVernon Mauery         t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
329e7329c71SVernon Mauery         p.rawIndex = p.raw.size();
330e7329c71SVernon Mauery         return 0;
331e7329c71SVernon Mauery     }
332e7329c71SVernon Mauery };
333e7329c71SVernon Mauery 
334997952afSVernon Mauery /** @brief Specialization of UnpackSingle for SecureBuffer */
335997952afSVernon Mauery template <>
336997952afSVernon Mauery struct UnpackSingle<SecureBuffer>
337997952afSVernon Mauery {
opipmi::message::details::UnpackSingle338997952afSVernon Mauery     static int op(Payload& p, SecureBuffer& t)
339997952afSVernon Mauery     {
340997952afSVernon Mauery         // copy out the remainder of the message
341997952afSVernon Mauery         t.reserve(p.raw.size() - p.rawIndex);
342997952afSVernon Mauery         t.insert(t.begin(), p.raw.begin() + p.rawIndex, p.raw.end());
343997952afSVernon Mauery         p.rawIndex = p.raw.size();
344997952afSVernon Mauery         return 0;
345997952afSVernon Mauery     }
346997952afSVernon Mauery };
347997952afSVernon Mauery 
348c329ceeaSWilly Tu /** @brief Specialization of UnpackSingle for std::span<const uint8_t> */
349c329ceeaSWilly Tu template <>
350c329ceeaSWilly Tu struct UnpackSingle<std::span<const uint8_t>>
351c329ceeaSWilly Tu {
opipmi::message::details::UnpackSingle352c329ceeaSWilly Tu     static int op(Payload& p, std::span<const uint8_t>& t)
353c329ceeaSWilly Tu     {
354c329ceeaSWilly Tu         // copy out the remainder of the message
355c329ceeaSWilly Tu         t = std::span<const uint8_t>(p.raw.begin() + p.rawIndex, p.raw.end());
356c329ceeaSWilly Tu         p.rawIndex = p.raw.size();
357c329ceeaSWilly Tu         return 0;
358c329ceeaSWilly Tu     }
359c329ceeaSWilly Tu };
360c329ceeaSWilly Tu 
361e7329c71SVernon Mauery /** @brief Specialization of UnpackSingle for Payload */
362e7329c71SVernon Mauery template <>
363e7329c71SVernon Mauery struct UnpackSingle<Payload>
364e7329c71SVernon Mauery {
opipmi::message::details::UnpackSingle365e7329c71SVernon Mauery     static int op(Payload& p, Payload& t)
366e7329c71SVernon Mauery     {
36751694c22SWilliam A. Kennington III         t = p;
368e7329c71SVernon Mauery         // mark that this payload is being included in the args
369e7329c71SVernon Mauery         p.trailingOk = true;
370e7329c71SVernon Mauery         return 0;
371e7329c71SVernon Mauery     }
372e7329c71SVernon Mauery };
373e7329c71SVernon Mauery 
374e7329c71SVernon Mauery } // namespace details
375e7329c71SVernon Mauery 
376e7329c71SVernon Mauery } // namespace message
377e7329c71SVernon Mauery 
378e7329c71SVernon Mauery } // namespace ipmi
379