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