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