blob: 4079eabb1608b0319bb3839831b6e103f2ed36f6 [file] [log] [blame]
Vernon Mauerye7329c72018-10-08 12:05:16 -07001/**
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 <algorithm>
19#include <boost/asio/spawn.hpp>
20#include <cstdint>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070021#include <ipmid/api-types.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070022#include <ipmid/message/types.hpp>
23#include <memory>
24#include <phosphor-logging/log.hpp>
25#include <tuple>
26#include <utility>
27#include <vector>
28
29namespace ipmi
30{
31
32struct Context
33{
34 using ptr = std::shared_ptr<Context>;
35
36 Context() = default;
37
38 Context(NetFn netFn, Cmd cmd, int channel, int userId, Privilege priv,
39 boost::asio::yield_context* yield = nullptr) :
40 netFn(netFn),
41 cmd(cmd), channel(channel), userId(userId), priv(priv), yield(yield)
42 {
43 }
44
45 // normal IPMI context (what call is this, from whence it came...)
46 NetFn netFn = 0;
47 Cmd cmd = 0;
48 int channel = 0;
49 int userId = 0;
50 Privilege priv = Privilege::None;
51 // if non-null, use this to do blocking asynchronous asio calls
52 boost::asio::yield_context* yield = nullptr;
53};
54
55namespace message
56{
57
58namespace details
59{
60
61template <typename A>
62struct UnpackSingle;
63
64template <typename T>
65using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>;
66
67template <typename A>
68struct PackSingle;
69
70template <typename T>
71using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>;
72
73// size to hold 64 bits plus one (possibly-)partial byte
74static constexpr size_t bitStreamSize = ((sizeof(uint64_t) + 1) * CHAR_BIT);
75
76} // namespace details
77
78/**
79 * @brief a payload class that provides a mechanism to pack and unpack data
80 *
81 * When a new request is being executed, the Payload class is responsible for
82 * attempting to unpack all the required arguments from the incoming blob. For
83 * variable-length functions, it is possible to have function signature have a
84 * Payload object, which will then allow the remaining data to be extracted as
85 * needed.
86 *
87 * When creating a response, the parameters returned from the callback use a
88 * newly created payload object to pack all the parameters into a buffer that is
89 * then returned to the requester.
90 *
91 * These interfaces make calls into the message/pack.hpp and message/unpack.hpp
92 * functions.
93 */
94struct Payload
95{
96 Payload() = default;
97 Payload(const Payload&) = default;
98 Payload& operator=(const Payload&) = default;
99 Payload(Payload&&) = default;
100 Payload& operator=(Payload&&) = default;
101
102 explicit Payload(std::vector<uint8_t>&& data) :
103 raw(std::move(data)), unpackCheck(false)
104 {
105 }
106
107 ~Payload()
108 {
109 using namespace phosphor::logging;
110 if (trailingOk && !unpackCheck && !fullyUnpacked())
111 {
112 log<level::ERR>("Failed to check request for full unpack");
113 }
114 }
115
116 /******************************************************************
117 * raw vector access
118 *****************************************************************/
119 /**
120 * @brief return the size of the underlying raw buffer
121 */
122 size_t size() const
123 {
124 return raw.size();
125 }
126 /**
127 * @brief resize the underlying raw buffer to a new size
128 *
129 * @param sz - new size for the buffer
130 */
131 void resize(size_t sz)
132 {
133 raw.resize(sz);
134 }
135 /**
136 * @brief return a pointer to the underlying raw buffer
137 */
138 uint8_t* data()
139 {
140 return raw.data();
141 }
142 /**
143 * @brief return a const pointer to the underlying raw buffer
144 */
145 const uint8_t* data() const
146 {
147 return raw.data();
148 }
149
150 /******************************************************************
151 * Response operations
152 *****************************************************************/
153 /**
154 * @brief append a series of bytes to the buffer
155 *
156 * @tparam T - the type pointer to return; must be compatible to a byte
157 *
158 * @param begin - a pointer to the beginning of the series
159 * @param end - a pointer to the end of the series
160 */
161 template <typename T>
162 void append(T* begin, T* end)
163 {
164 static_assert(
165 std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
166 std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
167 std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
168 "begin and end must be signed or unsigned byte pointers");
169 // this interface only allows full-byte access; pack in partial bytes
170 drain();
171 raw.insert(raw.end(), reinterpret_cast<const uint8_t*>(begin),
172 reinterpret_cast<const uint8_t*>(end));
173 }
174
175 /**
176 * @brief append a series of bits to the buffer
177 *
178 * Only the lowest @count order of bits will be appended, with the most
179 * significant of those bits getting appended first.
180 *
181 * @param count - number of bits to append
182 * @param bits - a byte with count significant bits to append
183 */
184 void appendBits(size_t count, uint8_t bits)
185 {
186 // drain whole bytes out
187 drain(true);
188
189 // add in the new bits as the higher-order bits, filling LSBit first
190 fixed_uint_t<details::bitStreamSize> tmp = bits;
191 tmp <<= bitCount;
192 bitStream |= tmp;
193 bitCount += count;
194
195 // drain any whole bytes we have appended
196 drain(true);
197 }
198
199 /**
200 * @brief empty out the bucket and pack it as bytes LSB-first
201 *
202 * @param wholeBytesOnly - if true, only the whole bytes will be drained
203 */
204 void drain(bool wholeBytesOnly = false)
205 {
206 while (bitCount > 0)
207 {
208 uint8_t retVal;
209 if (bitCount < CHAR_BIT)
210 {
211 if (wholeBytesOnly)
212 {
213 break;
214 }
215 }
216 size_t bitsOut = std::min(static_cast<size_t>(CHAR_BIT), bitCount);
217 retVal = static_cast<uint8_t>(bitStream);
218 raw.push_back(retVal);
219 bitStream >>= bitsOut;
220 bitCount -= bitsOut;
221 }
222 }
223
224 // base empty pack
225 int pack()
226 {
227 return 0;
228 }
229
230 /**
231 * @brief pack arbitrary values (of any supported type) into the buffer
232 *
233 * @tparam Arg - the type of the first argument
234 * @tparam Args - the type of the optional remaining arguments
235 *
236 * @param arg - the first argument to pack
237 * @param args... - the optional remaining arguments to pack
238 *
239 * @return int - non-zero on pack errors
240 */
241 template <typename Arg, typename... Args>
242 int pack(Arg&& arg, Args&&... args)
243 {
244 int packRet =
245 details::PackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
246 if (packRet)
247 {
248 return packRet;
249 }
250 packRet = pack(std::forward<Args>(args)...);
251 drain();
252 return packRet;
253 }
254
255 /**
256 * @brief pack a tuple of values (of any supported type) into the buffer
257 *
258 * This will pack the elements of the tuple as if each one was passed in
259 * individually, as if passed into the above variadic function.
260 *
261 * @tparam Types - the implicitly declared list of the tuple element types
262 *
263 * @param t - the tuple of values to pack
264 *
265 * @return int - non-zero on pack errors
266 */
267 template <typename... Types>
268 int pack(std::tuple<Types...>& t)
269 {
270 return std::apply([this](Types&... args) { return pack(args...); }, t);
271 }
272
273 /******************************************************************
274 * Request operations
275 *****************************************************************/
276 /**
277 * @brief pop a series of bytes from the raw buffer
278 *
279 * @tparam T - the type pointer to return; must be compatible to a byte
280 *
281 * @param count - the number of bytes to return
282 *
283 * @return - a tuple of pointers (begin,begin+count)
284 */
285 template <typename T>
286 auto pop(size_t count)
287 {
288 static_assert(
289 std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
290 std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
291 std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
292 "T* must be signed or unsigned byte pointers");
293 // this interface only allows full-byte access; skip partial bits
294 if (bitCount)
295 {
296 // WARN on unused bits?
297 discardBits();
298 }
299 if (count <= (raw.size() - rawIndex))
300 {
301 auto range = std::make_tuple(
302 reinterpret_cast<T*>(raw.data() + rawIndex),
303 reinterpret_cast<T*>(raw.data() + rawIndex + count));
304 rawIndex += count;
305 return range;
306 }
307 unpackError = true;
308 return std::make_tuple(reinterpret_cast<T*>(NULL),
309 reinterpret_cast<T*>(NULL));
310 }
311
312 /**
313 * @brief fill bit stream with at least count bits for consumption
314 *
315 * @param count - number of bit needed
316 *
317 * @return - unpackError
318 */
319 bool fillBits(size_t count)
320 {
321 // add more bits to the top end of the bitstream
322 // so we consume bits least-significant first
323 if (count > (details::bitStreamSize - CHAR_BIT))
324 {
325 unpackError = true;
326 return unpackError;
327 }
328 while (bitCount < count)
329 {
330 if (rawIndex < raw.size())
331 {
332 fixed_uint_t<details::bitStreamSize> tmp = raw[rawIndex++];
333 tmp <<= bitCount;
334 bitStream |= tmp;
335 bitCount += CHAR_BIT;
336 }
337 else
338 {
339 // raw has run out of bytes to pop
340 unpackError = true;
341 return unpackError;
342 }
343 }
344 return false;
345 }
346
347 /**
348 * @brief consume count bits from bitstream (must call fillBits first)
349 *
350 * @param count - number of bit needed
351 *
352 * @return - count bits from stream
353 */
354 uint8_t popBits(size_t count)
355 {
356 if (bitCount < count)
357 {
358 unpackError = true;
359 return 0;
360 }
361 // consume bits low-order bits first
362 auto bits = bitStream.convert_to<uint8_t>();
363 bits &= ((1 << count) - 1);
364 bitStream >>= count;
365 bitCount -= count;
366 return bits;
367 }
368
369 /**
370 * @brief discard all partial bits
371 */
372 void discardBits()
373 {
374 bitStream = 0;
375 bitCount = 0;
376 }
377
378 /**
379 * @brief fully reset the unpack stream
380 */
381 void reset()
382 {
383 discardBits();
384 rawIndex = 0;
385 unpackError = false;
386 }
387
388 /**
389 * @brief check to see if the stream has been fully unpacked
390 *
391 * @return bool - true if the stream has been unpacked and has no errors
392 */
393 bool fullyUnpacked()
394 {
395 unpackCheck = true;
396 return raw.size() == rawIndex && bitCount == 0 && !unpackError;
397 }
398
399 // base empty unpack
400 int unpack()
401 {
402 return 0;
403 }
404
405 /**
406 * @brief unpack arbitrary values (of any supported type) from the buffer
407 *
408 * @tparam Arg - the type of the first argument
409 * @tparam Args - the type of the optional remaining arguments
410 *
411 * @param arg - the first argument to unpack
412 * @param args... - the optional remaining arguments to unpack
413 *
414 * @return int - non-zero for unpack error
415 */
416 template <typename Arg, typename... Args>
417 int unpack(Arg&& arg, Args&&... args)
418 {
419 int unpackRet =
420 details::UnpackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
421 if (unpackRet)
422 {
423 unpackError = true;
424 return unpackRet;
425 }
426 return unpack(std::forward<Args>(args)...);
427 }
428
429 /**
430 * @brief unpack a tuple of values (of any supported type) from the buffer
431 *
432 * This will unpack the elements of the tuple as if each one was passed in
433 * individually, as if passed into the above variadic function.
434 *
435 * @tparam Types - the implicitly declared list of the tuple element types
436 *
437 * @param t - the tuple of values to unpack
438 *
439 * @return int - non-zero on unpack error
440 */
441 template <typename... Types>
442 int unpack(std::tuple<Types...>& t)
443 {
444 // roll back checkpoint so that unpacking a tuple is atomic
445 size_t priorBitCount = bitCount;
446 size_t priorIndex = rawIndex;
447 fixed_uint_t<details::bitStreamSize> priorBits = bitStream;
448
449 int ret =
450 std::apply([this](Types&... args) { return unpack(args...); }, t);
451 if (ret)
452 {
453 bitCount = priorBitCount;
454 bitStream = priorBits;
455 rawIndex = priorIndex;
456 }
457
458 return ret;
459 }
460
461 // partial bytes in the form of bits
462 fixed_uint_t<details::bitStreamSize> bitStream;
463 size_t bitCount = 0;
464 std::vector<uint8_t> raw;
465 size_t rawIndex = 0;
466 bool trailingOk = false;
467 bool unpackCheck = true;
468 bool unpackError = false;
469};
470
471/**
472 * @brief high-level interface to an IPMI response
473 *
474 * Make it easy to just pack in the response args from the callback into a
475 * buffer that goes back to the requester.
476 */
477struct Response
478{
479 /* Define all of the basic class operations:
480 * Not allowed:
481 * - Default constructor to avoid nullptrs.
482 * Allowed:
483 * - Copy operations.
484 * - Move operations.
485 * - Destructor.
486 */
487 Response() = delete;
488 Response(const Response&) = default;
489 Response& operator=(const Response&) = default;
490 Response(Response&&) = default;
491 Response& operator=(Response&&) = default;
492 ~Response() = default;
493
494 using ptr = std::shared_ptr<Response>;
495
496 explicit Response(Context::ptr& context) :
497 payload(), ctx(context), cc(ccSuccess)
498 {
499 }
500
501 /**
502 * @brief pack arbitrary values (of any supported type) into the payload
503 *
504 * @tparam Args - the type of the optional arguments
505 *
506 * @param args... - the optional arguments to pack
507 *
508 * @return int - non-zero on pack errors
509 */
510 template <typename... Args>
511 int pack(Args&&... args)
512 {
513 return payload.pack(std::forward<Args>(args)...);
514 }
515
516 /**
517 * @brief pack a tuple of values (of any supported type) into the payload
518 *
519 * This will pack the elements of the tuple as if each one was passed in
520 * individually, as if passed into the above variadic function.
521 *
522 * @tparam Types - the implicitly declared list of the tuple element types
523 *
524 * @param t - the tuple of values to pack
525 *
526 * @return int - non-zero on pack errors
527 */
528 template <typename... Types>
529 int pack(std::tuple<Types...>& t)
530 {
531 return payload.pack(t);
532 }
533
534 Payload payload;
535 Context::ptr ctx;
536 Cc cc;
537};
538
539/**
540 * @brief high-level interface to an IPMI request
541 *
542 * Make it easy to unpack the buffer into the request args for the callback.
543 */
544struct Request
545{
546 /* Define all of the basic class operations:
547 * Not allowed:
548 * - Default constructor to avoid nullptrs.
549 * Allowed:
550 * - Copy operations.
551 * - Move operations.
552 * - Destructor.
553 */
554 Request() = delete;
555 Request(const Request&) = default;
556 Request& operator=(const Request&) = default;
557 Request(Request&&) = default;
558 Request& operator=(Request&&) = default;
559 ~Request() = default;
560
561 using ptr = std::shared_ptr<Request>;
562
563 explicit Request(Context::ptr context, std::vector<uint8_t>&& d) :
564 payload(std::forward<std::vector<uint8_t>>(d)), ctx(context)
565 {
566 }
567
568 /**
569 * @brief unpack arbitrary values (of any supported type) from the payload
570 *
571 * @tparam Args - the type of the optional arguments
572 *
573 * @param args... - the optional arguments to unpack
574 *
575 * @return int - non-zero for unpack error
576 */
577 template <typename... Args>
578 int unpack(Args&&... args)
579 {
580 int unpackRet = payload.unpack(std::forward<Args>(args)...);
581 if (unpackRet == ipmi::ccSuccess)
582 {
583 if (!payload.trailingOk)
584 {
585 if (!payload.fullyUnpacked())
586 {
587 // not all bits were consumed by requested parameters
588 return ipmi::ccReqDataLenInvalid;
589 }
590 payload.unpackCheck = false;
591 }
592 }
593 return unpackRet;
594 }
595
596 /**
597 * @brief unpack a tuple of values (of any supported type) from the payload
598 *
599 * This will unpack the elements of the tuple as if each one was passed in
600 * individually, as if passed into the above variadic function.
601 *
602 * @tparam Types - the implicitly declared list of the tuple element types
603 *
604 * @param t - the tuple of values to unpack
605 *
606 * @return int - non-zero on unpack error
607 */
608 template <typename... Types>
609 int unpack(std::tuple<Types...>& t)
610 {
611 return std::apply([this](Types&... args) { return unpack(args...); },
612 t);
613 }
614
615 /** @brief Create a response message that corresponds to this request
616 *
617 * @return A shared_ptr to the response message created
618 */
619 Response::ptr makeResponse()
620 {
621 return std::make_shared<Response>(ctx);
622 }
623
624 Payload payload;
625 Context::ptr ctx;
626};
627
628} // namespace message
629
630} // namespace ipmi
631
632// include packing and unpacking of types
633#include <ipmid/message/pack.hpp>
634#include <ipmid/message/unpack.hpp>