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 <ipmid/types.hpp> 25 #include <memory> 26 #include <phosphor-logging/log.hpp> 27 #include <sdbusplus/asio/connection.hpp> 28 #include <tuple> 29 #include <utility> 30 #include <vector> 31 32 namespace ipmi 33 { 34 35 struct Context 36 { 37 using ptr = std::shared_ptr<Context>; 38 39 Context() = delete; 40 Context(const Context&) = default; 41 Context& operator=(const Context&) = default; 42 Context(Context&&) = delete; 43 Context& operator=(Context&&) = delete; 44 45 Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn, 46 uint8_t lun, Cmd cmd, int channel, int userId, uint32_t sessionId, 47 Privilege priv, int rqSA, int hostIdx, 48 boost::asio::yield_context& yield) : 49 bus(bus), 50 netFn(netFn), lun(lun), cmd(cmd), channel(channel), userId(userId), 51 sessionId(sessionId), priv(priv), rqSA(rqSA), hostIdx(hostIdx), 52 yield(yield) 53 { 54 } 55 56 std::shared_ptr<sdbusplus::asio::connection> bus; 57 // normal IPMI context (what call is this, from whence it came...) 58 NetFn netFn; 59 uint8_t lun; 60 Cmd cmd; 61 int channel; 62 int userId; 63 uint32_t sessionId; 64 Privilege priv; 65 // srcAddr is only set on IPMB requests because 66 // Platform Event Message needs it to determine the incoming format 67 int rqSA; 68 int hostIdx; 69 boost::asio::yield_context yield; 70 }; 71 72 namespace message 73 { 74 75 namespace details 76 { 77 78 template <typename A> 79 struct UnpackSingle; 80 81 template <typename T> 82 using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>; 83 84 template <typename A> 85 struct PackSingle; 86 87 template <typename T> 88 using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>; 89 90 // size to hold 64 bits plus one (possibly-)partial byte 91 static 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 */ 111 struct 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 119 explicit Payload(SecureBuffer&& data) : raw(std::move(data)) 120 { 121 } 122 123 ~Payload() 124 { 125 using namespace phosphor::logging; 126 if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk && 127 !unpackCheck && !unpackError) 128 { 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 272 /** 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 293 /****************************************************************** 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; 484 SecureBuffer raw; 485 size_t rawIndex = 0; 486 bool trailingOk = true; 487 bool unpackCheck = false; 488 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 */ 497 struct 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 554 /** 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 569 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 */ 579 struct 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 598 explicit Request(Context::ptr context, SecureBuffer&& d) : 599 payload(std::forward<SecureBuffer>(d)), ctx(context) 600 { 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)...); 616 if (unpackRet != ipmi::ccSuccess) 617 { 618 // not all bits were consumed by requested parameters 619 return ipmi::ccReqDataLenInvalid; 620 } 621 if (!payload.trailingOk) 622 { 623 if (!payload.fullyUnpacked()) 624 { 625 // not all bits were consumed by requested parameters 626 return ipmi::ccReqDataLenInvalid; 627 } 628 } 629 return ipmi::ccSuccess; 630 } 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> 671