1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/sbuf.h> 35 36 #ifdef _KERNEL 37 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/systm.h> 41 42 #include <machine/_inttypes.h> 43 44 #else /* !_KERNEL */ 45 46 #include <inttypes.h> 47 #include <errno.h> 48 #include <stdlib.h> 49 #include <string.h> 50 51 #endif /* _KERNEL */ 52 53 #include "bhnd_nvram_private.h" 54 55 #include "bhnd_nvram_valuevar.h" 56 57 58 static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, 59 size_t ilen, bhnd_nvram_type itype, 60 uint32_t flags); 61 static int bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, 62 size_t ilen, bhnd_nvram_type itype, 63 uint32_t flags); 64 static int bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, 65 const void *inp, size_t ilen, bhnd_nvram_type itype); 66 67 #define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \ 68 (bhnd_nvram_val_t) { \ 69 .refs = 1, \ 70 .val_storage = _storage, \ 71 .fmt = _fmt, \ 72 .data_storage = BHND_NVRAM_VAL_DATA_NONE, \ 73 }; 74 75 76 /** Assert that @p value's backing representation state has initialized 77 * as empty. */ 78 #define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \ 79 BHND_NV_ASSERT( \ 80 value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \ 81 value->data_len == 0 && \ 82 value->data.ptr == NULL, \ 83 ("previously initialized value")) 84 85 /* Common initialization support for bhnd_nvram_val_init() and 86 * bhnd_nvram_val_new() */ 87 static int 88 bhnd_nvram_val_init_common(bhnd_nvram_val_t *value, bhnd_nvram_val_storage_t 89 val_storage, const bhnd_nvram_val_fmt_t *fmt, const void *inp, size_t ilen, 90 bhnd_nvram_type itype, uint32_t flags) 91 { 92 void *outp; 93 bhnd_nvram_type otype; 94 size_t olen; 95 int error; 96 97 /* Determine expected data type, and allow the format to delegate to 98 * a new format instance */ 99 if (fmt != NULL && fmt->op_filter != NULL) { 100 const bhnd_nvram_val_fmt_t *nfmt = fmt; 101 102 /* Use the filter function to determine whether direct 103 * initialization from is itype permitted */ 104 error = fmt->op_filter(&nfmt, inp, ilen, itype); 105 if (error) 106 return (error); 107 108 /* Retry initialization with new format? */ 109 if (nfmt != fmt) { 110 return (bhnd_nvram_val_init_common(value, val_storage, 111 nfmt, inp, ilen, itype, flags)); 112 } 113 114 /* Value can be initialized with provided input type */ 115 otype = itype; 116 117 } else if (fmt != NULL) { 118 /* Value must be initialized with the format's native 119 * type */ 120 otype = fmt->native_type; 121 122 } else { 123 /* No format specified; we can initialize directly from the 124 * input data, and we'll handle all format operations 125 * internally. */ 126 otype = itype; 127 } 128 129 /* Initialize value instance */ 130 *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); 131 132 /* If input data already in native format, init directly. */ 133 if (otype == itype) { 134 error = bhnd_nvram_val_set(value, inp, ilen, itype, flags); 135 if (error) 136 return (error); 137 138 return (0); 139 } 140 141 /* Determine size when encoded in native format */ 142 error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype); 143 if (error) 144 return (error); 145 146 /* Fetch reference to (or allocate) an appropriately sized buffer */ 147 outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); 148 if (outp == NULL) 149 return (ENOMEM); 150 151 /* Perform encode */ 152 error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype); 153 if (error) 154 return (error); 155 156 return (0); 157 } 158 159 /** 160 * Initialize an externally allocated instance of @p value with @p fmt from the 161 * given @p inp buffer of @p itype and @p ilen. 162 * 163 * On success, the caller owns a reference to @p value, and is responsible for 164 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 165 * 166 * @param value The externally allocated value instance to be 167 * initialized. 168 * @param fmt The value's format, or NULL to use the default format 169 * for @p itype. 170 * @param inp Input buffer. 171 * @param ilen Input buffer length. 172 * @param itype Input buffer type. 173 * @param flags Value flags (see BHND_NVRAM_VAL_*). 174 * 175 * @retval 0 success 176 * @retval ENOMEM If allocation fails. 177 * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. 178 * @retval EFAULT if @p ilen is not correctly aligned for elements of 179 * @p itype. 180 * @retval ERANGE If value coercion would overflow (or underflow) the 181 * @p fmt representation. 182 */ 183 int 184 bhnd_nvram_val_init(bhnd_nvram_val_t *value, const bhnd_nvram_val_fmt_t *fmt, 185 const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) 186 { 187 int error; 188 189 error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO, 190 fmt, inp, ilen, itype, flags); 191 if (error) 192 bhnd_nvram_val_release(value); 193 194 return (error); 195 } 196 197 /** 198 * Allocate a value instance with @p fmt, and attempt to initialize its internal 199 * representation from the given @p inp buffer of @p itype and @p ilen. 200 * 201 * On success, the caller owns a reference to @p value, and is responsible for 202 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 203 * 204 * @param[out] value On success, the allocated value instance. 205 * @param fmt The value's format, or NULL to use the default format 206 * for @p itype. 207 * @param inp Input buffer. 208 * @param ilen Input buffer length. 209 * @param itype Input buffer type. 210 * @param flags Value flags (see BHND_NVRAM_VAL_*). 211 * 212 * @retval 0 success 213 * @retval ENOMEM If allocation fails. 214 * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. 215 * @retval EFAULT if @p ilen is not correctly aligned for elements of 216 * @p itype. 217 * @retval ERANGE If value coercion would overflow (or underflow) the 218 * @p fmt representation. 219 */ 220 int 221 bhnd_nvram_val_new(bhnd_nvram_val_t **value, const bhnd_nvram_val_fmt_t *fmt, 222 const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) 223 { 224 int error; 225 226 /* Allocate new instance */ 227 if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) 228 return (ENOMEM); 229 230 /* Perform common initialization. */ 231 error = bhnd_nvram_val_init_common(*value, 232 BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags); 233 if (error) { 234 /* Will also free() the value allocation */ 235 bhnd_nvram_val_release(*value); 236 } 237 238 return (error); 239 } 240 241 /** 242 * Copy or retain a reference to @p value. 243 * 244 * On success, the caller is responsible for freeing the result via 245 * bhnd_nvram_val_release(). 246 * 247 * @param value The value to be copied (or retained). 248 * 249 * @retval bhnd_nvram_val_t if @p value was successfully copied or retained. 250 * @retval NULL if allocation failed. 251 */ 252 bhnd_nvram_val_t * 253 bhnd_nvram_val_copy(bhnd_nvram_val_t *value) 254 { 255 bhnd_nvram_val_t *result; 256 const void *bytes; 257 bhnd_nvram_type type; 258 size_t len; 259 uint32_t flags; 260 int error; 261 262 /* If dynamically allocated, simply bump the reference count */ 263 if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) { 264 refcount_acquire(&value->refs); 265 return (value); 266 } 267 268 /* Otherwise, we need to perform an actual copy */ 269 BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has " 270 "active refcount (%u)", value->refs)); 271 272 /* Compute the new value's flags based on the source value */ 273 switch (value->data_storage) { 274 case BHND_NVRAM_VAL_DATA_NONE: 275 case BHND_NVRAM_VAL_DATA_INLINE: 276 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 277 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 278 /* Copy the source data and permit additional allocation if the 279 * value cannot be represented inline */ 280 flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC; 281 break; 282 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 283 flags = BHND_NVRAM_VAL_STATIC_DATA; 284 break; 285 default: 286 BHND_NV_PANIC("invalid storage type: %d", value->data_storage); 287 } 288 289 /* Allocate new value copy */ 290 bytes = bhnd_nvram_val_bytes(value, &len, &type); 291 error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type, 292 flags); 293 if (error) { 294 BHND_NV_LOG("copy failed: %d", error); 295 return (NULL); 296 } 297 298 return (result); 299 } 300 301 /** 302 * Release a reference to @p value. 303 * 304 * If this is the last reference, all associated resources will be freed. 305 * 306 * @param value The value to be released. 307 */ 308 void 309 bhnd_nvram_val_release(bhnd_nvram_val_t *value) 310 { 311 BHND_NV_ASSERT(value->refs >= 1, ("value over-released")); 312 313 /* Drop reference */ 314 if (!refcount_release(&value->refs)) 315 return; 316 317 /* Free allocated external representation data */ 318 if (value->data_storage == BHND_NVRAM_VAL_DATA_EXT_ALLOC) 319 bhnd_nv_free(__DECONST(void *, value->data.ptr)); 320 321 /* Free instance if dynamically allocated */ 322 if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) 323 bhnd_nv_free(value); 324 } 325 326 /** 327 * Standard string/char array/char encoding implementation. 328 * 329 * Input type must be one of: 330 * - BHND_NVRAM_TYPE_STRING 331 * - BHND_NVRAM_TYPE_CHAR 332 * - BHND_NVRAM_TYPE_CHAR_ARRAY 333 */ 334 static int 335 bhnd_nvram_val_encode_string(void *outp, size_t *olen, bhnd_nvram_type otype, 336 const void *inp, size_t ilen, bhnd_nvram_type itype) 337 { 338 const char *cstr; 339 bhnd_nvram_type otype_base; 340 size_t cstr_size, cstr_len; 341 size_t limit, nbytes; 342 343 BHND_NV_ASSERT( 344 itype == BHND_NVRAM_TYPE_STRING || 345 itype == BHND_NVRAM_TYPE_CHAR || 346 itype == BHND_NVRAM_TYPE_CHAR_ARRAY, 347 ("unsupported type: %d", itype)); 348 349 cstr = inp; 350 cstr_size = ilen; 351 nbytes = 0; 352 otype_base = bhnd_nvram_base_type(otype); 353 354 /* Determine output byte limit */ 355 if (outp != NULL) 356 limit = *olen; 357 else 358 limit = 0; 359 360 /* Determine string length, minus trailing NUL (if any) */ 361 cstr_len = strnlen(cstr, cstr_size); 362 363 /* Parse the field data */ 364 switch (otype) { 365 case BHND_NVRAM_TYPE_CHAR: 366 case BHND_NVRAM_TYPE_CHAR_ARRAY: 367 /* String must contain exactly 1 non-terminating-NUL character 368 * to be represented as a single char */ 369 if (!bhnd_nvram_is_array_type(otype)) { 370 if (cstr_len != 1) 371 return (EFTYPE); 372 } 373 374 /* Copy out the characters directly (excluding trailing NUL) */ 375 for (size_t i = 0; i < cstr_len; i++) { 376 if (limit > nbytes) 377 *((uint8_t *)outp + nbytes) = cstr[i]; 378 nbytes++; 379 } 380 381 /* Provide required length */ 382 *olen = nbytes; 383 if (limit < *olen && outp != NULL) 384 return (ENOMEM); 385 386 return (0); 387 388 case BHND_NVRAM_TYPE_UINT8: 389 case BHND_NVRAM_TYPE_UINT8_ARRAY: 390 case BHND_NVRAM_TYPE_UINT16: 391 case BHND_NVRAM_TYPE_UINT16_ARRAY: 392 case BHND_NVRAM_TYPE_UINT32: 393 case BHND_NVRAM_TYPE_UINT32_ARRAY: 394 case BHND_NVRAM_TYPE_UINT64: 395 case BHND_NVRAM_TYPE_UINT64_ARRAY: 396 case BHND_NVRAM_TYPE_INT8: 397 case BHND_NVRAM_TYPE_INT8_ARRAY: 398 case BHND_NVRAM_TYPE_INT16: 399 case BHND_NVRAM_TYPE_INT16_ARRAY: 400 case BHND_NVRAM_TYPE_INT32: 401 case BHND_NVRAM_TYPE_INT32_ARRAY: 402 case BHND_NVRAM_TYPE_INT64: 403 case BHND_NVRAM_TYPE_INT64_ARRAY: { 404 const char *p; 405 size_t plen, parsed_len; 406 int error; 407 408 /* Trim leading/trailing whitespace */ 409 p = cstr; 410 plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); 411 412 /* Try to parse the integer value */ 413 error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp, 414 olen, otype_base); 415 if (error) { 416 BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n", 417 BHND_NV_PRINT_WIDTH(plen), p, error); 418 return (error); 419 } 420 421 /* Do additional bytes remain unparsed? */ 422 if (plen != parsed_len) { 423 BHND_NV_DEBUG("error parsing '%.*s' as a single " 424 "integer value; trailing garbage '%.*s'\n", 425 BHND_NV_PRINT_WIDTH(plen), p, 426 BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len); 427 return (EFTYPE); 428 } 429 430 return (0); 431 } 432 433 case BHND_NVRAM_TYPE_STRING: 434 case BHND_NVRAM_TYPE_STRING_ARRAY: 435 /* Copy out the string representation as-is */ 436 *olen = cstr_size; 437 438 /* Need additional space for trailing NUL? */ 439 if (cstr_len == cstr_size) 440 (*olen)++; 441 442 /* Skip output? */ 443 if (outp == NULL) 444 return (0); 445 446 /* Verify required length */ 447 if (limit < *olen) 448 return (ENOMEM); 449 450 /* Copy and NUL terminate */ 451 strncpy(outp, cstr, cstr_len); 452 *((char *)outp + cstr_len) = '\0'; 453 454 return (0); 455 } 456 457 BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype)); 458 } 459 460 /** 461 * Standard integer encoding implementation. 462 */ 463 static int 464 bhnd_nvram_val_encode_int(void *outp, size_t *olen, bhnd_nvram_type otype, 465 const void *inp, size_t ilen, bhnd_nvram_type itype) 466 { 467 bhnd_nvram_type otype_base; 468 size_t limit, nbytes; 469 bool itype_signed, otype_signed, otype_int; 470 union { 471 uint64_t u64; 472 int64_t i64; 473 } intv; 474 475 BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type")); 476 477 /* Determine output byte limit */ 478 if (outp != NULL) 479 limit = *olen; 480 else 481 limit = 0; 482 483 /* Fetch output type info */ 484 otype_base = bhnd_nvram_base_type(otype); 485 otype_int = bhnd_nvram_is_int_type(otype); 486 otype_signed = bhnd_nvram_is_signed_type(otype_base); 487 488 /* 489 * Promote integer value to a common 64-bit representation. 490 */ 491 switch (itype) { 492 case BHND_NVRAM_TYPE_UINT8: 493 if (ilen != sizeof(uint8_t)) 494 return (EFAULT); 495 496 itype_signed = false; 497 intv.u64 = *(const uint8_t *)inp; 498 break; 499 500 case BHND_NVRAM_TYPE_UINT16: 501 if (ilen != sizeof(uint16_t)) 502 return (EFAULT); 503 504 itype_signed = false; 505 intv.u64 = *(const uint16_t *)inp; 506 break; 507 508 case BHND_NVRAM_TYPE_UINT32: 509 if (ilen != sizeof(uint32_t)) 510 return (EFAULT); 511 512 itype_signed = false; 513 intv.u64 = *(const uint32_t *)inp; 514 break; 515 516 case BHND_NVRAM_TYPE_UINT64: 517 if (ilen != sizeof(uint64_t)) 518 return (EFAULT); 519 520 itype_signed = false; 521 intv.u64 = *(const uint64_t *)inp; 522 break; 523 524 case BHND_NVRAM_TYPE_INT8: 525 if (ilen != sizeof(int8_t)) 526 return (EFAULT); 527 528 itype_signed = true; 529 intv.i64 = *(const int8_t *)inp; 530 break; 531 532 case BHND_NVRAM_TYPE_INT16: 533 if (ilen != sizeof(int16_t)) 534 return (EFAULT); 535 536 itype_signed = true; 537 intv.i64 = *(const int16_t *)inp; 538 break; 539 540 case BHND_NVRAM_TYPE_INT32: 541 if (ilen != sizeof(int32_t)) 542 return (EFAULT); 543 544 itype_signed = true; 545 intv.i64 = *(const int32_t *)inp; 546 break; 547 548 case BHND_NVRAM_TYPE_INT64: 549 if (ilen != sizeof(int32_t)) 550 return (EFAULT); 551 552 itype_signed = true; 553 intv.i64 = *(const int32_t *)inp; 554 break; 555 556 default: 557 BHND_NV_PANIC("invalid type %d\n", itype); 558 } 559 560 /* Perform signed/unsigned conversion */ 561 if (itype_signed && otype_int && !otype_signed) { 562 if (intv.i64 < 0) { 563 /* Can't represent negative value */ 564 BHND_NV_LOG("cannot represent %" PRId64 " as %s\n", 565 intv.i64, bhnd_nvram_type_name(otype)); 566 567 return (ERANGE); 568 } 569 570 /* Convert to unsigned representation */ 571 intv.u64 = intv.i64; 572 573 } else if (!itype_signed && otype_int && otype_signed) { 574 /* Handle unsigned -> signed coercions */ 575 if (intv.u64 > INT64_MAX) { 576 /* Can't represent positive value */ 577 BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n", 578 intv.u64, bhnd_nvram_type_name(otype)); 579 return (ERANGE); 580 } 581 582 /* Convert to signed representation */ 583 intv.i64 = intv.u64; 584 } 585 586 /* Write output */ 587 switch (otype) { 588 case BHND_NVRAM_TYPE_CHAR: 589 case BHND_NVRAM_TYPE_CHAR_ARRAY: 590 case BHND_NVRAM_TYPE_UINT8: 591 case BHND_NVRAM_TYPE_UINT8_ARRAY: 592 if (intv.u64 > UINT8_MAX) 593 return (ERANGE); 594 595 nbytes = sizeof(uint8_t); 596 if (limit >= nbytes) 597 *((uint8_t *)outp) = (uint8_t)intv.u64; 598 break; 599 600 case BHND_NVRAM_TYPE_UINT16: 601 case BHND_NVRAM_TYPE_UINT16_ARRAY: 602 if (intv.u64 > UINT16_MAX) 603 return (ERANGE); 604 605 nbytes = sizeof(uint16_t); 606 if (limit >= nbytes) 607 *((uint16_t *)outp) = (uint16_t)intv.u64; 608 break; 609 610 case BHND_NVRAM_TYPE_UINT32: 611 case BHND_NVRAM_TYPE_UINT32_ARRAY: 612 if (intv.u64 > UINT32_MAX) 613 return (ERANGE); 614 615 nbytes = sizeof(uint32_t); 616 if (limit >= nbytes) 617 *((uint32_t *)outp) = (uint32_t)intv.u64; 618 break; 619 620 case BHND_NVRAM_TYPE_UINT64: 621 case BHND_NVRAM_TYPE_UINT64_ARRAY: 622 nbytes = sizeof(uint64_t); 623 if (limit >= nbytes) 624 *((uint64_t *)outp) = intv.u64; 625 break; 626 627 case BHND_NVRAM_TYPE_INT8: 628 case BHND_NVRAM_TYPE_INT8_ARRAY: 629 if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX) 630 return (ERANGE); 631 632 nbytes = sizeof(int8_t); 633 if (limit >= nbytes) 634 *((int8_t *)outp) = (int8_t)intv.i64; 635 break; 636 637 case BHND_NVRAM_TYPE_INT16: 638 case BHND_NVRAM_TYPE_INT16_ARRAY: 639 if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX) 640 return (ERANGE); 641 642 nbytes = sizeof(int16_t); 643 if (limit >= nbytes) 644 *((int16_t *)outp) = (int16_t)intv.i64; 645 break; 646 647 case BHND_NVRAM_TYPE_INT32: 648 case BHND_NVRAM_TYPE_INT32_ARRAY: 649 if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX) 650 return (ERANGE); 651 652 nbytes = sizeof(int32_t); 653 if (limit >= nbytes) 654 *((int32_t *)outp) = (int32_t)intv.i64; 655 break; 656 657 case BHND_NVRAM_TYPE_INT64: 658 case BHND_NVRAM_TYPE_INT64_ARRAY: 659 nbytes = sizeof(int64_t); 660 if (limit >= nbytes) 661 *((int64_t *)outp) = intv.i64; 662 break; 663 664 case BHND_NVRAM_TYPE_STRING: 665 case BHND_NVRAM_TYPE_STRING_ARRAY: { 666 ssize_t len; 667 668 /* Attempt to write the entry + NUL */ 669 if (otype_signed) { 670 len = snprintf(outp, limit, "%" PRId64, intv.i64); 671 } else { 672 len = snprintf(outp, limit, "%" PRIu64, intv.u64); 673 } 674 675 if (len < 0) { 676 BHND_NV_LOG("snprintf() failed: %zd\n", len); 677 return (EFTYPE); 678 } 679 680 /* Set total length to the formatted string length, plus 681 * trailing NUL */ 682 nbytes = len + 1; 683 break; 684 } 685 686 default: 687 BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype)); 688 return (EFTYPE); 689 } 690 691 /* Provide required length */ 692 *olen = nbytes; 693 if (limit < *olen) { 694 if (outp == NULL) 695 return (0); 696 697 return (ENOMEM); 698 } 699 700 return (0); 701 } 702 703 /** 704 * Encode the given @p value as @p otype, writing the result to @p outp. 705 * 706 * @param value The value to be encoded. 707 * @param[out] outp On success, the value will be written to this 708 * buffer. This argment may be NULL if the value is 709 * not desired. 710 * @param[in,out] olen The capacity of @p outp. On success, will be set 711 * to the actual size of the requested value. 712 * @param otype The data type to be written to @p outp. 713 * 714 * @retval 0 success 715 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 716 * is too small to hold the encoded value. 717 * @retval EFTYPE If value coercion from @p value to @p otype is 718 * impossible. 719 * @retval ERANGE If value coercion would overflow (or underflow) the 720 * a @p otype representation. 721 */ 722 int 723 bhnd_nvram_val_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen, 724 bhnd_nvram_type otype) 725 { 726 /* Prefer format implementation */ 727 if (value->fmt != NULL && value->fmt->op_encode != NULL) 728 return (value->fmt->op_encode(value, outp, olen, otype)); 729 730 return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); 731 } 732 733 /** 734 * Encode the given @p value's element as @p otype, writing the result to 735 * @p outp. 736 * 737 * @param inp The element to be be encoded. Must be a value 738 * previously returned by bhnd_nvram_val_next() 739 * or bhnd_nvram_val_elem(). 740 * @param ilen The size of @p inp, as returned by 741 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). 742 * @param[out] outp On success, the value will be written to this 743 * buffer. This argment may be NULL if the value is 744 * not desired. 745 * @param[in,out] olen The capacity of @p outp. On success, will be set 746 * to the actual size of the requested value. 747 * @param otype The data type to be written to @p outp. 748 * 749 * @retval 0 success 750 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 751 * is too small to hold the encoded value. 752 * @retval EFTYPE If value coercion from @p value to @p otype is 753 * impossible. 754 * @retval ERANGE If value coercion would overflow (or underflow) the 755 * a @p otype representation. 756 */ 757 int 758 bhnd_nvram_val_encode_elem(bhnd_nvram_val_t *value, const void *inp, 759 size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) 760 { 761 /* Prefer format implementation */ 762 if (value->fmt != NULL && value->fmt->op_encode_elem != NULL) { 763 return (value->fmt->op_encode_elem(value, inp, ilen, outp, 764 olen, otype)); 765 } 766 767 return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, 768 olen, otype)); 769 } 770 771 /** 772 * Return the type, size, and a pointer to the internal representation 773 * of @p value. 774 * 775 * @param value The value to be queried. 776 * @param[out] olen Size of the returned data, in bytes. 777 * @param[out] otype Data type. 778 */ 779 const void * 780 bhnd_nvram_val_bytes(bhnd_nvram_val_t *value, size_t *olen, 781 bhnd_nvram_type *otype) 782 { 783 /* Provide type and length */ 784 *otype = value->data_type; 785 *olen = value->data_len; 786 787 switch (value->data_storage) { 788 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 789 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 790 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 791 /* Return a pointer to external storage */ 792 return (value->data.ptr); 793 794 case BHND_NVRAM_VAL_DATA_INLINE: 795 /* Return a pointer to inline storage */ 796 return (&value->data); 797 798 case BHND_NVRAM_VAL_DATA_NONE: 799 BHND_NV_PANIC("uninitialized value"); 800 } 801 802 BHND_NV_PANIC("unknown storage type: %d", value->data_storage); 803 } 804 805 /** 806 * Iterate over all array elements in @p value. 807 * 808 * @param value The value to be iterated 809 * @param prev A value pointer previously returned by 810 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(), 811 * or NULL to begin iteration at the first element. 812 * @param[in,out] len If prev is non-NULL, len must be a pointer 813 * to the length previously returned by 814 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). 815 * On success, will be set to the next element's 816 * length, in bytes. 817 * 818 * @retval non-NULL A borrowed reference to the element data. 819 * @retval NULL If the end of the element array is reached. 820 */ 821 const void * 822 bhnd_nvram_val_next(bhnd_nvram_val_t *value, const void *prev, size_t *len) 823 { 824 /* Prefer the format implementation */ 825 if (value->fmt != NULL && value->fmt->op_next != NULL) 826 return (value->fmt->op_next(value, prev, len)); 827 828 return (bhnd_nvram_val_generic_next(value, prev, len)); 829 } 830 831 /** 832 * Return value's element data type. 833 * 834 * @param value The value to be queried. 835 */ 836 bhnd_nvram_type 837 bhnd_nvram_val_elem_type(bhnd_nvram_val_t *value) 838 { 839 return (bhnd_nvram_base_type(value->data_type)); 840 } 841 842 /** 843 * Return the total number of elements represented by @p value. 844 */ 845 size_t 846 bhnd_nvram_val_nelem(bhnd_nvram_val_t *value) 847 { 848 const void *bytes; 849 bhnd_nvram_type type; 850 size_t nelem, len; 851 int error; 852 853 /* Prefer format implementation */ 854 if (value->fmt != NULL && value->fmt->op_nelem != NULL) 855 return (value->fmt->op_nelem(value)); 856 857 /* 858 * If a custom op_next() is defined, bhnd_nvram_value_nelem() almost 859 * certainly cannot produce a valid element count; it assumes a standard 860 * data format that may not apply when custom iteration is required. 861 * 862 * Instead, use bhnd_nvram_val_next() to parse the backing data and 863 * produce a total count. 864 */ 865 if (value->fmt != NULL && value->fmt->op_next != NULL) { 866 const void *next; 867 868 next = NULL; 869 nelem = 0; 870 while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL) 871 nelem++; 872 873 return (nelem); 874 } 875 876 /* Otherwise, compute the standard element count */ 877 bytes = bhnd_nvram_val_bytes(value, &len, &type); 878 if ((error = bhnd_nvram_value_nelem(type, bytes, len, &nelem))) { 879 /* Should always succeed */ 880 BHND_NV_PANIC("error calculating element count for type '%s' " 881 "with length %zu: %d\n", bhnd_nvram_type_name(type), len, 882 error); 883 } 884 885 return (nelem); 886 } 887 888 /** 889 * Generic implementation of bhnd_nvram_val_op_encode(), compatible with 890 * all supported NVRAM data types. 891 */ 892 int 893 bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen, 894 bhnd_nvram_type otype) 895 { 896 const void *inp; 897 bhnd_nvram_type itype; 898 size_t ilen; 899 const void *next; 900 bhnd_nvram_type otype_base; 901 size_t limit, nelem, nbytes; 902 size_t next_len; 903 int error; 904 905 nbytes = 0; 906 nelem = 0; 907 otype_base = bhnd_nvram_base_type(otype); 908 909 /* 910 * Normally, a rank polymorphic type like a character array would not 911 * be representable as a rank 1 type. 912 * 913 * As a special-cased exception, we can support conversion directly 914 * from CHAR_ARRAY to STRING by treating the character array as a 915 * non-NUL-terminated string. 916 * 917 * This conversion is isomorphic; we also support conversion directly 918 * from a STRING to a CHAR_ARRAY by the same mechanism. 919 */ 920 inp = bhnd_nvram_val_bytes(value, &ilen, &itype); 921 if ((itype == BHND_NVRAM_TYPE_CHAR_ARRAY && 922 otype == BHND_NVRAM_TYPE_STRING) || 923 (itype == BHND_NVRAM_TYPE_STRING && 924 otype == BHND_NVRAM_TYPE_CHAR_ARRAY)) 925 { 926 return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, 927 otype)); 928 } 929 930 /* 931 * If both input and output are non-array types, try to encode them 932 * without performing element iteration. 933 */ 934 if (!bhnd_nvram_is_array_type(itype) && 935 !bhnd_nvram_is_array_type(otype)) 936 { 937 return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, 938 otype)); 939 } 940 941 /* Determine output byte limit */ 942 if (outp != NULL) 943 limit = *olen; 944 else 945 limit = 0; 946 947 /* Iterate over our array elements and encode as the requested 948 * type */ 949 next = NULL; 950 while ((next = bhnd_nvram_val_next(value, next, &next_len))) { 951 void *elem_outp; 952 size_t elem_nbytes; 953 954 /* If the output type is not an array type, we can only encode 955 * one element */ 956 nelem++; 957 if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) { 958 return (EFTYPE); 959 } 960 961 /* Determine output offset / limit */ 962 if (nbytes >= limit) { 963 elem_nbytes = 0; 964 elem_outp = NULL; 965 } else { 966 elem_nbytes = limit - nbytes; 967 elem_outp = (uint8_t *)outp + nbytes; 968 } 969 970 /* Attempt encode */ 971 error = bhnd_nvram_val_encode_elem(value, next, next_len, 972 elem_outp, &elem_nbytes, otype_base); 973 974 /* If encoding failed for any reason other than ENOMEM (which 975 * we'll detect and report below), return immediately */ 976 if (error && error != ENOMEM) 977 return (error); 978 979 /* Add to total length */ 980 if (SIZE_MAX - nbytes < elem_nbytes) 981 return (EFTYPE); /* would overflow size_t */ 982 983 nbytes += elem_nbytes; 984 } 985 986 /* Provide the actual length */ 987 *olen = nbytes; 988 989 /* If no output was requested, nothing left to do */ 990 if (outp == NULL) 991 return (0); 992 993 /* Otherwise, report a memory error if the output buffer was too 994 * small */ 995 if (limit < nbytes) 996 return (ENOMEM); 997 998 return (0); 999 } 1000 1001 /** 1002 * Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with 1003 * all supported NVRAM data types. 1004 */ 1005 int 1006 bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value, const void *inp, 1007 size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) 1008 { 1009 bhnd_nvram_type itype; 1010 1011 itype = bhnd_nvram_val_elem_type(value); 1012 switch (itype) { 1013 case BHND_NVRAM_TYPE_STRING: 1014 case BHND_NVRAM_TYPE_CHAR: 1015 case BHND_NVRAM_TYPE_CHAR_ARRAY: 1016 return (bhnd_nvram_val_encode_string(outp, olen, otype, inp, 1017 ilen, itype)); 1018 1019 case BHND_NVRAM_TYPE_UINT8: 1020 case BHND_NVRAM_TYPE_UINT16: 1021 case BHND_NVRAM_TYPE_UINT32: 1022 case BHND_NVRAM_TYPE_UINT64: 1023 case BHND_NVRAM_TYPE_INT8: 1024 case BHND_NVRAM_TYPE_INT16: 1025 case BHND_NVRAM_TYPE_INT32: 1026 case BHND_NVRAM_TYPE_INT64: 1027 return (bhnd_nvram_val_encode_int(outp, olen, otype, inp, ilen, 1028 itype)); 1029 1030 default: 1031 BHND_NV_PANIC("missing encode_elem() implementation"); 1032 } 1033 } 1034 1035 /** 1036 * Generic implementation of bhnd_nvram_val_op_next(), compatible with 1037 * all supported NVRAM data types. 1038 */ 1039 const void * 1040 bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value, const void *prev, 1041 size_t *len) 1042 { 1043 const uint8_t *inp; 1044 const uint8_t *next; 1045 bhnd_nvram_type itype; 1046 size_t ilen; 1047 size_t offset; 1048 1049 /* Otherwise, default to iterating over the backing representation 1050 * according to its native representation */ 1051 inp = bhnd_nvram_val_bytes(value, &ilen, &itype); 1052 1053 /* First element */ 1054 if (prev == NULL) { 1055 /* Zero-length array? */ 1056 if (ilen == 0) 1057 return (NULL); 1058 1059 *len = bhnd_nvram_value_size(itype, inp, ilen, 1); 1060 return (inp); 1061 } 1062 1063 /* Advance to next element */ 1064 BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); 1065 next = (const uint8_t *)prev + *len; 1066 offset = (size_t)(next - inp); 1067 1068 if (offset >= ilen) { 1069 /* Hit end of the array */ 1070 return (NULL); 1071 } 1072 1073 /* Determine element size */ 1074 *len = bhnd_nvram_value_size(itype, next, ilen - offset, 1); 1075 if (ilen - offset < *len) 1076 BHND_NV_PANIC("short element -- misaligned representation"); 1077 1078 return (next); 1079 } 1080 1081 /** 1082 * Initialize the representation of @p value with @p ptr. 1083 * 1084 * If @p value is an externally allocated instance and the representation 1085 * cannot be represented inline, the given data will not be copied, and @p ptr 1086 * must remain valid for the lifetime of @p value. 1087 * 1088 * Otherwise, @p value will be initialized with a copy of the @p ptr. 1089 * 1090 * @param value The value to be initialized. 1091 * @param inp The external representation. 1092 * @param ilen The external representation length, in bytes. 1093 * @param itype The external representation's data type. 1094 * @param flags Value flags. 1095 * 1096 * @retval 0 success. 1097 * @retval ENOMEM if allocation fails 1098 * @retval EFTYPE if @p itype is not an array type, and @p ilen is not 1099 * equal to the size of a single element of @p itype. 1100 * @retval EFAULT if @p ilen is not correctly aligned for elements of 1101 * @p itype. 1102 */ 1103 static int 1104 bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, size_t ilen, 1105 bhnd_nvram_type itype, uint32_t flags) 1106 { 1107 void *bytes; 1108 1109 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1110 1111 /* Reference the external data */ 1112 if ((flags & BHND_NVRAM_VAL_BORROW_DATA) || 1113 (flags & BHND_NVRAM_VAL_STATIC_DATA)) 1114 { 1115 if (flags & BHND_NVRAM_VAL_BORROW_DATA) 1116 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK; 1117 else 1118 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC; 1119 1120 value->data.ptr = inp; 1121 value->data_type = itype; 1122 value->data_len = ilen; 1123 return (0); 1124 } 1125 1126 /* Fetch reference to (or allocate) an appropriately sized buffer */ 1127 bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags); 1128 if (bytes == NULL) 1129 return (ENOMEM); 1130 1131 /* Copy data */ 1132 memcpy(bytes, inp, ilen); 1133 1134 return (0); 1135 } 1136 1137 /** 1138 * Initialize the internal inline representation of @p value with a copy of 1139 * the data referenced by @p inp of @p itype. 1140 * 1141 * If @p inp is NULL, @p itype and @p ilen will be validated, but no data will 1142 * be copied. 1143 * 1144 * @param value The value to be initialized. 1145 * @param inp The input data to be copied, or NULL to verify 1146 * that data of @p ilen and @p itype can be represented 1147 * inline. 1148 * @param ilen The size of the external buffer to be allocated. 1149 * @param itype The type of the external buffer to be allocated. 1150 * 1151 * @retval 0 success 1152 * @retval ENOMEM if @p ilen is too large to be represented inline. 1153 * @retval EFAULT if @p ilen is not correctly aligned for elements of 1154 * @p itype. 1155 */ 1156 static int 1157 bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, const void *inp, size_t ilen, 1158 bhnd_nvram_type itype) 1159 { 1160 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1161 1162 #define NV_STORE_INIT_INLINE() do { \ 1163 value->data_len = ilen; \ 1164 } while(0) 1165 1166 #define NV_STORE_INLINE(_type, _dest) do { \ 1167 if (ilen != sizeof(_type)) \ 1168 return (EFAULT); \ 1169 \ 1170 if (inp != NULL) { \ 1171 value->data._dest[0] = *(const _type *)inp; \ 1172 NV_STORE_INIT_INLINE(); \ 1173 } \ 1174 } while (0) 1175 1176 #define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \ 1177 if (ilen % sizeof(_type) != 0) \ 1178 return (EFAULT); \ 1179 \ 1180 if (ilen > nitems(value->data. _dest)) \ 1181 return (ENOMEM); \ 1182 \ 1183 if (inp == NULL) \ 1184 return (0); \ 1185 \ 1186 memcpy(&value->data._dest, inp, ilen); \ 1187 if (inp != NULL) { \ 1188 memcpy(&value->data._dest, inp, ilen); \ 1189 NV_STORE_INIT_INLINE(); \ 1190 } \ 1191 } while (0) 1192 1193 /* Attempt to copy to inline storage */ 1194 switch (itype) { 1195 case BHND_NVRAM_TYPE_CHAR: 1196 NV_STORE_INLINE(uint8_t, ch); 1197 return (0); 1198 1199 case BHND_NVRAM_TYPE_UINT8: 1200 case BHND_NVRAM_TYPE_INT8: 1201 NV_STORE_INLINE(uint8_t, u8); 1202 return (0); 1203 1204 case BHND_NVRAM_TYPE_UINT16: 1205 case BHND_NVRAM_TYPE_INT16: 1206 NV_STORE_INLINE(uint16_t, u16); 1207 return (0); 1208 1209 case BHND_NVRAM_TYPE_UINT32: 1210 case BHND_NVRAM_TYPE_INT32: 1211 NV_STORE_INLINE(uint32_t, u32); 1212 return (0); 1213 1214 case BHND_NVRAM_TYPE_UINT64: 1215 case BHND_NVRAM_TYPE_INT64: 1216 NV_STORE_INLINE(uint32_t, u32); 1217 return (0); 1218 1219 case BHND_NVRAM_TYPE_CHAR_ARRAY: 1220 NV_COPY_ARRRAY_INLINE(uint8_t, ch); 1221 return (0); 1222 1223 case BHND_NVRAM_TYPE_UINT8_ARRAY: 1224 case BHND_NVRAM_TYPE_INT8_ARRAY: 1225 NV_COPY_ARRRAY_INLINE(uint8_t, u8); 1226 return (0); 1227 1228 case BHND_NVRAM_TYPE_UINT16_ARRAY: 1229 case BHND_NVRAM_TYPE_INT16_ARRAY: 1230 NV_COPY_ARRRAY_INLINE(uint16_t, u16); 1231 return (0); 1232 1233 case BHND_NVRAM_TYPE_UINT32_ARRAY: 1234 case BHND_NVRAM_TYPE_INT32_ARRAY: 1235 NV_COPY_ARRRAY_INLINE(uint32_t, u32); 1236 return (0); 1237 1238 case BHND_NVRAM_TYPE_UINT64_ARRAY: 1239 case BHND_NVRAM_TYPE_INT64_ARRAY: 1240 NV_COPY_ARRRAY_INLINE(uint64_t, u64); 1241 return (0); 1242 1243 case BHND_NVRAM_TYPE_STRING: 1244 case BHND_NVRAM_TYPE_STRING_ARRAY: 1245 if (ilen > sizeof(value->data.ch)) 1246 return (ENOMEM); 1247 1248 if (inp != NULL) { 1249 memcpy(&value->data.ch, inp, ilen); 1250 NV_STORE_INIT_INLINE(); 1251 } 1252 1253 return (0); 1254 } 1255 1256 #undef NV_STORE_INIT_INLINE 1257 #undef NV_STORE_INLINE 1258 #undef NV_COPY_ARRRAY_INLINE 1259 1260 BHND_NV_PANIC("unknown data type %d", itype); 1261 } 1262 1263 /** 1264 * Initialize the internal representation of @p value with a buffer allocation 1265 * of @p len and @p itype, returning a pointer to the allocated buffer. 1266 * 1267 * If a buffer of @p len and @p itype can be represented inline, no 1268 * external buffer will be allocated, and instead a pointer to the inline 1269 * data representation will be returned. 1270 * 1271 * @param value The value to be initialized. 1272 * @param ilen The size of the external buffer to be allocated. 1273 * @param itype The type of the external buffer to be allocated. 1274 * @param flags Value flags. 1275 * 1276 * @retval non-null The newly allocated buffer. 1277 * @retval NULL If allocation failed. 1278 * @retval NULL If @p value is an externally allocated instance. 1279 */ 1280 static void * 1281 bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, size_t ilen, 1282 bhnd_nvram_type itype, uint32_t flags) 1283 { 1284 void *ptr; 1285 1286 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1287 1288 /* Can we use inline storage? */ 1289 if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) { 1290 BHND_NV_ASSERT(sizeof(value->data) >= ilen, 1291 ("ilen exceeds inline storage")); 1292 1293 value->data_type = itype; 1294 value->data_len = ilen; 1295 value->data_storage = BHND_NVRAM_VAL_DATA_INLINE; 1296 return (&value->data); 1297 } 1298 1299 /* Is allocation permitted? */ 1300 if (!(flags & BHND_NVRAM_VAL_DYNAMIC)) 1301 return (NULL); 1302 1303 /* Allocate external storage */ 1304 if ((ptr = bhnd_nv_malloc(ilen)) == NULL) 1305 return (NULL); 1306 1307 value->data.ptr = ptr; 1308 value->data_len = ilen; 1309 value->data_type = itype; 1310 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC; 1311 1312 return (ptr); 1313 } 1314