blob: 64f777707dc6efec3186f24c2a627e608a476e1b [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
Vernon Mauerye7329c72018-10-08 12:05:16 -070018#include <boost/asio/spawn.hpp>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070019#include <ipmid/api-types.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070020#include <ipmid/message/types.hpp>
Vernon Mauery997952a2021-07-30 14:06:14 -070021#include <ipmid/types.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070022#include <phosphor-logging/log.hpp>
Vernon Mauery33298af2019-05-13 15:32:37 -070023#include <sdbusplus/asio/connection.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050024
25#include <algorithm>
26#include <cstdint>
27#include <exception>
28#include <memory>
Vernon Mauerye7329c72018-10-08 12:05:16 -070029#include <tuple>
30#include <utility>
31#include <vector>
32
33namespace ipmi
34{
35
36struct Context
37{
38 using ptr = std::shared_ptr<Context>;
39
Vernon Mauery33298af2019-05-13 15:32:37 -070040 Context() = delete;
41 Context(const Context&) = default;
42 Context& operator=(const Context&) = default;
43 Context(Context&&) = delete;
44 Context& operator=(Context&&) = delete;
Vernon Mauerye7329c72018-10-08 12:05:16 -070045
Vernon Mauery33298af2019-05-13 15:32:37 -070046 Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn,
Johnathan Manteyc11cc5c2020-07-22 13:52:33 -070047 uint8_t lun, Cmd cmd, int channel, int userId, uint32_t sessionId,
Kumar Thangavelf7d081f2020-08-19 20:41:18 +053048 Privilege priv, int rqSA, int hostIdx,
49 boost::asio::yield_context& yield) :
Vernon Mauery33298af2019-05-13 15:32:37 -070050 bus(bus),
Johnathan Manteyc11cc5c2020-07-22 13:52:33 -070051 netFn(netFn), lun(lun), cmd(cmd), channel(channel), userId(userId),
Kumar Thangavelf7d081f2020-08-19 20:41:18 +053052 sessionId(sessionId), priv(priv), rqSA(rqSA), hostIdx(hostIdx),
53 yield(yield)
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050054 {}
Vernon Mauerye7329c72018-10-08 12:05:16 -070055
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
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500119 explicit Payload(SecureBuffer&& data) : raw(std::move(data)) {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700120
121 ~Payload()
122 {
123 using namespace phosphor::logging;
William A. Kennington IIIf2fd17a2019-04-24 01:53:52 -0700124 if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk &&
125 !unpackCheck && !unpackError)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700126 {
127 log<level::ERR>("Failed to check request for full unpack");
128 }
129 }
130
131 /******************************************************************
132 * raw vector access
133 *****************************************************************/
134 /**
135 * @brief return the size of the underlying raw buffer
136 */
137 size_t size() const
138 {
139 return raw.size();
140 }
141 /**
142 * @brief resize the underlying raw buffer to a new size
143 *
144 * @param sz - new size for the buffer
145 */
146 void resize(size_t sz)
147 {
148 raw.resize(sz);
149 }
150 /**
151 * @brief return a pointer to the underlying raw buffer
152 */
153 uint8_t* data()
154 {
155 return raw.data();
156 }
157 /**
158 * @brief return a const pointer to the underlying raw buffer
159 */
160 const uint8_t* data() const
161 {
162 return raw.data();
163 }
164
165 /******************************************************************
166 * Response operations
167 *****************************************************************/
168 /**
169 * @brief append a series of bytes to the buffer
170 *
171 * @tparam T - the type pointer to return; must be compatible to a byte
172 *
173 * @param begin - a pointer to the beginning of the series
174 * @param end - a pointer to the end of the series
175 */
176 template <typename T>
177 void append(T* begin, T* end)
178 {
179 static_assert(
180 std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
181 std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
182 std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
183 "begin and end must be signed or unsigned byte pointers");
184 // this interface only allows full-byte access; pack in partial bytes
185 drain();
186 raw.insert(raw.end(), reinterpret_cast<const uint8_t*>(begin),
187 reinterpret_cast<const uint8_t*>(end));
188 }
189
190 /**
191 * @brief append a series of bits to the buffer
192 *
193 * Only the lowest @count order of bits will be appended, with the most
194 * significant of those bits getting appended first.
195 *
196 * @param count - number of bits to append
197 * @param bits - a byte with count significant bits to append
198 */
199 void appendBits(size_t count, uint8_t bits)
200 {
201 // drain whole bytes out
202 drain(true);
203
204 // add in the new bits as the higher-order bits, filling LSBit first
205 fixed_uint_t<details::bitStreamSize> tmp = bits;
206 tmp <<= bitCount;
207 bitStream |= tmp;
208 bitCount += count;
209
210 // drain any whole bytes we have appended
211 drain(true);
212 }
213
214 /**
215 * @brief empty out the bucket and pack it as bytes LSB-first
216 *
217 * @param wholeBytesOnly - if true, only the whole bytes will be drained
218 */
219 void drain(bool wholeBytesOnly = false)
220 {
221 while (bitCount > 0)
222 {
223 uint8_t retVal;
224 if (bitCount < CHAR_BIT)
225 {
226 if (wholeBytesOnly)
227 {
228 break;
229 }
230 }
231 size_t bitsOut = std::min(static_cast<size_t>(CHAR_BIT), bitCount);
232 retVal = static_cast<uint8_t>(bitStream);
233 raw.push_back(retVal);
234 bitStream >>= bitsOut;
235 bitCount -= bitsOut;
236 }
237 }
238
239 // base empty pack
240 int pack()
241 {
242 return 0;
243 }
244
245 /**
246 * @brief pack arbitrary values (of any supported type) into the buffer
247 *
248 * @tparam Arg - the type of the first argument
249 * @tparam Args - the type of the optional remaining arguments
250 *
251 * @param arg - the first argument to pack
252 * @param args... - the optional remaining arguments to pack
253 *
254 * @return int - non-zero on pack errors
255 */
256 template <typename Arg, typename... Args>
257 int pack(Arg&& arg, Args&&... args)
258 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500259 int packRet = details::PackSingle_t<Arg>::op(*this,
260 std::forward<Arg>(arg));
Vernon Mauerye7329c72018-10-08 12:05:16 -0700261 if (packRet)
262 {
263 return packRet;
264 }
265 packRet = pack(std::forward<Args>(args)...);
266 drain();
267 return packRet;
268 }
269
William A. Kennington III92476a82019-04-25 01:32:30 -0700270 /**
271 * @brief Prepends another payload to this one
272 *
273 * Avoid using this unless absolutely required since it inserts into the
274 * front of the response payload.
275 *
276 * @param p - The payload to prepend
277 *
278 * @retunr int - non-zero on prepend errors
279 */
280 int prepend(const ipmi::message::Payload& p)
281 {
282 if (bitCount != 0 || p.bitCount != 0)
283 {
284 return 1;
285 }
286 raw.reserve(raw.size() + p.raw.size());
287 raw.insert(raw.begin(), p.raw.begin(), p.raw.end());
288 return 0;
289 }
290
Vernon Mauerye7329c72018-10-08 12:05:16 -0700291 /******************************************************************
292 * Request operations
293 *****************************************************************/
294 /**
295 * @brief pop a series of bytes from the raw buffer
296 *
297 * @tparam T - the type pointer to return; must be compatible to a byte
298 *
299 * @param count - the number of bytes to return
300 *
301 * @return - a tuple of pointers (begin,begin+count)
302 */
303 template <typename T>
304 auto pop(size_t count)
305 {
306 static_assert(
307 std::is_same_v<utility::TypeIdDowncast_t<T>, int8_t> ||
308 std::is_same_v<utility::TypeIdDowncast_t<T>, uint8_t> ||
309 std::is_same_v<utility::TypeIdDowncast_t<T>, char>,
310 "T* must be signed or unsigned byte pointers");
311 // this interface only allows full-byte access; skip partial bits
312 if (bitCount)
313 {
314 // WARN on unused bits?
315 discardBits();
316 }
317 if (count <= (raw.size() - rawIndex))
318 {
319 auto range = std::make_tuple(
320 reinterpret_cast<T*>(raw.data() + rawIndex),
321 reinterpret_cast<T*>(raw.data() + rawIndex + count));
322 rawIndex += count;
323 return range;
324 }
325 unpackError = true;
326 return std::make_tuple(reinterpret_cast<T*>(NULL),
327 reinterpret_cast<T*>(NULL));
328 }
329
330 /**
331 * @brief fill bit stream with at least count bits for consumption
332 *
333 * @param count - number of bit needed
334 *
335 * @return - unpackError
336 */
337 bool fillBits(size_t count)
338 {
339 // add more bits to the top end of the bitstream
340 // so we consume bits least-significant first
341 if (count > (details::bitStreamSize - CHAR_BIT))
342 {
343 unpackError = true;
344 return unpackError;
345 }
346 while (bitCount < count)
347 {
348 if (rawIndex < raw.size())
349 {
350 fixed_uint_t<details::bitStreamSize> tmp = raw[rawIndex++];
351 tmp <<= bitCount;
352 bitStream |= tmp;
353 bitCount += CHAR_BIT;
354 }
355 else
356 {
357 // raw has run out of bytes to pop
358 unpackError = true;
359 return unpackError;
360 }
361 }
362 return false;
363 }
364
365 /**
366 * @brief consume count bits from bitstream (must call fillBits first)
367 *
368 * @param count - number of bit needed
369 *
370 * @return - count bits from stream
371 */
372 uint8_t popBits(size_t count)
373 {
374 if (bitCount < count)
375 {
376 unpackError = true;
377 return 0;
378 }
379 // consume bits low-order bits first
380 auto bits = bitStream.convert_to<uint8_t>();
381 bits &= ((1 << count) - 1);
382 bitStream >>= count;
383 bitCount -= count;
384 return bits;
385 }
386
387 /**
388 * @brief discard all partial bits
389 */
390 void discardBits()
391 {
392 bitStream = 0;
393 bitCount = 0;
394 }
395
396 /**
397 * @brief fully reset the unpack stream
398 */
399 void reset()
400 {
401 discardBits();
402 rawIndex = 0;
403 unpackError = false;
404 }
405
406 /**
407 * @brief check to see if the stream has been fully unpacked
408 *
409 * @return bool - true if the stream has been unpacked and has no errors
410 */
411 bool fullyUnpacked()
412 {
413 unpackCheck = true;
414 return raw.size() == rawIndex && bitCount == 0 && !unpackError;
415 }
416
417 // base empty unpack
418 int unpack()
419 {
420 return 0;
421 }
422
423 /**
424 * @brief unpack arbitrary values (of any supported type) from the buffer
425 *
426 * @tparam Arg - the type of the first argument
427 * @tparam Args - the type of the optional remaining arguments
428 *
429 * @param arg - the first argument to unpack
430 * @param args... - the optional remaining arguments to unpack
431 *
432 * @return int - non-zero for unpack error
433 */
434 template <typename Arg, typename... Args>
435 int unpack(Arg&& arg, Args&&... args)
436 {
437 int unpackRet =
438 details::UnpackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
439 if (unpackRet)
440 {
441 unpackError = true;
442 return unpackRet;
443 }
444 return unpack(std::forward<Args>(args)...);
445 }
446
447 /**
448 * @brief unpack a tuple of values (of any supported type) from the buffer
449 *
450 * This will unpack the elements of the tuple as if each one was passed in
451 * individually, as if passed into the above variadic function.
452 *
453 * @tparam Types - the implicitly declared list of the tuple element types
454 *
455 * @param t - the tuple of values to unpack
456 *
457 * @return int - non-zero on unpack error
458 */
459 template <typename... Types>
460 int unpack(std::tuple<Types...>& t)
461 {
462 // roll back checkpoint so that unpacking a tuple is atomic
463 size_t priorBitCount = bitCount;
464 size_t priorIndex = rawIndex;
465 fixed_uint_t<details::bitStreamSize> priorBits = bitStream;
466
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500467 int ret = std::apply([this](Types&... args) { return unpack(args...); },
468 t);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700469 if (ret)
470 {
471 bitCount = priorBitCount;
472 bitStream = priorBits;
473 rawIndex = priorIndex;
474 }
475
476 return ret;
477 }
478
479 // partial bytes in the form of bits
480 fixed_uint_t<details::bitStreamSize> bitStream;
481 size_t bitCount = 0;
Vernon Mauery997952a2021-07-30 14:06:14 -0700482 SecureBuffer raw;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700483 size_t rawIndex = 0;
William A. Kennington III51694c22019-04-24 01:44:44 -0700484 bool trailingOk = true;
485 bool unpackCheck = false;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700486 bool unpackError = false;
487};
488
489/**
490 * @brief high-level interface to an IPMI response
491 *
492 * Make it easy to just pack in the response args from the callback into a
493 * buffer that goes back to the requester.
494 */
495struct Response
496{
497 /* Define all of the basic class operations:
498 * Not allowed:
499 * - Default constructor to avoid nullptrs.
500 * Allowed:
501 * - Copy operations.
502 * - Move operations.
503 * - Destructor.
504 */
505 Response() = delete;
506 Response(const Response&) = default;
507 Response& operator=(const Response&) = default;
508 Response(Response&&) = default;
509 Response& operator=(Response&&) = default;
510 ~Response() = default;
511
512 using ptr = std::shared_ptr<Response>;
513
514 explicit Response(Context::ptr& context) :
515 payload(), ctx(context), cc(ccSuccess)
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500516 {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700517
518 /**
519 * @brief pack arbitrary values (of any supported type) into the payload
520 *
521 * @tparam Args - the type of the optional arguments
522 *
523 * @param args... - the optional arguments to pack
524 *
525 * @return int - non-zero on pack errors
526 */
527 template <typename... Args>
528 int pack(Args&&... args)
529 {
530 return payload.pack(std::forward<Args>(args)...);
531 }
532
533 /**
534 * @brief pack a tuple of values (of any supported type) into the payload
535 *
536 * This will pack the elements of the tuple as if each one was passed in
537 * individually, as if passed into the above variadic function.
538 *
539 * @tparam Types - the implicitly declared list of the tuple element types
540 *
541 * @param t - the tuple of values to pack
542 *
543 * @return int - non-zero on pack errors
544 */
545 template <typename... Types>
546 int pack(std::tuple<Types...>& t)
547 {
548 return payload.pack(t);
549 }
550
William A. Kennington III92476a82019-04-25 01:32:30 -0700551 /**
552 * @brief Prepends another payload to this one
553 *
554 * Avoid using this unless absolutely required since it inserts into the
555 * front of the response payload.
556 *
557 * @param p - The payload to prepend
558 *
559 * @retunr int - non-zero on prepend errors
560 */
561 int prepend(const ipmi::message::Payload& p)
562 {
563 return payload.prepend(p);
564 }
565
Vernon Mauerye7329c72018-10-08 12:05:16 -0700566 Payload payload;
567 Context::ptr ctx;
568 Cc cc;
569};
570
571/**
572 * @brief high-level interface to an IPMI request
573 *
574 * Make it easy to unpack the buffer into the request args for the callback.
575 */
576struct Request
577{
578 /* Define all of the basic class operations:
579 * Not allowed:
580 * - Default constructor to avoid nullptrs.
581 * Allowed:
582 * - Copy operations.
583 * - Move operations.
584 * - Destructor.
585 */
586 Request() = delete;
587 Request(const Request&) = default;
588 Request& operator=(const Request&) = default;
589 Request(Request&&) = default;
590 Request& operator=(Request&&) = default;
591 ~Request() = default;
592
593 using ptr = std::shared_ptr<Request>;
594
Vernon Mauery997952a2021-07-30 14:06:14 -0700595 explicit Request(Context::ptr context, SecureBuffer&& d) :
596 payload(std::forward<SecureBuffer>(d)), ctx(context)
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500597 {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700598
599 /**
600 * @brief unpack arbitrary values (of any supported type) from the payload
601 *
602 * @tparam Args - the type of the optional arguments
603 *
604 * @param args... - the optional arguments to unpack
605 *
606 * @return int - non-zero for unpack error
607 */
608 template <typename... Args>
609 int unpack(Args&&... args)
610 {
611 int unpackRet = payload.unpack(std::forward<Args>(args)...);
Vernon Maueryf865dea2019-05-03 13:29:40 -0700612 if (unpackRet != ipmi::ccSuccess)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700613 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700614 // not all bits were consumed by requested parameters
615 return ipmi::ccReqDataLenInvalid;
616 }
617 if (!payload.trailingOk)
618 {
619 if (!payload.fullyUnpacked())
Vernon Mauerye7329c72018-10-08 12:05:16 -0700620 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700621 // not all bits were consumed by requested parameters
622 return ipmi::ccReqDataLenInvalid;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700623 }
624 }
Vernon Maueryf865dea2019-05-03 13:29:40 -0700625 return ipmi::ccSuccess;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700626 }
627
628 /**
629 * @brief unpack a tuple of values (of any supported type) from the payload
630 *
631 * This will unpack the elements of the tuple as if each one was passed in
632 * individually, as if passed into the above variadic function.
633 *
634 * @tparam Types - the implicitly declared list of the tuple element types
635 *
636 * @param t - the tuple of values to unpack
637 *
638 * @return int - non-zero on unpack error
639 */
640 template <typename... Types>
641 int unpack(std::tuple<Types...>& t)
642 {
643 return std::apply([this](Types&... args) { return unpack(args...); },
644 t);
645 }
646
647 /** @brief Create a response message that corresponds to this request
648 *
649 * @return A shared_ptr to the response message created
650 */
651 Response::ptr makeResponse()
652 {
653 return std::make_shared<Response>(ctx);
654 }
655
656 Payload payload;
657 Context::ptr ctx;
658};
659
660} // namespace message
661
662} // namespace ipmi
663
664// include packing and unpacking of types
665#include <ipmid/message/pack.hpp>
666#include <ipmid/message/unpack.hpp>