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