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 <ipmid/message/types.hpp> 19 #include <phosphor-logging/log.hpp> 20 21 #include <array> 22 #include <memory> 23 #include <optional> 24 #include <span> 25 #include <string_view> 26 #include <tuple> 27 #include <utility> 28 #include <variant> 29 #include <vector> 30 31 namespace ipmi 32 { 33 34 namespace message 35 { 36 37 namespace details 38 { 39 40 /************************************** 41 * ipmi return type helpers 42 **************************************/ 43 44 template <typename NumericType, size_t byteIndex = 0> 45 void PackBytes(uint8_t* pointer, const NumericType& i) 46 { 47 if constexpr (byteIndex < sizeof(NumericType)) 48 { 49 *pointer = static_cast<uint8_t>(i >> (8 * byteIndex)); 50 PackBytes<NumericType, byteIndex + 1>(pointer + 1, i); 51 } 52 } 53 54 template <typename NumericType, size_t byteIndex = 0> 55 void PackBytesUnaligned(Payload& p, const NumericType& i) 56 { 57 if constexpr (byteIndex < sizeof(NumericType)) 58 { 59 p.appendBits(CHAR_BIT, static_cast<uint8_t>(i >> (8 * byteIndex))); 60 PackBytesUnaligned<NumericType, byteIndex + 1>(p, i); 61 } 62 } 63 64 /** @struct PackSingle 65 * @brief Utility to pack a single C++ element into a Payload 66 * 67 * User-defined types are expected to specialize this template in order to 68 * get their functionality. 69 * 70 * @tparam S - Type of element to pack. 71 */ 72 template <typename T> 73 struct PackSingle 74 { 75 /** @brief Do the operation to pack element. 76 * 77 * @param[in] p - Payload to pack into. 78 * @param[out] t - The reference to pack item into. 79 */ 80 static int op(Payload& p, const T& t) 81 { 82 static_assert(std::is_integral_v<T>, 83 "Attempt to pack a type that has no IPMI pack operation"); 84 // if not on a byte boundary, must pack values LSbit/LSByte first 85 if (p.bitCount) 86 { 87 PackBytesUnaligned<T>(p, t); 88 } 89 else 90 { 91 // copy in bits to vector.... 92 p.raw.resize(p.raw.size() + sizeof(T)); 93 uint8_t* out = p.raw.data() + p.raw.size() - sizeof(T); 94 PackBytes<T>(out, t); 95 } 96 return 0; 97 } 98 }; 99 100 /** @brief Specialization of PackSingle for std::tuple<T> */ 101 template <typename... T> 102 struct PackSingle<std::tuple<T...>> 103 { 104 static int op(Payload& p, const std::tuple<T...>& v) 105 { 106 return std::apply([&p](const T&... args) { return p.pack(args...); }, 107 v); 108 } 109 }; 110 111 /** @brief Specialization of PackSingle for std::string 112 * represented as a UCSD-Pascal style string 113 */ 114 template <> 115 struct PackSingle<std::string> 116 { 117 static int op(Payload& p, const std::string& t) 118 { 119 // check length first 120 uint8_t len; 121 if (t.length() > std::numeric_limits<decltype(len)>::max()) 122 { 123 using namespace phosphor::logging; 124 log<level::ERR>("long string truncated on IPMI message pack"); 125 return 1; 126 } 127 len = static_cast<uint8_t>(t.length()); 128 PackSingle<uint8_t>::op(p, len); 129 p.append(t.c_str(), t.c_str() + t.length()); 130 return 0; 131 } 132 }; 133 134 /** @brief Specialization of PackSingle for fixed_uint_t types 135 */ 136 template <bitcount_t N> 137 struct PackSingle<fixed_uint_t<N>> 138 { 139 static int op(Payload& p, const fixed_uint_t<N>& t) 140 { 141 size_t count = N; 142 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 143 static_assert(N <= std::numeric_limits<uint64_t>::digits, 144 "Type exceeds uint64_t limit"); 145 uint64_t bits = static_cast<uint64_t>(t); 146 while (count > 0) 147 { 148 size_t appendCount = std::min(count, static_cast<size_t>(CHAR_BIT)); 149 p.appendBits(appendCount, static_cast<uint8_t>(bits)); 150 bits >>= CHAR_BIT; 151 count -= appendCount; 152 } 153 return 0; 154 } 155 }; 156 157 /** @brief Specialization of PackSingle for bool. */ 158 template <> 159 struct PackSingle<bool> 160 { 161 static int op(Payload& p, const bool& b) 162 { 163 p.appendBits(1, b); 164 return 0; 165 } 166 }; 167 168 /** @brief Specialization of PackSingle for std::bitset<N> */ 169 template <size_t N> 170 struct PackSingle<std::bitset<N>> 171 { 172 static int op(Payload& p, const std::bitset<N>& t) 173 { 174 size_t count = N; 175 static_assert(N <= (details::bitStreamSize - CHAR_BIT)); 176 unsigned long long bits = t.to_ullong(); 177 while (count > 0) 178 { 179 size_t appendCount = std::min(count, size_t(CHAR_BIT)); 180 p.appendBits(appendCount, static_cast<uint8_t>(bits)); 181 bits >>= CHAR_BIT; 182 count -= appendCount; 183 } 184 return 0; 185 } 186 }; 187 188 /** @brief Specialization of PackSingle for std::optional<T> */ 189 template <typename T> 190 struct PackSingle<std::optional<T>> 191 { 192 static int op(Payload& p, const std::optional<T>& t) 193 { 194 int ret = 0; 195 if (t) 196 { 197 ret = PackSingle<T>::op(p, *t); 198 } 199 return ret; 200 } 201 }; 202 203 /** @brief Specialization of PackSingle for std::array<T, N> */ 204 template <typename T, size_t N> 205 struct PackSingle<std::array<T, N>> 206 { 207 static int op(Payload& p, const std::array<T, N>& t) 208 { 209 int ret = 0; 210 for (const auto& v : t) 211 { 212 int ret = PackSingle<T>::op(p, v); 213 if (ret) 214 { 215 break; 216 } 217 } 218 return ret; 219 } 220 }; 221 222 /** @brief Specialization of PackSingle for std::vector<T> */ 223 template <typename T> 224 struct PackSingle<std::vector<T>> 225 { 226 static int op(Payload& p, const std::vector<T>& t) 227 { 228 int ret = 0; 229 for (const auto& v : t) 230 { 231 int ret = PackSingle<T>::op(p, v); 232 if (ret) 233 { 234 break; 235 } 236 } 237 return ret; 238 } 239 }; 240 241 /** @brief Specialization of PackSingle for std::vector<uint8_t> */ 242 template <> 243 struct PackSingle<std::vector<uint8_t>> 244 { 245 static int op(Payload& p, const std::vector<uint8_t>& t) 246 { 247 if (p.bitCount != 0) 248 { 249 return 1; 250 } 251 p.raw.reserve(p.raw.size() + t.size()); 252 p.raw.insert(p.raw.end(), t.begin(), t.end()); 253 return 0; 254 } 255 }; 256 257 /** @brief Specialization of PackSingle for SecureBuffer */ 258 template <> 259 struct PackSingle<SecureBuffer> 260 { 261 static int op(Payload& p, const SecureBuffer& t) 262 { 263 if (p.bitCount != 0) 264 { 265 return 1; 266 } 267 p.raw.reserve(p.raw.size() + t.size()); 268 p.raw.insert(p.raw.end(), t.begin(), t.end()); 269 return 0; 270 } 271 }; 272 273 /** @brief Specialization of PackSingle for std::span<const uint8_t> */ 274 template <> 275 struct PackSingle<std::span<const uint8_t>> 276 { 277 static int op(Payload& p, const std::span<const uint8_t>& t) 278 { 279 if (p.bitCount != 0) 280 { 281 return 1; 282 } 283 p.raw.reserve(p.raw.size() + t.size()); 284 p.raw.insert(p.raw.end(), t.begin(), t.end()); 285 return 0; 286 } 287 }; 288 289 /** @brief Specialization of PackSingle for std::string_view */ 290 template <> 291 struct PackSingle<std::string_view> 292 { 293 static int op(Payload& p, const std::string_view& t) 294 { 295 if (p.bitCount != 0) 296 { 297 return 1; 298 } 299 p.raw.reserve(p.raw.size() + t.size()); 300 p.raw.insert(p.raw.end(), t.begin(), t.end()); 301 return 0; 302 } 303 }; 304 305 /** @brief Specialization of PackSingle for std::variant<T, N> */ 306 template <typename... T> 307 struct PackSingle<std::variant<T...>> 308 { 309 static int op(Payload& p, const std::variant<T...>& v) 310 { 311 return std::visit( 312 [&p](const auto& arg) { 313 return PackSingle<std::decay_t<decltype(arg)>>::op(p, arg); 314 }, 315 v); 316 } 317 }; 318 319 /** @brief Specialization of PackSingle for Payload */ 320 template <> 321 struct PackSingle<Payload> 322 { 323 static int op(Payload& p, const Payload& t) 324 { 325 if (p.bitCount != 0 || t.bitCount != 0) 326 { 327 return 1; 328 } 329 p.raw.reserve(p.raw.size() + t.raw.size()); 330 p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end()); 331 return 0; 332 } 333 }; 334 335 } // namespace details 336 337 } // namespace message 338 339 } // namespace ipmi 340