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