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