blob: c828e3c72b1aa38e4aed5bada0b089d211a41358 [file] [log] [blame]
Vernon Mauerye7329c72018-10-08 12:05:16 -07001/**
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>
William A. Kennington IIIf2fd17a2019-04-24 01:53:52 -070021#include <exception>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070022#include <ipmid/api-types.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070023#include <ipmid/message/types.hpp>
24#include <memory>
25#include <phosphor-logging/log.hpp>
Vernon Mauery33298af2019-05-13 15:32:37 -070026#include <sdbusplus/asio/connection.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070027#include <tuple>
28#include <utility>
29#include <vector>
30
31namespace ipmi
32{
33
34struct Context
35{
36 using ptr = std::shared_ptr<Context>;
37
Vernon Mauery33298af2019-05-13 15:32:37 -070038 Context() = delete;
39 Context(const Context&) = default;
40 Context& operator=(const Context&) = default;
41 Context(Context&&) = delete;
42 Context& operator=(Context&&) = delete;
Vernon Mauerye7329c72018-10-08 12:05:16 -070043
Vernon Mauery33298af2019-05-13 15:32:37 -070044 Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn,
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +053045 Cmd cmd, int channel, int userId, uint32_t sessionId,
46 Privilege priv, int rqSA, boost::asio::yield_context& yield) :
Vernon Mauery33298af2019-05-13 15:32:37 -070047 bus(bus),
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +053048 netFn(netFn), cmd(cmd), channel(channel), userId(userId),
49 sessionId(sessionId), priv(priv), rqSA(rqSA), yield(yield)
Vernon Mauerye7329c72018-10-08 12:05:16 -070050 {
51 }
52
Vernon Mauery33298af2019-05-13 15:32:37 -070053 std::shared_ptr<sdbusplus::asio::connection> bus;
Vernon Mauerye7329c72018-10-08 12:05:16 -070054 // normal IPMI context (what call is this, from whence it came...)
Vernon Mauery33298af2019-05-13 15:32:37 -070055 NetFn netFn;
56 Cmd cmd;
57 int channel;
58 int userId;
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +053059 uint32_t sessionId;
Vernon Mauery33298af2019-05-13 15:32:37 -070060 Privilege priv;
Vernon Maueryd6a2da02019-04-09 16:00:46 -070061 // srcAddr is only set on IPMB requests because
62 // Platform Event Message needs it to determine the incoming format
Vernon Mauery33298af2019-05-13 15:32:37 -070063 int rqSA;
James Feistcb09aa02019-09-06 13:41:59 -070064 boost::asio::yield_context yield;
Vernon Mauerye7329c72018-10-08 12:05:16 -070065};
66
67namespace message
68{
69
70namespace details
71{
72
73template <typename A>
74struct UnpackSingle;
75
76template <typename T>
77using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>;
78
79template <typename A>
80struct PackSingle;
81
82template <typename T>
83using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>;
84
85// size to hold 64 bits plus one (possibly-)partial byte
86static 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 */
106struct 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
William A. Kennington III51694c22019-04-24 01:44:44 -0700114 explicit Payload(std::vector<uint8_t>&& data) : raw(std::move(data))
Vernon Mauerye7329c72018-10-08 12:05:16 -0700115 {
116 }
117
118 ~Payload()
119 {
120 using namespace phosphor::logging;
William A. Kennington IIIf2fd17a2019-04-24 01:53:52 -0700121 if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk &&
122 !unpackCheck && !unpackError)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700123 {
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
William A. Kennington III92476a82019-04-25 01:32:30 -0700267 /**
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
Vernon Mauerye7329c72018-10-08 12:05:16 -0700288 /******************************************************************
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;
William A. Kennington III51694c22019-04-24 01:44:44 -0700481 bool trailingOk = true;
482 bool unpackCheck = false;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700483 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 */
492struct 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
William A. Kennington III92476a82019-04-25 01:32:30 -0700549 /**
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
Vernon Mauerye7329c72018-10-08 12:05:16 -0700564 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 */
574struct 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)...);
Vernon Maueryf865dea2019-05-03 13:29:40 -0700611 if (unpackRet != ipmi::ccSuccess)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700612 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700613 // not all bits were consumed by requested parameters
614 return ipmi::ccReqDataLenInvalid;
615 }
616 if (!payload.trailingOk)
617 {
618 if (!payload.fullyUnpacked())
Vernon Mauerye7329c72018-10-08 12:05:16 -0700619 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700620 // not all bits were consumed by requested parameters
621 return ipmi::ccReqDataLenInvalid;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700622 }
623 }
Vernon Maueryf865dea2019-05-03 13:29:40 -0700624 return ipmi::ccSuccess;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700625 }
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>