1 /** 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> 21 #include <exception> 22 #include <ipmid/api-types.hpp> 23 #include <ipmid/message/types.hpp> 24 #include <memory> 25 #include <phosphor-logging/log.hpp> 26 #include <sdbusplus/asio/connection.hpp> 27 #include <tuple> 28 #include <utility> 29 #include <vector> 30 31 namespace ipmi 32 { 33 34 struct Context 35 { 36 using ptr = std::shared_ptr<Context>; 37 38 Context() = delete; 39 Context(const Context&) = default; 40 Context& operator=(const Context&) = default; 41 Context(Context&&) = delete; 42 Context& operator=(Context&&) = delete; 43 44 Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn, 45 Cmd cmd, int channel, int userId, uint32_t sessionId, 46 Privilege priv, int rqSA, boost::asio::yield_context& yield) : 47 bus(bus), 48 netFn(netFn), cmd(cmd), channel(channel), userId(userId), 49 sessionId(sessionId), priv(priv), rqSA(rqSA), yield(yield) 50 { 51 } 52 53 std::shared_ptr<sdbusplus::asio::connection> bus; 54 // normal IPMI context (what call is this, from whence it came...) 55 NetFn netFn; 56 Cmd cmd; 57 int channel; 58 int userId; 59 uint32_t sessionId; 60 Privilege priv; 61 // srcAddr is only set on IPMB requests because 62 // Platform Event Message needs it to determine the incoming format 63 int rqSA; 64 boost::asio::yield_context yield; 65 }; 66 67 namespace message 68 { 69 70 namespace details 71 { 72 73 template <typename A> 74 struct UnpackSingle; 75 76 template <typename T> 77 using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>; 78 79 template <typename A> 80 struct PackSingle; 81 82 template <typename T> 83 using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>; 84 85 // size to hold 64 bits plus one (possibly-)partial byte 86 static 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 */ 106 struct 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 114 explicit Payload(std::vector<uint8_t>&& data) : raw(std::move(data)) 115 { 116 } 117 118 ~Payload() 119 { 120 using namespace phosphor::logging; 121 if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk && 122 !unpackCheck && !unpackError) 123 { 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 267 /** 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 288 /****************************************************************** 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; 481 bool trailingOk = true; 482 bool unpackCheck = false; 483 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 */ 492 struct 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 549 /** 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 564 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 */ 574 struct 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)...); 611 if (unpackRet != ipmi::ccSuccess) 612 { 613 // not all bits were consumed by requested parameters 614 return ipmi::ccReqDataLenInvalid; 615 } 616 if (!payload.trailingOk) 617 { 618 if (!payload.fullyUnpacked()) 619 { 620 // not all bits were consumed by requested parameters 621 return ipmi::ccReqDataLenInvalid; 622 } 623 } 624 return ipmi::ccSuccess; 625 } 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> 666