[34] | 1 | // =================================================================== |
---|
| 2 | // |
---|
| 3 | // Copyright (c) 2005, Intel Corp. |
---|
| 4 | // All rights reserved. |
---|
| 5 | // |
---|
| 6 | // Redistribution and use in source and binary forms, with or without |
---|
| 7 | // modification, are permitted provided that the following conditions |
---|
| 8 | // are met: |
---|
| 9 | // |
---|
| 10 | // * Redistributions of source code must retain the above copyright |
---|
| 11 | // notice, this list of conditions and the following disclaimer. |
---|
| 12 | // * Redistributions in binary form must reproduce the above |
---|
| 13 | // copyright notice, this list of conditions and the following |
---|
| 14 | // disclaimer in the documentation and/or other materials provided |
---|
| 15 | // with the distribution. |
---|
| 16 | // * Neither the name of Intel Corporation nor the names of its |
---|
| 17 | // contributors may be used to endorse or promote products derived |
---|
| 18 | // from this software without specific prior written permission. |
---|
| 19 | // |
---|
| 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
---|
| 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
---|
| 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
---|
| 23 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
---|
| 24 | // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
| 25 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
---|
| 26 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
---|
| 27 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
| 28 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
---|
| 29 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
| 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
---|
| 31 | // OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
| 32 | // =================================================================== |
---|
| 33 | // |
---|
| 34 | // bsg.cpp |
---|
| 35 | // |
---|
| 36 | // This file will handle all the TPM Byte Stream functions |
---|
| 37 | // |
---|
| 38 | // ================================================================== |
---|
| 39 | |
---|
| 40 | #include <stdio.h> |
---|
| 41 | #include <string.h> |
---|
| 42 | #include <stdarg.h> |
---|
| 43 | #include <malloc.h> |
---|
| 44 | #include "tcg.h" |
---|
| 45 | #include "crypto.h" |
---|
| 46 | #include "bsg.h" |
---|
| 47 | #include "log.h" |
---|
| 48 | |
---|
| 49 | static int g_log_recursion_level = 0; |
---|
| 50 | |
---|
| 51 | // a largest buffer size. if we get a buf size bigger than this when unpacking, |
---|
| 52 | // will complain! |
---|
| 53 | #define BSG_MAX_BUF_SIZE (1<<18) |
---|
| 54 | |
---|
| 55 | #define bsglog(fmt, ...) do { \ |
---|
| 56 | int __i; \ |
---|
| 57 | for (__i=0; __i < g_log_recursion_level; __i++) { \ |
---|
| 58 | vtpmloginfomore (VTPM_LOG_BSG, "%s", " "); \ |
---|
| 59 | } \ |
---|
| 60 | vtpmloginfomore (VTPM_LOG_BSG, fmt, __VA_ARGS__); \ |
---|
| 61 | } while (0) |
---|
| 62 | |
---|
| 63 | |
---|
| 64 | // FIXME: trigger the selfcheck--need to use glibc hook to do this |
---|
| 65 | //BOOL dummy1 = BSG_static_selfcheck(); |
---|
| 66 | |
---|
| 67 | |
---|
| 68 | // Interpretting Types |
---|
| 69 | // ------------------- |
---|
| 70 | // |
---|
| 71 | // Incoming Types are composed of two parts {format, info} squished into a |
---|
| 72 | // BSG_UINT32. The first 4 bits is a format spec indicating what type of |
---|
| 73 | // data it is. If the first 4 bits are zero the info corresponds to a value in |
---|
| 74 | // BSG_s_fmt[]. This is a structure whose composition is described in |
---|
| 75 | // BSG_s_fmt[]. If the value is non-zero, info corresponds to the size of the |
---|
| 76 | // data (in bytes) being passed in. For example a UINT32 being passed in would |
---|
| 77 | // have a format of (__FMT_CONST | 4). If both, the format and info are zero, |
---|
| 78 | // this is interpretted as the end of the structure, and the result is returned. |
---|
| 79 | |
---|
| 80 | // these flags are mutually exclusive, so I'll just make them |
---|
| 81 | // format values which indicate the semantics of the 'info' part and the source |
---|
| 82 | // data. The above description has been accordingly adjusted. |
---|
| 83 | |
---|
| 84 | // format values for determining what type of data the incoming type is |
---|
| 85 | // it's a 4 bit value, occupying the high 4 bits |
---|
| 86 | #define __FMT_CONST (1UL << 28) // Constant sized value |
---|
| 87 | #define __FMT_DATA (2UL << 28) // Believed to be raw data NOT {size,data} |
---|
| 88 | #define __FMT_SIZE (3UL << 28) // A size. Used in FMT_SIZE??_DATA. |
---|
| 89 | #define __FMT_HSIZE (4UL << 28) // A number of handles |
---|
| 90 | #define __FMT_PACKED (5UL << 28) // 'info' is unused; the source data consists |
---|
| 91 | // of {size32, data} but we're to pack only the |
---|
| 92 | // data as that is already packed, and so |
---|
| 93 | // can/must be unpacked without |
---|
| 94 | // explicitly reading it size |
---|
| 95 | |
---|
| 96 | #define __FMT_MASK 0x0FFFFFFFUL // this masks out the 4-bit format |
---|
| 97 | #define __FMT_MASK_SIZE(type) ((type) & __FMT_MASK) |
---|
| 98 | #define __FMT_MASK_FORMAT(type) ((type) & (~__FMT_MASK)) |
---|
| 99 | |
---|
| 100 | // constant (8/16/32-bits) |
---|
| 101 | #define FMT_U8 (__FMT_CONST | 1UL) |
---|
| 102 | #define FMT_U16 (__FMT_CONST | 2UL) |
---|
| 103 | #define FMT_U32 (__FMT_CONST | 4UL) |
---|
| 104 | |
---|
| 105 | // const with a compiler-computed size |
---|
| 106 | #define FMT_SIZEOF(type) (__FMT_CONST | sizeof(type)) |
---|
| 107 | |
---|
| 108 | // other data (size bytes) |
---|
| 109 | // Used primarily for DIGESTS -> FMT_DATA(20) |
---|
| 110 | #define FMT_DATA(size) (__FMT_DATA | ((BSG_UINT32) (size) & __FMT_MASK)) |
---|
| 111 | |
---|
| 112 | // 16/32-bit size followed by N bytes of data |
---|
| 113 | #define FMT_SIZE16_DATA (__FMT_SIZE | 2UL) |
---|
| 114 | #define FMT_SIZE32_DATA (__FMT_SIZE | 4UL) |
---|
| 115 | |
---|
| 116 | // 16-bit size followed by N key handles |
---|
| 117 | #define FMT_SIZE16_HANDLES (__FMT_HSIZE | 2UL) |
---|
| 118 | |
---|
| 119 | #define DIGEST_SIZE 20 |
---|
| 120 | typedef BSG_UINT32 BSG_HANDLE; |
---|
| 121 | |
---|
| 122 | // TCPA_AUTH has 11 fields! |
---|
| 123 | #define MAX_FIELDS 11 |
---|
| 124 | typedef struct BSG_Format |
---|
| 125 | { |
---|
| 126 | BSG_Type type; |
---|
| 127 | const char* name; |
---|
| 128 | BSG_UINT32 fields[MAX_FIELDS + 1]; |
---|
| 129 | } BSG_Format; |
---|
| 130 | |
---|
| 131 | /* |
---|
| 132 | * TCPA structure data formats |
---|
| 133 | */ |
---|
| 134 | // this has to be manually kept in sync with the |
---|
| 135 | // Type enum!! the static_selfcheck() function should be used regularly! |
---|
| 136 | static BSG_Format s_fmt[] = |
---|
| 137 | { |
---|
| 138 | {BSG_TYPE_UINT32, "BSG_TYPE_UINT32", {FMT_U32, 0}}, |
---|
| 139 | {BSG_TYPE_UINT16, "BSG_TYPE_UINT16", {FMT_U16, 0}}, |
---|
| 140 | {BSG_TYPE_BYTE, "BSG_TYPE_BYTE", {FMT_U8, 0}}, |
---|
| 141 | {BSG_TYPE_BOOL, "BSG_TYPE_BOOL", {FMT_U8, 0}}, |
---|
| 142 | {BSG_TPM_SIZE32_DATA, "BSG_TPM_SIZE32_DATA", {FMT_SIZE32_DATA, 0}}, |
---|
| 143 | {BSG_TPM_TAG, "BSG_TPM_TAG", {FMT_SIZEOF(TPM_TAG), 0}}, |
---|
| 144 | {BSG_TPM_HANDLE, "BSG_TPM_HANDLE", {FMT_SIZEOF(TPM_HANDLE), 0}}, |
---|
| 145 | {BSG_TPM_RESULT, "BSG_TPM_RESULT", {FMT_SIZEOF(TPM_RESULT), 0}}, |
---|
| 146 | {BSG_TPM_RESOURCE_TYPE, "BSG_TPM_RESOURCE_TYPE", {FMT_SIZEOF(TPM_RESOURCE_TYPE), 0}}, |
---|
| 147 | {BSG_TPM_COMMAND_CODE, "BSG_TPM_COMMAND_CODE", {FMT_U32, 0}}, |
---|
| 148 | {BSG_TPM_AUTH_DATA_USAGE, "BSG_TPM_AUTH_DATA_USAGE", {FMT_U8, 0}}, |
---|
| 149 | {BSG_TPM_ALGORITHM_ID, "BSG_TPM_ALGORITHM_ID", {FMT_U32, 0}}, |
---|
| 150 | {BSG_TPM_PROTOCOL_ID, "BSG_TPM_PROTOCOL_ID", {FMT_SIZEOF(TPM_PROTOCOL_ID), 0}}, |
---|
| 151 | {BSG_TPM_KEY_USAGE, "BSG_TPM_KEY_USAGE", {FMT_U16, 0}}, |
---|
| 152 | {BSG_TPM_ENC_SCHEME, "BSG_TPM_ENC_SCHEME", {FMT_U16, 0}}, |
---|
| 153 | {BSG_TPM_SIG_SCHEME, "BSG_TPM_SIG_SCHEME", {FMT_U16, 0}}, |
---|
| 154 | {BSG_TPM_MIGRATE_SCHEME, "BSG_TPM_MIGRATE_SCHEME", {FMT_U16, 0}}, |
---|
| 155 | {BSG_TPM_KEY_FLAGS, "BSG_TPM_KEY_FLAGS", {FMT_U32, 0}}, |
---|
| 156 | |
---|
| 157 | {BSG_TPM_AUTHDATA, "BSG_TPM_AUTHDATA", {FMT_DATA(DIGEST_SIZE), 0}}, |
---|
| 158 | {BSG_TPM_SECRET, "BSG_TPM_SECRET", {BSG_TPM_AUTHDATA, 0}}, |
---|
| 159 | {BSG_TPM_ENCAUTH, "BSG_TPM_ENCAUTH", {BSG_TPM_AUTHDATA, 0}}, |
---|
| 160 | {BSG_TPM_PAYLOAD_TYPE, "BSG_TPM_PAYLOAD_TYPE", {FMT_SIZEOF(TPM_PAYLOAD_TYPE), 0}}, |
---|
| 161 | |
---|
| 162 | {BSG_TPM_VERSION, "BSG_TPM_VERSION", {FMT_DATA(4), 0}}, // vers 1.2 |
---|
| 163 | {BSG_TPM_DIGEST, "BSG_TPM_DIGEST", {FMT_DATA(DIGEST_SIZE), 0}}, |
---|
| 164 | {BSG_TPM_COMPOSITE_HASH, "BSG_TPM_COMPOSITE_HASH", {BSG_TPM_DIGEST, 0}}, |
---|
| 165 | {BSG_TPM_CHOSENID_HASH, "BSG_TPM_CHOSENID_HASH", {BSG_TPM_DIGEST, 0}}, |
---|
| 166 | |
---|
| 167 | {BSG_TPM_NONCE, "BSG_TPM_NONCE", {FMT_DATA(DIGEST_SIZE), 0}}, |
---|
| 168 | {BSG_TPM_KEY_HANDLE, "BSG_TPM_KEY_HANDLE", {FMT_SIZEOF(TPM_KEY_HANDLE), 0}}, |
---|
| 169 | {BSG_TPM_KEY_HANDLE_LIST, "BSG_TPM_KEY_HANDLE_LIST", |
---|
| 170 | {FMT_SIZE16_HANDLES, 0}}, |
---|
| 171 | |
---|
| 172 | {BSG_TPM_KEY_PARMS, "BSG_TPM_KEY_PARMS", { |
---|
| 173 | BSG_TPM_ALGORITHM_ID, |
---|
| 174 | BSG_TPM_ENC_SCHEME, |
---|
| 175 | BSG_TPM_SIG_SCHEME, |
---|
| 176 | FMT_SIZE32_DATA, |
---|
| 177 | 0}}, |
---|
| 178 | {BSG_TPM_RSA_KEY_PARMS, "BSG_TPM_RSA_KEY_PARMS", { |
---|
| 179 | FMT_U32, FMT_U32, FMT_SIZE32_DATA, 0}}, |
---|
| 180 | {BSG_TPM_STORE_PUBKEY, "BSG_TPM_STORE_PUBKEY", {FMT_SIZE32_DATA, 0}}, |
---|
| 181 | {BSG_TPM_PUBKEY, "BSG_TPM_PUBKEY", {BSG_TPM_KEY_PARMS, BSG_TPM_STORE_PUBKEY, 0}}, |
---|
| 182 | {BSG_TPM_KEY, "BSG_TPM_KEY", { |
---|
| 183 | BSG_TPM_VERSION, |
---|
| 184 | BSG_TPM_KEY_USAGE, |
---|
| 185 | BSG_TPM_KEY_FLAGS, |
---|
| 186 | BSG_TPM_AUTH_DATA_USAGE, |
---|
| 187 | BSG_TPM_KEY_PARMS, |
---|
| 188 | FMT_SIZE32_DATA, // the PCR_INFO |
---|
| 189 | BSG_TPM_STORE_PUBKEY, |
---|
| 190 | FMT_SIZE32_DATA, // the encrypted part |
---|
| 191 | 0}}, |
---|
| 192 | |
---|
| 193 | {BSG_TPM_MIGRATIONKEYAUTH, "BSG_TPM_MIGRATIONKEYAUTH", { |
---|
| 194 | BSG_TPM_PUBKEY, |
---|
| 195 | BSG_TPM_MIGRATE_SCHEME, |
---|
| 196 | BSG_TPM_DIGEST, 0}}, |
---|
| 197 | |
---|
| 198 | {BSG_TCPA_AUDIT_EVENT, "TCPA_AUDIT_EVENT", { |
---|
| 199 | BSG_TPM_COMMAND_CODE, |
---|
| 200 | BSG_TPM_RESULT, 0 }}, |
---|
| 201 | |
---|
| 202 | {BSG_TCPA_EVENT_CERT, "TCPA_EVENT_CERT", { |
---|
| 203 | BSG_TPM_DIGEST, |
---|
| 204 | BSG_TPM_DIGEST, |
---|
| 205 | FMT_DATA(2), |
---|
| 206 | FMT_SIZE32_DATA, 0}}, |
---|
| 207 | |
---|
| 208 | {BSG_TPM_PCR_SELECTION, "BSG_TPM_PCR_SELECTION", {FMT_SIZE16_DATA, 0} }, |
---|
| 209 | {BSG_TPM_PCR_COMPOSITE, "BSG_TPM_PCR_COMPOSITE", { BSG_TPM_PCR_SELECTION, |
---|
| 210 | FMT_SIZE32_DATA, |
---|
| 211 | 0} }, |
---|
| 212 | |
---|
| 213 | {BSG_TPM_PCR_INFO, "BSG_TPM_PCR_INFO", { BSG_TPM_PCR_SELECTION, |
---|
| 214 | BSG_TPM_COMPOSITE_HASH, |
---|
| 215 | BSG_TPM_COMPOSITE_HASH, |
---|
| 216 | 0} }, |
---|
| 217 | |
---|
| 218 | |
---|
| 219 | {BSG_TPM_STORED_DATA, "BSG_TPM_STORED_DATA", { |
---|
| 220 | BSG_TPM_VERSION, |
---|
| 221 | FMT_SIZE32_DATA, |
---|
| 222 | FMT_SIZE32_DATA, |
---|
| 223 | 0}}, |
---|
| 224 | {BSG_TPM_SYMMETRIC_KEY, "BSG_TPM_SYMMETRIC_KEY", { |
---|
| 225 | BSG_TPM_ALGORITHM_ID, |
---|
| 226 | BSG_TPM_ENC_SCHEME, |
---|
| 227 | FMT_SIZE16_DATA, |
---|
| 228 | 0}}, |
---|
| 229 | {BSG_TPM_STORE_PRIVKEY, "BSG_TPM_STORE_PRIVKEY", {FMT_SIZE32_DATA, 0}}, |
---|
| 230 | {BSG_TPM_STORE_ASYMKEY, "BSG_TPM_STORE_ASYMKEY", { |
---|
| 231 | BSG_TPM_PAYLOAD_TYPE, |
---|
| 232 | BSG_TPM_SECRET, |
---|
| 233 | BSG_TPM_SECRET, |
---|
| 234 | BSG_TPM_DIGEST, |
---|
| 235 | BSG_TPM_STORE_PRIVKEY, |
---|
| 236 | 0}}, |
---|
| 237 | {BSG_TPM_MIGRATE_ASYMKEY, "BSG_TPM_MIGRATE_ASYMKEY", { |
---|
| 238 | BSG_TPM_PAYLOAD_TYPE, |
---|
| 239 | BSG_TPM_SECRET, |
---|
| 240 | BSG_TPM_DIGEST, |
---|
| 241 | FMT_U32, |
---|
| 242 | BSG_TPM_STORE_PRIVKEY, |
---|
| 243 | 0}}, |
---|
| 244 | |
---|
| 245 | {BSG_TPM_QUOTE_INFO, "BSG_TPM_QUOTE_INFO", { |
---|
| 246 | BSG_TPM_VERSION, |
---|
| 247 | FMT_DATA(4), |
---|
| 248 | BSG_TPM_COMPOSITE_HASH, |
---|
| 249 | BSG_TPM_NONCE, |
---|
| 250 | 0}}, |
---|
| 251 | |
---|
| 252 | {BSG_TPM_IDENTITY_CONTENTS, "BSG_TPM_IDENTITY_CONTENTS", { |
---|
| 253 | BSG_TPM_VERSION, |
---|
| 254 | FMT_U32, |
---|
| 255 | BSG_TPM_CHOSENID_HASH, |
---|
| 256 | BSG_TPM_PUBKEY, |
---|
| 257 | 0}}, |
---|
| 258 | |
---|
| 259 | {BSG_TPM_PCRVALUE, "BSG_TPM_PCRVALUE", {FMT_DATA(DIGEST_SIZE), 0}}, |
---|
| 260 | |
---|
| 261 | {BSG_TCPA_PCR_FLAGS, "TCPA_PCR_FLAGS", { |
---|
| 262 | FMT_U8, |
---|
| 263 | FMT_U8, |
---|
| 264 | 0}}, |
---|
| 265 | |
---|
| 266 | {BSG_TCS_AUTH, "TCS_AUTH", { |
---|
| 267 | BSG_TYPE_UINT32, |
---|
| 268 | BSG_TPM_NONCE, |
---|
| 269 | BSG_TPM_NONCE, |
---|
| 270 | BSG_TYPE_BOOL, |
---|
| 271 | BSG_TPM_AUTHDATA, |
---|
| 272 | 0}}, |
---|
| 273 | |
---|
| 274 | {BSG_TPM_KEY_NONSENSITIVE, "BSG_TPM_KEY_NONSENSITIVE", { |
---|
| 275 | BSG_TPM_VERSION, |
---|
| 276 | BSG_TPM_KEY_USAGE, |
---|
| 277 | BSG_TPM_KEY_FLAGS, |
---|
| 278 | BSG_TPM_AUTH_DATA_USAGE, |
---|
| 279 | BSG_TPM_KEY_PARMS, |
---|
| 280 | FMT_SIZE32_DATA, |
---|
| 281 | BSG_TPM_STORE_PUBKEY, |
---|
| 282 | 0}}, |
---|
| 283 | |
---|
| 284 | {BSG_PACKED, "BSG_PACKED", { |
---|
| 285 | __FMT_PACKED, |
---|
| 286 | 0 }}, |
---|
| 287 | |
---|
| 288 | {BSG_TYPE_MAX, "", {0}}, |
---|
| 289 | }; |
---|
| 290 | |
---|
| 291 | |
---|
| 292 | static const BSG_Format* find_format (BSG_Type t) { |
---|
| 293 | BSG_Format * f = s_fmt; |
---|
| 294 | |
---|
| 295 | if (t >= BSG_TYPE_MAX) { |
---|
| 296 | return NULL; |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | // WARNING: this depends on the enum and s_fmt[] array being in sync! make |
---|
| 300 | // sure to run the static_selfcheck() to make sure |
---|
| 301 | f = s_fmt + (t - BSG_TYPE_FIRST); |
---|
| 302 | |
---|
| 303 | return f; |
---|
| 304 | } |
---|
| 305 | |
---|
| 306 | // |
---|
| 307 | // a consistency-checking routine which can be run at compile time |
---|
| 308 | // (ie. immediately after compilation) |
---|
| 309 | // |
---|
| 310 | // tasks: |
---|
| 311 | // - verify that s_fmt has one entry per Type t, and that entry is at s_fmt[t] |
---|
| 312 | // |
---|
| 313 | // conditions: |
---|
| 314 | // - need that s_fmt[0] is the first type listed in the Type enum! ie the first |
---|
| 315 | // Type has value 0, not 1 |
---|
| 316 | // |
---|
| 317 | // FIXME: should have a function be passed in here which is called if the test |
---|
| 318 | // fails. Then the caller can decide what to do: abort, notify, whatever |
---|
| 319 | // |
---|
| 320 | BOOL BSG_static_selfcheck () |
---|
| 321 | { |
---|
| 322 | int i; |
---|
| 323 | |
---|
| 324 | for (i=BSG_TYPE_FIRST; i <= BSG_TYPE_MAX; i++) { |
---|
| 325 | if (s_fmt[i - BSG_TYPE_FIRST].type != i) { |
---|
| 326 | bsglog ("%s\n", "BSG: static_selfcheck failed!\n"); |
---|
| 327 | bsglog ("failure at %i, allegedly %s\n", |
---|
| 328 | i, s_fmt[i - BSG_TYPE_FIRST].name); |
---|
| 329 | abort(); |
---|
| 330 | return FALSE; |
---|
| 331 | } |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | bsglog ("%s\n", "BSG: static_selfcheck success!"); |
---|
| 335 | return TRUE; |
---|
| 336 | } |
---|
| 337 | |
---|
| 338 | |
---|
| 339 | /** |
---|
| 340 | * Flatten a TCPA structure into a buffer in big-endian format |
---|
| 341 | * @type: TCPA structure type |
---|
| 342 | * @src: (IN) TCPA structure (OUT) end of TCPA structure |
---|
| 343 | * @dst: (OUT) flattened data |
---|
| 344 | * Returns: Flattened size or -1 for unknown types |
---|
| 345 | */ |
---|
| 346 | // make it so that it can just run through the whole process and return |
---|
| 347 | // the packed size, without packing anything. this will be done if dst is NULL. |
---|
| 348 | static int BSG_Pack_private(BSG_Type type, const BSG_BYTE** src, BSG_BYTE* dst) |
---|
| 349 | { |
---|
| 350 | // check incoming parameters |
---|
| 351 | if (*src == NULL) |
---|
| 352 | return 0; |
---|
| 353 | |
---|
| 354 | const BSG_BYTE* s = *src; |
---|
| 355 | BSG_BYTE* d = dst; |
---|
| 356 | |
---|
| 357 | BSG_UINT32 size = __FMT_MASK_SIZE(type); |
---|
| 358 | BSG_UINT32 format = __FMT_MASK_FORMAT(type); |
---|
| 359 | |
---|
| 360 | if (format == __FMT_CONST) // We are dealing with a fixed length value eg. UINT32 |
---|
| 361 | { |
---|
| 362 | BSG_UINT32 val = 0; |
---|
| 363 | switch (size) { |
---|
| 364 | case 1: val = * (BYTE*) s; break; |
---|
| 365 | case 2: val = * (unsigned short*) s; break; |
---|
| 366 | case 4: val = * (BSG_UINT32*) s; break; |
---|
| 367 | } |
---|
| 368 | if (dst) |
---|
| 369 | BSG_PackConst(val, size, d); |
---|
| 370 | |
---|
| 371 | s += size; |
---|
| 372 | d += size; |
---|
| 373 | } else if (format == __FMT_DATA) { // We are dealing with raw data. Not sure when |
---|
| 374 | // this is used. |
---|
| 375 | |
---|
| 376 | if (dst) { |
---|
| 377 | bsglog ("BSG: __FMT_DATA size %d, src %p, dst %p\n", size, s, d); |
---|
| 378 | memcpy(d, s, size); |
---|
| 379 | } |
---|
| 380 | |
---|
| 381 | s += size; |
---|
| 382 | d += size; |
---|
| 383 | } else if (format == __FMT_SIZE || format == __FMT_HSIZE) { // It's a size, followed by that much data or handles |
---|
| 384 | |
---|
| 385 | BSG_UINT32 psize = 0; |
---|
| 386 | switch (size) { |
---|
| 387 | case 1: psize = * (BYTE*) s; break; |
---|
| 388 | case 2: psize = * (unsigned short*) s; break; |
---|
| 389 | case 4: psize = * (BSG_UINT32*) s; break; |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | if (dst) |
---|
| 393 | BSG_PackConst(psize, size, d); |
---|
| 394 | |
---|
| 395 | s += size; |
---|
| 396 | d += size; |
---|
| 397 | |
---|
| 398 | // now 's' points to an address, so cast it to BSG_BYTE** |
---|
| 399 | const BSG_BYTE* pdata = * ((BSG_BYTE**) s); |
---|
| 400 | s += sizeof(BSG_BYTE*); |
---|
| 401 | |
---|
| 402 | if (format == __FMT_HSIZE) {// This is a list of psize Handles |
---|
| 403 | if (dst) { |
---|
| 404 | BSG_HANDLE* d2 = (BSG_HANDLE*) d; |
---|
| 405 | BSG_HANDLE* p2 = (BSG_HANDLE*) pdata; |
---|
| 406 | BSG_UINT32 i; |
---|
| 407 | for (i = 0; i < psize; i++) |
---|
| 408 | d2[i] = BSG_UnpackConst((BSG_BYTE*)(p2 + i), 4); |
---|
| 409 | |
---|
| 410 | } |
---|
| 411 | d += psize * sizeof(BSG_HANDLE); |
---|
| 412 | } else {// If it's not psize handles, it's psize data. |
---|
| 413 | if (psize > 0) { |
---|
| 414 | if (dst) { |
---|
| 415 | bsglog ("BSG: __FMT_SIZE, size=%d, src=%p, dst=%p\n", |
---|
| 416 | psize, pdata, d); |
---|
| 417 | memcpy(d, pdata, psize); |
---|
| 418 | } |
---|
| 419 | } |
---|
| 420 | d += psize; |
---|
| 421 | } |
---|
| 422 | } else if (format == __FMT_PACKED) { |
---|
| 423 | // the source buffer is a pack_constbuf_t, which has a size and a |
---|
| 424 | // pointer. just copy the buffer value, the size is not included in the |
---|
| 425 | // output stream. |
---|
| 426 | pack_constbuf_t * buf = (pack_constbuf_t*) s; |
---|
| 427 | |
---|
| 428 | if (dst) { |
---|
| 429 | bsglog ("BSG: __FMT_PACKED, size=%d, src=%p, dst=%p\n", |
---|
| 430 | buf->size, buf->data, d); |
---|
| 431 | memcpy(d, buf->data, buf->size); |
---|
| 432 | } |
---|
| 433 | |
---|
| 434 | s += buf->size; |
---|
| 435 | d += buf->size; |
---|
| 436 | } else if (format == 0) {// No flags are set. This is a structure & it should |
---|
| 437 | // be looked up in the bsg_s_fmt[] |
---|
| 438 | |
---|
| 439 | const BSG_Format* x = find_format (type); |
---|
| 440 | if (x == NULL) { |
---|
| 441 | vtpmloginfo(VTPM_LOG_BSG, "BSG_Pack: cannot find type %d\n", type); |
---|
| 442 | return -1; |
---|
| 443 | } |
---|
| 444 | |
---|
| 445 | if (dst) |
---|
| 446 | bsglog ("BSG_Pack type %s\n", x->name); |
---|
| 447 | |
---|
| 448 | |
---|
| 449 | // iterate through the fields |
---|
| 450 | const BSG_UINT32* f = x->fields; |
---|
| 451 | for (; *f; f++) { |
---|
| 452 | int fsize; |
---|
| 453 | |
---|
| 454 | g_log_recursion_level++; |
---|
| 455 | fsize = BSG_Pack_private((BSG_Type) *f, &s, dst ? d : NULL); |
---|
| 456 | g_log_recursion_level--; |
---|
| 457 | |
---|
| 458 | if (fsize <= 0) |
---|
| 459 | return fsize; |
---|
| 460 | |
---|
| 461 | d += fsize; |
---|
| 462 | } |
---|
| 463 | } else { |
---|
| 464 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Pack(): Unknown format %d\n", format); |
---|
| 465 | return -1; |
---|
| 466 | } |
---|
| 467 | |
---|
| 468 | *src = s; |
---|
| 469 | return (d - dst); |
---|
| 470 | } |
---|
| 471 | |
---|
| 472 | /** |
---|
| 473 | * Unflatten a TCPA structure from a buffer in big-endian format |
---|
| 474 | * @type: TCPA structure type |
---|
| 475 | * @src: flattened data |
---|
| 476 | * @dst: (IN) TCPA structure (OUT) end of TCPA structure |
---|
| 477 | * Returns: Flattened size |
---|
| 478 | * Note: Returns flattened size NOT the unpacked structure size |
---|
| 479 | */ |
---|
| 480 | static int BSG_Unpack_private(BSG_Type type, const BSG_BYTE* src, BSG_BYTE** dst) { |
---|
| 481 | // check incoming parameters |
---|
| 482 | if (src == NULL) |
---|
| 483 | return 0; |
---|
| 484 | |
---|
| 485 | |
---|
| 486 | const BSG_BYTE* s = src; |
---|
| 487 | BSG_BYTE* d = dst ? *dst:NULL; |
---|
| 488 | if (dst && !d) |
---|
| 489 | dst = NULL; |
---|
| 490 | |
---|
| 491 | BSG_UINT32 size = __FMT_MASK_SIZE(type); |
---|
| 492 | BSG_UINT32 format = __FMT_MASK_FORMAT(type); |
---|
| 493 | |
---|
| 494 | if (format == __FMT_CONST) {// We are dealing with a fixed length value ie. UINT32 |
---|
| 495 | |
---|
| 496 | BSG_UINT32 val = BSG_UnpackConst(s, size); |
---|
| 497 | |
---|
| 498 | if (dst) { |
---|
| 499 | switch (size) { |
---|
| 500 | case 1: *(BYTE *) d = (BSG_BYTE) val; break; |
---|
| 501 | case 2: *(unsigned short*) d = (unsigned short) val; break; |
---|
| 502 | case 4: *(BSG_UINT32*) d = (BSG_UINT32) val; break; |
---|
| 503 | } |
---|
| 504 | } |
---|
| 505 | |
---|
| 506 | s += size; |
---|
| 507 | d += size; |
---|
| 508 | } else if (format == __FMT_DATA) {// We are dealing with raw data. Not sure when this is used. |
---|
| 509 | if (dst) |
---|
| 510 | memcpy(d, s, size); |
---|
| 511 | |
---|
| 512 | d += size; |
---|
| 513 | s += size; |
---|
| 514 | } else if (format == __FMT_SIZE || format == __FMT_HSIZE) {// It's a size, followed by that much data or handles |
---|
| 515 | |
---|
| 516 | BSG_UINT32 psize = BSG_UnpackConst(s, size); |
---|
| 517 | |
---|
| 518 | if (psize > BSG_MAX_BUF_SIZE) { |
---|
| 519 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Unpack runs into var-sized data bigger than %u bytes!!\n", |
---|
| 520 | BSG_MAX_BUF_SIZE); |
---|
| 521 | return -1; |
---|
| 522 | } |
---|
| 523 | |
---|
| 524 | if (dst) { |
---|
| 525 | switch (size) { |
---|
| 526 | case 1: *(BYTE *) d = (BSG_BYTE) psize; break; |
---|
| 527 | case 2: *(unsigned short*) d = (unsigned short) psize; break; |
---|
| 528 | case 4: *(BSG_UINT32*) d = (BSG_UINT32) psize; break; |
---|
| 529 | } |
---|
| 530 | } |
---|
| 531 | |
---|
| 532 | s += size; |
---|
| 533 | d += size; |
---|
| 534 | |
---|
| 535 | BSG_BYTE* pdata = NULL; |
---|
| 536 | |
---|
| 537 | if (psize) { |
---|
| 538 | if (format == __FMT_HSIZE) { // This is a list of psize Handles |
---|
| 539 | if (dst) { |
---|
| 540 | BSG_HANDLE* s2 = (BSG_HANDLE*) s; |
---|
| 541 | pdata = (BSG_BYTE *)malloc(psize * sizeof(BSG_HANDLE)); |
---|
| 542 | if (!pdata) |
---|
| 543 | return -1; |
---|
| 544 | |
---|
| 545 | BSG_HANDLE* p2 = (BSG_HANDLE*) pdata; |
---|
| 546 | BSG_UINT32 i; |
---|
| 547 | for (i = 0; i < psize; i++) { |
---|
| 548 | BSG_PackConst(s2[i], 4, (BSG_BYTE*)(p2 + i)); |
---|
| 549 | } |
---|
| 550 | } |
---|
| 551 | s += psize * sizeof(BSG_HANDLE); |
---|
| 552 | } else { // If it's not psize handles, it's psize data. |
---|
| 553 | if (dst) { |
---|
| 554 | pdata = (BSG_BYTE *)malloc(sizeof(BSG_BYTE) * psize); |
---|
| 555 | if (!pdata) |
---|
| 556 | return -1; |
---|
| 557 | memcpy(pdata, s, psize); |
---|
| 558 | } |
---|
| 559 | s += psize; |
---|
| 560 | } |
---|
| 561 | } |
---|
| 562 | if (dst) |
---|
| 563 | *(void**) d = pdata; |
---|
| 564 | |
---|
| 565 | d += sizeof(void*); |
---|
| 566 | } else if (format == __FMT_PACKED) { |
---|
| 567 | |
---|
| 568 | // this doesn't make sense for unpacking! |
---|
| 569 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Unpack() called with format __FMT_PACKED. " |
---|
| 570 | "This does not make sense\n"); |
---|
| 571 | |
---|
| 572 | return -1; |
---|
| 573 | } else if (format == 0) {// No flags are set. This is a structure & it should |
---|
| 574 | // be looked up in the bsg_s_fmt[] |
---|
| 575 | |
---|
| 576 | const BSG_Format* x = find_format (type); |
---|
| 577 | if (x == NULL) { |
---|
| 578 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Unpack: cannot find type %d\n", type); |
---|
| 579 | return -1; |
---|
| 580 | } |
---|
| 581 | |
---|
| 582 | const BSG_UINT32* f = x->fields; |
---|
| 583 | for (; *f; f++) { |
---|
| 584 | int fsize = BSG_Unpack_private((BSG_Type) *f, s, dst ? &d:NULL); |
---|
| 585 | if (fsize <= 0) |
---|
| 586 | return fsize; |
---|
| 587 | s += fsize; |
---|
| 588 | } |
---|
| 589 | } |
---|
| 590 | |
---|
| 591 | if (dst) |
---|
| 592 | *dst = d; |
---|
| 593 | return (s - src); |
---|
| 594 | } |
---|
| 595 | |
---|
| 596 | /** |
---|
| 597 | * Free memory associated with unpacked TCPA structure |
---|
| 598 | * @type: TCPA structure type |
---|
| 599 | * @src: (IN) TCPA structure (OUT) end of TCPA structure |
---|
| 600 | * Note: Destroy should be called on all structures created with Unpack |
---|
| 601 | * to ensure that any allocated memory is freed |
---|
| 602 | */ |
---|
| 603 | static void BSG_Destroy_private(BSG_Type type, BSG_BYTE** src) { |
---|
| 604 | BSG_BYTE* s = *src; |
---|
| 605 | |
---|
| 606 | BSG_UINT32 size = __FMT_MASK_SIZE(type); |
---|
| 607 | BSG_UINT32 format = __FMT_MASK_FORMAT(type); |
---|
| 608 | |
---|
| 609 | if ((src == NULL) || (*src == NULL)) { |
---|
| 610 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Destroy() called with NULL src\n"); |
---|
| 611 | return; |
---|
| 612 | } |
---|
| 613 | |
---|
| 614 | if (format == __FMT_CONST || format == __FMT_DATA) |
---|
| 615 | s += size; |
---|
| 616 | else if (format == __FMT_SIZE || format == __FMT_HSIZE) { |
---|
| 617 | s += size; |
---|
| 618 | BSG_BYTE* ptr = *(BSG_BYTE**) s; |
---|
| 619 | free(ptr); |
---|
| 620 | s += sizeof(void*); |
---|
| 621 | } else if (format == __FMT_PACKED) { |
---|
| 622 | |
---|
| 623 | // this doesn't make sense for unpacking, hence also for Destroy() |
---|
| 624 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Destroy() called with format __FMT_PACKED. " |
---|
| 625 | "This does not make sense\n"); |
---|
| 626 | |
---|
| 627 | return; |
---|
| 628 | } else if (format == 0) { |
---|
| 629 | const BSG_Format* x = find_format (type); |
---|
| 630 | if (x == NULL) { |
---|
| 631 | vtpmlogerror(VTPM_LOG_BSG, "BSG_Destroy: cannot find type %d\n", type); |
---|
| 632 | return; |
---|
| 633 | } |
---|
| 634 | |
---|
| 635 | const BSG_UINT32* f = x->fields; |
---|
| 636 | for (; *f; f++) |
---|
| 637 | BSG_Destroy_private((BSG_Type) *f, &s); |
---|
| 638 | } |
---|
| 639 | |
---|
| 640 | *src = s; |
---|
| 641 | } |
---|
| 642 | |
---|
| 643 | int BSG_Pack(BSG_Type type, const void* src, BSG_BYTE* dst) |
---|
| 644 | { |
---|
| 645 | const BSG_BYTE* src2 = (const BSG_BYTE*) src; |
---|
| 646 | return BSG_Pack_private(type, &src2, dst); |
---|
| 647 | } |
---|
| 648 | |
---|
| 649 | int BSG_Unpack(BSG_Type type, const BSG_BYTE* src, void* dst) |
---|
| 650 | { |
---|
| 651 | BSG_BYTE* dst2 = (BSG_BYTE*) dst; |
---|
| 652 | return BSG_Unpack_private(type, src, dst ? &dst2:NULL); |
---|
| 653 | } |
---|
| 654 | |
---|
| 655 | void BSG_Destroy(BSG_Type type, void* src) |
---|
| 656 | { |
---|
| 657 | BSG_BYTE* src2 = (BSG_BYTE*) src; |
---|
| 658 | BSG_Destroy_private(type, &src2); |
---|
| 659 | } |
---|
| 660 | |
---|
| 661 | /** |
---|
| 662 | * Pack a 8/16/32-bit constant into a buffer in big-endian format |
---|
| 663 | * @val: constant value |
---|
| 664 | * @size: constant size in bytes (1, 2, or 4) |
---|
| 665 | * @dst: (OUT) buffer |
---|
| 666 | */ |
---|
| 667 | void BSG_PackConst(BSG_UINT32 val, int size, BSG_BYTE* dst) { |
---|
| 668 | bsglog ("BSG: PackConst on %d of size %i into address %p\n", val, size, dst); |
---|
| 669 | |
---|
| 670 | switch (size) { |
---|
| 671 | case 4: |
---|
| 672 | dst[0] = (BSG_BYTE)((val >> 24) & 0xff); |
---|
| 673 | dst[1] = (BSG_BYTE)((val >> 16) & 0xff); |
---|
| 674 | dst[2] = (BSG_BYTE)((val >> 8) & 0xff); |
---|
| 675 | dst[3] = (BSG_BYTE)(val & 0xff); |
---|
| 676 | break; |
---|
| 677 | case 2: |
---|
| 678 | dst[0] = (BSG_BYTE)((val >> 8) & 0xff); |
---|
| 679 | dst[1] = (BSG_BYTE)(val & 0xff); |
---|
| 680 | break; |
---|
| 681 | case 1: |
---|
| 682 | dst[0] = (BSG_BYTE)(val & 0xff); |
---|
| 683 | break; |
---|
| 684 | } |
---|
| 685 | } |
---|
| 686 | |
---|
| 687 | /** |
---|
| 688 | * Unpack a 8/16/32-bit constant from a buffer in big-endian format |
---|
| 689 | * @src: buffer |
---|
| 690 | * @size: constant size in bytes (1, 2, or 4) |
---|
| 691 | */ |
---|
| 692 | BSG_UINT32 BSG_UnpackConst(const BSG_BYTE* src, int size) { |
---|
| 693 | BSG_UINT32 val = 0; |
---|
| 694 | |
---|
| 695 | if (src == NULL) |
---|
| 696 | return 0; |
---|
| 697 | |
---|
| 698 | switch (size) { |
---|
| 699 | case 4: |
---|
| 700 | val = (((BSG_UINT32) src[0]) << 24 |
---|
| 701 | | ((BSG_UINT32) src[1]) << 16 |
---|
| 702 | | ((BSG_UINT32) src[2]) << 8 |
---|
| 703 | | (BSG_UINT32) src[3]); |
---|
| 704 | break; |
---|
| 705 | case 2: |
---|
| 706 | val = (((BSG_UINT32) src[0]) << 8 | (BSG_UINT32) src[1]); |
---|
| 707 | break; |
---|
| 708 | case 1: |
---|
| 709 | val = (BSG_UINT32) src[0]; |
---|
| 710 | break; |
---|
| 711 | } |
---|
| 712 | return val; |
---|
| 713 | } |
---|
| 714 | |
---|
| 715 | // Pack a list of parameters. Beware not to send values, but rather you must |
---|
| 716 | // send a pointer to your values Instead. This includes UINT32's. |
---|
| 717 | int BSG_PackList( BSG_BYTE* dst, int ParamCount, ... ) { |
---|
| 718 | int ParamNumber; |
---|
| 719 | BSG_Type format; |
---|
| 720 | BSG_BYTE* val = NULL; |
---|
| 721 | int size=0; |
---|
| 722 | |
---|
| 723 | va_list paramList; |
---|
| 724 | va_start( paramList, ParamCount ); |
---|
| 725 | |
---|
| 726 | for( ParamNumber = 1; ParamNumber <= ParamCount; ParamNumber++) { |
---|
| 727 | //Strangeness with int is because gcc wanted an int rather than a enum of ints. |
---|
| 728 | format = (BSG_Type) va_arg( paramList, int ); |
---|
| 729 | val = va_arg( paramList, BSG_BYTE* ); |
---|
| 730 | size += BSG_Pack(format, val, dst == NULL ? NULL : dst + size); |
---|
| 731 | } |
---|
| 732 | |
---|
| 733 | va_end (paramList); |
---|
| 734 | |
---|
| 735 | return size; |
---|
| 736 | } |
---|
| 737 | |
---|
| 738 | // Unpack a list of parameters. |
---|
| 739 | int BSG_UnpackList( const BSG_BYTE* src, int ParamCount, ... ) { |
---|
| 740 | int ParamNumber = 0; |
---|
| 741 | BSG_Type format; |
---|
| 742 | BSG_BYTE* val = NULL; |
---|
| 743 | int size = 0; |
---|
| 744 | |
---|
| 745 | va_list paramList; |
---|
| 746 | va_start( paramList, ParamCount ); |
---|
| 747 | |
---|
| 748 | for( ParamNumber = 1; ParamNumber <= ParamCount; ParamNumber++) { |
---|
| 749 | format = (BSG_Type) va_arg( paramList, int ); |
---|
| 750 | val = va_arg( paramList, BSG_BYTE* ); |
---|
| 751 | |
---|
| 752 | size += BSG_Unpack(format, src + size, val); |
---|
| 753 | } |
---|
| 754 | |
---|
| 755 | va_end( paramList ); |
---|
| 756 | |
---|
| 757 | return size; |
---|
| 758 | } |
---|
| 759 | |
---|
| 760 | // Destroy any memory allocated by calls to unpack |
---|
| 761 | void BSG_DestroyList(int ParamCount, ... ) { |
---|
| 762 | int ParamNumber = 0; |
---|
| 763 | BSG_Type argType; |
---|
| 764 | BSG_BYTE* paramValue = NULL; |
---|
| 765 | |
---|
| 766 | va_list paramList; |
---|
| 767 | va_start( paramList, ParamCount ); |
---|
| 768 | |
---|
| 769 | for( ParamNumber = 1; ParamNumber <= ParamCount; ParamNumber++) { |
---|
| 770 | argType = (BSG_Type) va_arg( paramList, int ); |
---|
| 771 | paramValue = va_arg( paramList, BSG_BYTE* ); |
---|
| 772 | |
---|
| 773 | BSG_Destroy(argType, paramValue); |
---|
| 774 | } |
---|
| 775 | |
---|
| 776 | va_end( paramList ); |
---|
| 777 | |
---|
| 778 | return; |
---|
| 779 | } |
---|
| 780 | |
---|
| 781 | |
---|
| 782 | // and a tuple version |
---|
| 783 | TPM_RESULT BSG_DestroyTuple (int numParams, pack_tuple_t params[]) { |
---|
| 784 | int i; |
---|
| 785 | |
---|
| 786 | for (i = 0; i < numParams; i++) |
---|
| 787 | BSG_Destroy (params[i].type, params[i].addr); |
---|
| 788 | |
---|
| 789 | return TPM_SUCCESS; |
---|
| 790 | } |
---|
| 791 | |
---|
| 792 | |
---|
| 793 | // |
---|
| 794 | // wrappers of Pack and PackList which malloc the ouput buffer. to be freed |
---|
| 795 | // by the caller later |
---|
| 796 | // |
---|
| 797 | |
---|
| 798 | int BSG_PackMalloc (BSG_Type type, const void* src, BSG_BYTE** o_dst) { |
---|
| 799 | int size = BSG_Pack (type, src, NULL); |
---|
| 800 | BSG_BYTE * dest = (BSG_BYTE*) malloc (size); |
---|
| 801 | if (dest == NULL) |
---|
| 802 | return -1; |
---|
| 803 | |
---|
| 804 | size = BSG_Pack(type, src, dest); |
---|
| 805 | *o_dst = dest; |
---|
| 806 | return size; |
---|
| 807 | } |
---|
| 808 | |
---|
| 809 | |
---|
| 810 | |
---|
| 811 | int BSG_PackListMalloc(BSG_BYTE** outBuffer, int ParamCount, ... ) { |
---|
| 812 | va_list args; |
---|
| 813 | int size; |
---|
| 814 | |
---|
| 815 | va_start (args, ParamCount); |
---|
| 816 | size = BSG_PackList (NULL, ParamCount, args); |
---|
| 817 | va_end (args); |
---|
| 818 | |
---|
| 819 | BSG_BYTE * dest = (BSG_BYTE*) malloc (size); |
---|
| 820 | if (dest == NULL) |
---|
| 821 | return -1; |
---|
| 822 | |
---|
| 823 | va_start (args, ParamCount); |
---|
| 824 | size = BSG_PackList (dest, ParamCount, args); |
---|
| 825 | va_end (args); |
---|
| 826 | |
---|
| 827 | *outBuffer = dest; |
---|
| 828 | return size; |
---|
| 829 | } |
---|