blob: 6698581bc9f4491df306aaab583b10bfa671da93 [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>
George Liub4b40912024-07-17 16:14:17 +080022#include <phosphor-logging/lg2.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),
John Chung0a3f40b2025-01-22 18:07:52 -060051 userId(userId), sessionId(sessionId), priv(priv), group(0), rqSA(rqSA),
Patrick Williams1318a5e2024-08-16 15:19:54 -040052 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;
John Chung0a3f40b2025-01-22 18:07:52 -060064 // defining body code for netFnGroup
65 Group group;
Vernon Maueryd6a2da02019-04-09 16:00:46 -070066 // srcAddr is only set on IPMB requests because
67 // Platform Event Message needs it to determine the incoming format
Vernon Mauery33298af2019-05-13 15:32:37 -070068 int rqSA;
Kumar Thangavelf7d081f2020-08-19 20:41:18 +053069 int hostIdx;
James Feistcb09aa02019-09-06 13:41:59 -070070 boost::asio::yield_context yield;
Vernon Mauerye7329c72018-10-08 12:05:16 -070071};
72
73namespace message
74{
75
76namespace details
77{
78
79template <typename A>
80struct UnpackSingle;
81
82template <typename T>
83using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>;
84
85template <typename A>
86struct PackSingle;
87
88template <typename T>
89using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>;
90
91// size to hold 64 bits plus one (possibly-)partial byte
92static constexpr size_t bitStreamSize = ((sizeof(uint64_t) + 1) * CHAR_BIT);
93
94} // namespace details
95
96/**
97 * @brief a payload class that provides a mechanism to pack and unpack data
98 *
99 * When a new request is being executed, the Payload class is responsible for
100 * attempting to unpack all the required arguments from the incoming blob. For
101 * variable-length functions, it is possible to have function signature have a
102 * Payload object, which will then allow the remaining data to be extracted as
103 * needed.
104 *
105 * When creating a response, the parameters returned from the callback use a
106 * newly created payload object to pack all the parameters into a buffer that is
107 * then returned to the requester.
108 *
109 * These interfaces make calls into the message/pack.hpp and message/unpack.hpp
110 * functions.
111 */
112struct Payload
113{
114 Payload() = default;
115 Payload(const Payload&) = default;
116 Payload& operator=(const Payload&) = default;
117 Payload(Payload&&) = default;
118 Payload& operator=(Payload&&) = default;
119
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500120 explicit Payload(SecureBuffer&& data) : raw(std::move(data)) {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700121
122 ~Payload()
123 {
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 {
George Liub4b40912024-07-17 16:14:17 +0800127 lg2::error(
128 "Failed to check request for full unpack: raw size: {RAW_SIZE}",
129 "RAW_SIZE", raw.size());
Vernon Mauerye7329c72018-10-08 12:05:16 -0700130 }
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 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400261 int packRet =
262 details::PackSingle_t<Arg>::op(*this, std::forward<Arg>(arg));
Vernon Mauerye7329c72018-10-08 12:05:16 -0700263 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
Patrick Williams1318a5e2024-08-16 15:19:54 -0400469 int ret =
470 std::apply([this](Types&... args) { return unpack(args...); }, t);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700471 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)
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500518 {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700519
520 /**
521 * @brief pack arbitrary values (of any supported type) into the payload
522 *
523 * @tparam Args - the type of the optional arguments
524 *
525 * @param args... - the optional arguments to pack
526 *
527 * @return int - non-zero on pack errors
528 */
529 template <typename... Args>
530 int pack(Args&&... args)
531 {
532 return payload.pack(std::forward<Args>(args)...);
533 }
534
535 /**
536 * @brief pack a tuple of values (of any supported type) into the payload
537 *
538 * This will pack the elements of the tuple as if each one was passed in
539 * individually, as if passed into the above variadic function.
540 *
541 * @tparam Types - the implicitly declared list of the tuple element types
542 *
543 * @param t - the tuple of values to pack
544 *
545 * @return int - non-zero on pack errors
546 */
547 template <typename... Types>
548 int pack(std::tuple<Types...>& t)
549 {
550 return payload.pack(t);
551 }
552
William A. Kennington III92476a82019-04-25 01:32:30 -0700553 /**
554 * @brief Prepends another payload to this one
555 *
556 * Avoid using this unless absolutely required since it inserts into the
557 * front of the response payload.
558 *
559 * @param p - The payload to prepend
560 *
561 * @retunr int - non-zero on prepend errors
562 */
563 int prepend(const ipmi::message::Payload& p)
564 {
565 return payload.prepend(p);
566 }
567
Vernon Mauerye7329c72018-10-08 12:05:16 -0700568 Payload payload;
569 Context::ptr ctx;
570 Cc cc;
571};
572
573/**
574 * @brief high-level interface to an IPMI request
575 *
576 * Make it easy to unpack the buffer into the request args for the callback.
577 */
578struct Request
579{
580 /* Define all of the basic class operations:
581 * Not allowed:
582 * - Default constructor to avoid nullptrs.
583 * Allowed:
584 * - Copy operations.
585 * - Move operations.
586 * - Destructor.
587 */
588 Request() = delete;
589 Request(const Request&) = default;
590 Request& operator=(const Request&) = default;
591 Request(Request&&) = default;
592 Request& operator=(Request&&) = default;
593 ~Request() = default;
594
595 using ptr = std::shared_ptr<Request>;
596
Vernon Mauery997952a2021-07-30 14:06:14 -0700597 explicit Request(Context::ptr context, SecureBuffer&& d) :
598 payload(std::forward<SecureBuffer>(d)), ctx(context)
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500599 {}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700600
601 /**
602 * @brief unpack arbitrary values (of any supported type) from the payload
603 *
604 * @tparam Args - the type of the optional arguments
605 *
606 * @param args... - the optional arguments to unpack
607 *
608 * @return int - non-zero for unpack error
609 */
610 template <typename... Args>
611 int unpack(Args&&... args)
612 {
613 int unpackRet = payload.unpack(std::forward<Args>(args)...);
Vernon Maueryf865dea2019-05-03 13:29:40 -0700614 if (unpackRet != ipmi::ccSuccess)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700615 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700616 // not all bits were consumed by requested parameters
617 return ipmi::ccReqDataLenInvalid;
618 }
619 if (!payload.trailingOk)
620 {
621 if (!payload.fullyUnpacked())
Vernon Mauerye7329c72018-10-08 12:05:16 -0700622 {
Vernon Maueryf865dea2019-05-03 13:29:40 -0700623 // not all bits were consumed by requested parameters
624 return ipmi::ccReqDataLenInvalid;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700625 }
626 }
Vernon Maueryf865dea2019-05-03 13:29:40 -0700627 return ipmi::ccSuccess;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700628 }
629
630 /**
631 * @brief unpack a tuple of values (of any supported type) from the payload
632 *
633 * This will unpack the elements of the tuple as if each one was passed in
634 * individually, as if passed into the above variadic function.
635 *
636 * @tparam Types - the implicitly declared list of the tuple element types
637 *
638 * @param t - the tuple of values to unpack
639 *
640 * @return int - non-zero on unpack error
641 */
642 template <typename... Types>
643 int unpack(std::tuple<Types...>& t)
644 {
645 return std::apply([this](Types&... args) { return unpack(args...); },
646 t);
647 }
648
649 /** @brief Create a response message that corresponds to this request
650 *
651 * @return A shared_ptr to the response message created
652 */
653 Response::ptr makeResponse()
654 {
655 return std::make_shared<Response>(ctx);
656 }
657
658 Payload payload;
659 Context::ptr ctx;
660};
661
662} // namespace message
663
664} // namespace ipmi
665
666// include packing and unpacking of types
667#include <ipmid/message/pack.hpp>
668#include <ipmid/message/unpack.hpp>