xref: /openbmc/phosphor-host-ipmid/include/ipmid/message/pack.hpp (revision 9706bc8d269accb99ec9ada035838f28db441f97)
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 <ipmid/message.hpp>
19 #include <ipmid/message/types.hpp>
20 #include <phosphor-logging/lg2.hpp>
21 #include <phosphor-logging/log.hpp>
22 
23 #include <array>
24 #include <memory>
25 #include <optional>
26 #include <span>
27 #include <string_view>
28 #include <tuple>
29 #include <utility>
30 #include <variant>
31 #include <vector>
32 
33 namespace ipmi
34 {
35 
36 namespace message
37 {
38 
39 namespace details
40 {
41 
42 /**************************************
43  * ipmi return type helpers
44  **************************************/
45 
46 template <typename NumericType, size_t byteIndex = 0>
PackBytes(uint8_t * pointer,const NumericType & i)47 void PackBytes(uint8_t* pointer, const NumericType& i)
48 {
49     if constexpr (byteIndex < sizeof(NumericType))
50     {
51         *pointer = static_cast<uint8_t>(i >> (8 * byteIndex));
52         PackBytes<NumericType, byteIndex + 1>(pointer + 1, i);
53     }
54 }
55 
56 template <typename NumericType, size_t byteIndex = 0>
PackBytesUnaligned(Payload & p,const NumericType & i)57 void PackBytesUnaligned(Payload& p, const NumericType& i)
58 {
59     if constexpr (byteIndex < sizeof(NumericType))
60     {
61         p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex)));
62         PackBytesUnaligned<NumericType, byteIndex + 1>(p, i);
63     }
64 }
65 
66 /** @struct PackSingle
67  *  @brief Utility to pack a single C++ element into a Payload
68  *
69  *  User-defined types are expected to specialize this template in order to
70  *  get their functionality.
71  *
72  *  @tparam S - Type of element to pack.
73  */
74 template <typename T>
75 struct PackSingle
76 {
77     /** @brief Do the operation to pack element.
78      *
79      *  @param[in] p - Payload to pack into.
80      *  @param[out] t - The reference to pack item into.
81      */
opipmi::message::details::PackSingle82     static int op(Payload& p, const T& t)
83     {
84         static_assert(std::is_integral_v<T>,
85                       "Attempt to pack a type that has no IPMI pack operation");
86         // if not on a byte boundary, must pack values LSbit/LSByte first
87         if (p.bitCount)
88         {
89             PackBytesUnaligned<T>(p, t);
90         }
91         else
92         {
93             // copy in bits to vector....
94             p.raw.resize(p.raw.size() + sizeof(T));
95             uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T);
96             PackBytes<T>(out, t);
97         }
98         return 0;
99     }
100 };
101 
102 /** @brief Specialization of PackSingle for std::tuple<T> */
103 template <typename... T>
104 struct PackSingle<std::tuple<T...>>
105 {
opipmi::message::details::PackSingle106     static int op(Payload& p, const std::tuple<T...>& v)
107     {
108         return std::apply([&p](const T&... args) { return p.pack(args...); },
109                           v);
110     }
111 };
112 
113 /** @brief Specialization of PackSingle for std::string
114  *  represented as a UCSD-Pascal style string
115  */
116 template <>
117 struct PackSingle<std::string>
118 {
opipmi::message::details::PackSingle119     static int op(Payload& p, const std::string& t)
120     {
121         // check length first
122         uint8_t len;
123         if (t.length() > std::numeric_limits<decltype(len)>::max())
124         {
125             lg2::error("long string truncated on IPMI message pack");
126             return 1;
127         }
128         len = static_cast<uint8_t>(t.length());
129         PackSingle<uint8_t>::op(p, len);
130         p.append(t.c_str(), t.c_str() + t.length());
131         return 0;
132     }
133 };
134 
135 /** @brief Specialization of PackSingle for fixed_uint_t types
136  */
137 template <bitcount_t N>
138 struct PackSingle<fixed_uint_t<N>>
139 {
opipmi::message::details::PackSingle140     static int op(Payload& p, const fixed_uint_t<N>& t)
141     {
142         size_t count = N;
143         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
144         static_assert(N <= std::numeric_limits<uint64_t>::digits,
145                       "Type exceeds uint64_t limit");
146         uint64_t bits = static_cast<uint64_t>(t);
147         while (count > 0)
148         {
149             size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT));
150             p.appendBits(appendCount, static_cast<uint8_t>(bits));
151             bits >>= CHAR_BIT;
152             count -= appendCount;
153         }
154         return 0;
155     }
156 };
157 
158 /** @brief Specialization of PackSingle for bool. */
159 template <>
160 struct PackSingle<bool>
161 {
opipmi::message::details::PackSingle162     static int op(Payload& p, const bool& b)
163     {
164         p.appendBits(1, b);
165         return 0;
166     }
167 };
168 
169 /** @brief Specialization of PackSingle for std::bitset<N> */
170 template <size_t N>
171 struct PackSingle<std::bitset<N>>
172 {
opipmi::message::details::PackSingle173     static int op(Payload& p, const std::bitset<N>& t)
174     {
175         size_t count = N;
176         static_assert(N <= (details::bitStreamSize - CHAR_BIT));
177         unsigned long long bits = t.to_ullong();
178         while (count > 0)
179         {
180             size_t appendCount = std::min(count, size_t(CHAR_BIT));
181             p.appendBits(appendCount, static_cast<uint8_t>(bits));
182             bits >>= CHAR_BIT;
183             count -= appendCount;
184         }
185         return 0;
186     }
187 };
188 
189 /** @brief Specialization of PackSingle for std::optional<T> */
190 template <typename T>
191 struct PackSingle<std::optional<T>>
192 {
opipmi::message::details::PackSingle193     static int op(Payload& p, const std::optional<T>& t)
194     {
195         int ret = 0;
196         if (t)
197         {
198             ret = PackSingle<T>::op(p, *t);
199         }
200         return ret;
201     }
202 };
203 
204 /** @brief Specialization of PackSingle for std::array<T, N> */
205 template <typename T, size_t N>
206 struct PackSingle<std::array<T, N>>
207 {
opipmi::message::details::PackSingle208     static int op(Payload& p, const std::array<T, N>& t)
209     {
210         int ret = 0;
211         for (const auto& v : t)
212         {
213             int ret = PackSingle<T>::op(p, v);
214             if (ret)
215             {
216                 break;
217             }
218         }
219         return ret;
220     }
221 };
222 
223 /** @brief Specialization of PackSingle for std::vector<T> */
224 template <typename T>
225 struct PackSingle<std::vector<T>>
226 {
opipmi::message::details::PackSingle227     static int op(Payload& p, const std::vector<T>& t)
228     {
229         int ret = 0;
230         for (const auto& v : t)
231         {
232             int ret = PackSingle<T>::op(p, v);
233             if (ret)
234             {
235                 break;
236             }
237         }
238         return ret;
239     }
240 };
241 
242 /** @brief Specialization of PackSingle for std::vector<uint8_t> */
243 template <>
244 struct PackSingle<std::vector<uint8_t>>
245 {
opipmi::message::details::PackSingle246     static int op(Payload& p, const std::vector<uint8_t>& t)
247     {
248         if (p.bitCount != 0)
249         {
250             return 1;
251         }
252         p.raw.reserve(p.raw.size() + t.size());
253         p.raw.insert(p.raw.end(), t.begin(), t.end());
254         return 0;
255     }
256 };
257 
258 /** @brief Specialization of PackSingle for SecureBuffer */
259 template <>
260 struct PackSingle<SecureBuffer>
261 {
opipmi::message::details::PackSingle262     static int op(Payload& p, const SecureBuffer& t)
263     {
264         if (p.bitCount != 0)
265         {
266             return 1;
267         }
268         p.raw.reserve(p.raw.size() + t.size());
269         p.raw.insert(p.raw.end(), t.begin(), t.end());
270         return 0;
271     }
272 };
273 
274 /** @brief Specialization of PackSingle for std::span<const uint8_t> */
275 template <>
276 struct PackSingle<std::span<const uint8_t>>
277 {
opipmi::message::details::PackSingle278     static int op(Payload& p, const std::span<const uint8_t>& t)
279     {
280         if (p.bitCount != 0)
281         {
282             return 1;
283         }
284         p.raw.reserve(p.raw.size() + t.size());
285         p.raw.insert(p.raw.end(), t.begin(), t.end());
286         return 0;
287     }
288 };
289 
290 /** @brief Specialization of PackSingle for std::string_view */
291 template <>
292 struct PackSingle<std::string_view>
293 {
opipmi::message::details::PackSingle294     static int op(Payload& p, const std::string_view& t)
295     {
296         if (p.bitCount != 0)
297         {
298             return 1;
299         }
300         p.raw.reserve(p.raw.size() + t.size());
301         p.raw.insert(p.raw.end(), t.begin(), t.end());
302         return 0;
303     }
304 };
305 
306 /** @brief Specialization of PackSingle for std::variant<T, N> */
307 template <typename... T>
308 struct PackSingle<std::variant<T...>>
309 {
opipmi::message::details::PackSingle310     static int op(Payload& p, const std::variant<T...>& v)
311     {
312         return std::visit(
313             [&p](const auto& arg) {
314                 return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg);
315             },
316             v);
317     }
318 };
319 
320 /** @brief Specialization of PackSingle for Payload */
321 template <>
322 struct PackSingle<Payload>
323 {
opipmi::message::details::PackSingle324     static int op(Payload& p, const Payload& t)
325     {
326         if (p.bitCount != 0 || t.bitCount != 0)
327         {
328             return 1;
329         }
330         p.raw.reserve(p.raw.size() + t.raw.size());
331         p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end());
332         return 0;
333     }
334 };
335 
336 } // namespace details
337 
338 } // namespace message
339 
340 } // namespace ipmi
341