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 <boost/asio/spawn.hpp> 19 #include <ipmid/api-types.hpp> 20 #include <ipmid/message/types.hpp> 21 #include <ipmid/types.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/asio/connection.hpp> 24 25 #include <algorithm> 26 #include <cstdint> 27 #include <exception> 28 #include <memory> 29 #include <tuple> 30 #include <utility> 31 #include <vector> 32 33 namespace ipmi 34 { 35 36 struct Context 37 { 38 using ptr = std::shared_ptr<Context>; 39 40 Context() = delete; 41 Context(const Context&) = default; 42 Context& operator=(const Context&) = default; 43 Context(Context&&) = delete; 44 Context& operator=(Context&&) = delete; 45 46 Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn, 47 uint8_t lun, Cmd cmd, int channel, int userId, uint32_t sessionId, 48 Privilege priv, int rqSA, int hostIdx, 49 boost::asio::yield_context& yield) : 50 bus(bus), netFn(netFn), lun(lun), cmd(cmd), channel(channel), 51 userId(userId), sessionId(sessionId), priv(priv), rqSA(rqSA), 52 hostIdx(hostIdx), yield(yield) 53 {} 54 55 std::shared_ptr<sdbusplus::asio::connection> bus; 56 // normal IPMI context (what call is this, from whence it came...) 57 NetFn netFn; 58 uint8_t lun; 59 Cmd cmd; 60 int channel; 61 int userId; 62 uint32_t sessionId; 63 Privilege priv; 64 // srcAddr is only set on IPMB requests because 65 // Platform Event Message needs it to determine the incoming format 66 int rqSA; 67 int hostIdx; 68 boost::asio::yield_context yield; 69 }; 70 71 namespace message 72 { 73 74 namespace details 75 { 76 77 template <typename A> 78 struct UnpackSingle; 79 80 template <typename T> 81 using UnpackSingle_t = UnpackSingle<utility::TypeIdDowncast_t<T>>; 82 83 template <typename A> 84 struct PackSingle; 85 86 template <typename T> 87 using PackSingle_t = PackSingle<utility::TypeIdDowncast_t<T>>; 88 89 // size to hold 64 bits plus one (possibly-)partial byte 90 static 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 */ 110 struct 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 118 explicit Payload(SecureBuffer&& data) : raw(std::move(data)) {} 119 120 ~Payload() 121 { 122 using namespace phosphor::logging; 123 if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk && 124 !unpackCheck && !unpackError) 125 { 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 { 258 int packRet = 259 details::PackSingle_t<Arg>::op(*this, std::forward<Arg>(arg)); 260 if (packRet) 261 { 262 return packRet; 263 } 264 packRet = pack(std::forward<Args>(args)...); 265 drain(); 266 return packRet; 267 } 268 269 /** 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 290 /****************************************************************** 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 466 int ret = 467 std::apply([this](Types&... args) { return unpack(args...); }, t); 468 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; 481 SecureBuffer raw; 482 size_t rawIndex = 0; 483 bool trailingOk = true; 484 bool unpackCheck = false; 485 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 */ 494 struct 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) 515 {} 516 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 550 /** 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 565 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 */ 575 struct 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 594 explicit Request(Context::ptr context, SecureBuffer&& d) : 595 payload(std::forward<SecureBuffer>(d)), ctx(context) 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