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