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