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