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