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      * Request operations
262      *****************************************************************/
263     /**
264      * @brief pop a series of bytes from the raw buffer
265      *
266      * @tparam T - the type pointer to return; must be compatible to a byte
267      *
268      * @param count - the number of bytes to return
269      *
270      * @return - a tuple of pointers (begin,begin+count)
271      */
272     template <typename T>
273     auto pop(size_t count)
274     {
275         static_assert(
276             std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
277                 std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
278                 std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
279             "T* must be signed or unsigned byte pointers");
280         // this interface only allows full-byte access; skip partial bits
281         if (bitCount)
282         {
283             // WARN on unused bits?
284             discardBits();
285         }
286         if (count <= (raw.size() - rawIndex))
287         {
288             auto range = std::make_tuple(
289                 reinterpret_cast<T*>(raw.data() + rawIndex),
290                 reinterpret_cast<T*>(raw.data() + rawIndex + count));
291             rawIndex += count;
292             return range;
293         }
294         unpackError = true;
295         return std::make_tuple(reinterpret_cast<T*>(NULL),
296                                reinterpret_cast<T*>(NULL));
297     }
298 
299     /**
300      * @brief fill bit stream with at least count bits for consumption
301      *
302      * @param count - number of bit needed
303      *
304      * @return - unpackError
305      */
306     bool fillBits(size_t count)
307     {
308         // add more bits to the top end of the bitstream
309         // so we consume bits least-significant first
310         if (count > (details::bitStreamSize - CHAR_BIT))
311         {
312             unpackError = true;
313             return unpackError;
314         }
315         while (bitCount < count)
316         {
317             if (rawIndex < raw.size())
318             {
319                 fixed_uint_t<details::bitStreamSize> tmp = raw[rawIndex++];
320                 tmp <<= bitCount;
321                 bitStream |= tmp;
322                 bitCount += CHAR_BIT;
323             }
324             else
325             {
326                 // raw has run out of bytes to pop
327                 unpackError = true;
328                 return unpackError;
329             }
330         }
331         return false;
332     }
333 
334     /**
335      * @brief consume count bits from bitstream (must call fillBits first)
336      *
337      * @param count - number of bit needed
338      *
339      * @return - count bits from stream
340      */
341     uint8_t popBits(size_t count)
342     {
343         if (bitCount < count)
344         {
345             unpackError = true;
346             return 0;
347         }
348         // consume bits low-order bits first
349         auto bits = bitStream.convert_to<uint8_t>();
350         bits &= ((1 << count) - 1);
351         bitStream >>= count;
352         bitCount -= count;
353         return bits;
354     }
355 
356     /**
357      * @brief discard all partial bits
358      */
359     void discardBits()
360     {
361         bitStream = 0;
362         bitCount = 0;
363     }
364 
365     /**
366      * @brief fully reset the unpack stream
367      */
368     void reset()
369     {
370         discardBits();
371         rawIndex = 0;
372         unpackError = false;
373     }
374 
375     /**
376      * @brief check to see if the stream has been fully unpacked
377      *
378      * @return bool - true if the stream has been unpacked and has no errors
379      */
380     bool fullyUnpacked()
381     {
382         unpackCheck = true;
383         return raw.size() == rawIndex && bitCount == 0 && !unpackError;
384     }
385 
386     // base empty unpack
387     int unpack()
388     {
389         return 0;
390     }
391 
392     /**
393      * @brief unpack arbitrary values (of any supported type) from the buffer
394      *
395      * @tparam Arg - the type of the first argument
396      * @tparam Args - the type of the optional remaining arguments
397      *
398      * @param arg - the first argument to unpack
399      * @param args... - the optional remaining arguments to unpack
400      *
401      * @return int - non-zero for unpack error
402      */
403     template <typename Arg, typename... Args>
404     int unpack(Arg&& arg, Args&&... args)
405     {
406         int unpackRet =
407             details::UnpackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
408         if (unpackRet)
409         {
410             unpackError = true;
411             return unpackRet;
412         }
413         return unpack(std::forward<Args>(args)...);
414     }
415 
416     /**
417      * @brief unpack a tuple of values (of any supported type) from the buffer
418      *
419      * This will unpack the elements of the tuple as if each one was passed in
420      * individually, as if passed into the above variadic function.
421      *
422      * @tparam Types - the implicitly declared list of the tuple element types
423      *
424      * @param t - the tuple of values to unpack
425      *
426      * @return int - non-zero on unpack error
427      */
428     template <typename... Types>
429     int unpack(std::tuple<Types...>& t)
430     {
431         // roll back checkpoint so that unpacking a tuple is atomic
432         size_t priorBitCount = bitCount;
433         size_t priorIndex = rawIndex;
434         fixed_uint_t<details::bitStreamSize> priorBits = bitStream;
435 
436         int ret =
437             std::apply([this](Types&... args) { return unpack(args...); }, t);
438         if (ret)
439         {
440             bitCount = priorBitCount;
441             bitStream = priorBits;
442             rawIndex = priorIndex;
443         }
444 
445         return ret;
446     }
447 
448     // partial bytes in the form of bits
449     fixed_uint_t<details::bitStreamSize> bitStream;
450     size_t bitCount = 0;
451     std::vector<uint8_t> raw;
452     size_t rawIndex = 0;
453     bool trailingOk = true;
454     bool unpackCheck = false;
455     bool unpackError = false;
456 };
457 
458 /**
459  * @brief high-level interface to an IPMI response
460  *
461  * Make it easy to just pack in the response args from the callback into a
462  * buffer that goes back to the requester.
463  */
464 struct Response
465 {
466     /* Define all of the basic class operations:
467      *     Not allowed:
468      *         - Default constructor to avoid nullptrs.
469      *     Allowed:
470      *         - Copy operations.
471      *         - Move operations.
472      *         - Destructor.
473      */
474     Response() = delete;
475     Response(const Response&) = default;
476     Response& operator=(const Response&) = default;
477     Response(Response&&) = default;
478     Response& operator=(Response&&) = default;
479     ~Response() = default;
480 
481     using ptr = std::shared_ptr<Response>;
482 
483     explicit Response(Context::ptr& context) :
484         payload(), ctx(context), cc(ccSuccess)
485     {
486     }
487 
488     /**
489      * @brief pack arbitrary values (of any supported type) into the payload
490      *
491      * @tparam Args - the type of the optional arguments
492      *
493      * @param args... - the optional arguments to pack
494      *
495      * @return int - non-zero on pack errors
496      */
497     template <typename... Args>
498     int pack(Args&&... args)
499     {
500         return payload.pack(std::forward<Args>(args)...);
501     }
502 
503     /**
504      * @brief pack a tuple of values (of any supported type) into the payload
505      *
506      * This will pack the elements of the tuple as if each one was passed in
507      * individually, as if passed into the above variadic function.
508      *
509      * @tparam Types - the implicitly declared list of the tuple element types
510      *
511      * @param t - the tuple of values to pack
512      *
513      * @return int - non-zero on pack errors
514      */
515     template <typename... Types>
516     int pack(std::tuple<Types...>& t)
517     {
518         return payload.pack(t);
519     }
520 
521     Payload payload;
522     Context::ptr ctx;
523     Cc cc;
524 };
525 
526 /**
527  * @brief high-level interface to an IPMI request
528  *
529  * Make it easy to unpack the buffer into the request args for the callback.
530  */
531 struct Request
532 {
533     /* Define all of the basic class operations:
534      *     Not allowed:
535      *         - Default constructor to avoid nullptrs.
536      *     Allowed:
537      *         - Copy operations.
538      *         - Move operations.
539      *         - Destructor.
540      */
541     Request() = delete;
542     Request(const Request&) = default;
543     Request& operator=(const Request&) = default;
544     Request(Request&&) = default;
545     Request& operator=(Request&&) = default;
546     ~Request() = default;
547 
548     using ptr = std::shared_ptr<Request>;
549 
550     explicit Request(Context::ptr context, std::vector<uint8_t>&& d) :
551         payload(std::forward<std::vector<uint8_t>>(d)), ctx(context)
552     {
553     }
554 
555     /**
556      * @brief unpack arbitrary values (of any supported type) from the payload
557      *
558      * @tparam Args - the type of the optional arguments
559      *
560      * @param args... - the optional arguments to unpack
561      *
562      * @return int - non-zero for unpack error
563      */
564     template <typename... Args>
565     int unpack(Args&&... args)
566     {
567         int unpackRet = payload.unpack(std::forward<Args>(args)...);
568         if (unpackRet == ipmi::ccSuccess)
569         {
570             if (!payload.trailingOk)
571             {
572                 if (!payload.fullyUnpacked())
573                 {
574                     // not all bits were consumed by requested parameters
575                     return ipmi::ccReqDataLenInvalid;
576                 }
577             }
578         }
579         return unpackRet;
580     }
581 
582     /**
583      * @brief unpack a tuple of values (of any supported type) from the payload
584      *
585      * This will unpack the elements of the tuple as if each one was passed in
586      * individually, as if passed into the above variadic function.
587      *
588      * @tparam Types - the implicitly declared list of the tuple element types
589      *
590      * @param t - the tuple of values to unpack
591      *
592      * @return int - non-zero on unpack error
593      */
594     template <typename... Types>
595     int unpack(std::tuple<Types...>& t)
596     {
597         return std::apply([this](Types&... args) { return unpack(args...); },
598                           t);
599     }
600 
601     /** @brief Create a response message that corresponds to this request
602      *
603      * @return A shared_ptr to the response message created
604      */
605     Response::ptr makeResponse()
606     {
607         return std::make_shared<Response>(ctx);
608     }
609 
610     Payload payload;
611     Context::ptr ctx;
612 };
613 
614 } // namespace message
615 
616 } // namespace ipmi
617 
618 // include packing and unpacking of types
619 #include <ipmid/message/pack.hpp>
620 #include <ipmid/message/unpack.hpp>
621