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