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