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