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