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/ctype.h> 39 #include <sys/kernel.h> 40 #include <sys/malloc.h> 41 #include <sys/systm.h> 42 43 #include <machine/_inttypes.h> 44 45 #else /* !_KERNEL */ 46 47 #include <ctype.h> 48 #include <inttypes.h> 49 #include <errno.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #endif /* _KERNEL */ 54 55 #include "bhnd_nvram_private.h" 56 57 #include "bhnd_nvram_valuevar.h" 58 59 static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, 60 const void *inp, size_t ilen, bhnd_nvram_type itype); 61 62 static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen, 63 bhnd_nvram_type itype, uint32_t flags); 64 static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, 65 size_t ilen, bhnd_nvram_type itype, uint32_t flags); 66 static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value, 67 const void *inp, size_t ilen, bhnd_nvram_type itype); 68 69 70 static int bhnd_nvram_val_encode_data(const void *inp, size_t ilen, 71 bhnd_nvram_type itype, void *outp, size_t *olen, 72 bhnd_nvram_type otype); 73 static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen, 74 bhnd_nvram_type itype, void *outp, size_t *olen, 75 bhnd_nvram_type otype); 76 static int bhnd_nvram_val_encode_null(const void *inp, size_t ilen, 77 bhnd_nvram_type itype, void *outp, size_t *olen, 78 bhnd_nvram_type otype); 79 static int bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, 80 bhnd_nvram_type itype, void *outp, size_t *olen, 81 bhnd_nvram_type otype); 82 static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen, 83 bhnd_nvram_type itype, void *outp, size_t *olen, 84 bhnd_nvram_type otype); 85 86 /** Initialize an empty value instance with @p _fmt, @p _storage, and 87 * an implicit callee-owned reference */ 88 #define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \ 89 (bhnd_nvram_val) { \ 90 .refs = 1, \ 91 .val_storage = _storage, \ 92 .fmt = _fmt, \ 93 .data_storage = BHND_NVRAM_VAL_DATA_NONE, \ 94 }; 95 96 /** Assert that @p value's backing representation state has initialized 97 * as empty. */ 98 #define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \ 99 BHND_NV_ASSERT( \ 100 value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \ 101 value->data_len == 0 && \ 102 value->data.ptr == NULL, \ 103 ("previously initialized value")) 104 105 /** Return true if BHND_NVRAM_VAL_BORROW_DATA or BHND_NVRAM_VAL_STATIC_DATA is 106 * set in @p _flags (e.g. we should attempt to directly reference external 107 * data */ 108 #define BHND_NVRAM_VAL_EXTREF_BORROWED_DATA(_flags) \ 109 (((_flags) & BHND_NVRAM_VAL_BORROW_DATA) || \ 110 ((_flags) & BHND_NVRAM_VAL_STATIC_DATA)) 111 112 /** Flags permitted when performing val-based initialization via 113 * bhnd_nvram_val_convert_init() or bhnd_nvram_val_convert_new() */ 114 #define BHND_NVRAM_VALID_CONV_FLAGS \ 115 (BHND_NVRAM_VAL_FIXED | \ 116 BHND_NVRAM_VAL_DYNAMIC | \ 117 BHND_NVRAM_VAL_COPY_DATA) 118 119 /** Returns true if @p _val must be copied in bhnd_nvram_val_copy(), false 120 * if its reference count may be safely incremented */ 121 #define BHND_NVRAM_VAL_NEED_COPY(_val) \ 122 ((_val)->val_storage == BHND_NVRAM_VAL_STORAGE_AUTO || \ 123 (_val)->data_storage == BHND_NVRAM_VAL_DATA_EXT_WEAK) 124 125 volatile u_int refs; /**< reference count */ 126 bhnd_nvram_val_storage val_storage; /**< value structure storage */ 127 const bhnd_nvram_val_fmt *fmt; /**< value format */ 128 bhnd_nvram_val_data_storage data_storage; /**< data storage */ 129 bhnd_nvram_type data_type; /**< data type */ 130 size_t data_len; /**< data size */ 131 132 /* Shared NULL value instance */ 133 bhnd_nvram_val bhnd_nvram_val_null = { 134 .refs = 1, 135 .val_storage = BHND_NVRAM_VAL_STORAGE_STATIC, 136 .fmt = &bhnd_nvram_val_null_fmt, 137 .data_storage = BHND_NVRAM_VAL_DATA_INLINE, 138 .data_type = BHND_NVRAM_TYPE_NULL, 139 .data_len = 0, 140 }; 141 142 /** 143 * Return the human-readable name of @p fmt. 144 */ 145 const char * 146 bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt *fmt) 147 { 148 return (fmt->name); 149 } 150 151 /** 152 * Return the default format for values of @p type. 153 */ 154 const bhnd_nvram_val_fmt * 155 bhnd_nvram_val_default_fmt(bhnd_nvram_type type) 156 { 157 switch (type) { 158 case BHND_NVRAM_TYPE_UINT8: 159 return (&bhnd_nvram_val_uint8_fmt); 160 case BHND_NVRAM_TYPE_UINT16: 161 return (&bhnd_nvram_val_uint16_fmt); 162 case BHND_NVRAM_TYPE_UINT32: 163 return (&bhnd_nvram_val_uint32_fmt); 164 case BHND_NVRAM_TYPE_UINT64: 165 return (&bhnd_nvram_val_uint64_fmt); 166 case BHND_NVRAM_TYPE_INT8: 167 return (&bhnd_nvram_val_int8_fmt); 168 case BHND_NVRAM_TYPE_INT16: 169 return (&bhnd_nvram_val_int16_fmt); 170 case BHND_NVRAM_TYPE_INT32: 171 return (&bhnd_nvram_val_int32_fmt); 172 case BHND_NVRAM_TYPE_INT64: 173 return (&bhnd_nvram_val_int64_fmt); 174 case BHND_NVRAM_TYPE_CHAR: 175 return (&bhnd_nvram_val_char_fmt); 176 case BHND_NVRAM_TYPE_STRING: 177 return (&bhnd_nvram_val_string_fmt); 178 case BHND_NVRAM_TYPE_BOOL: 179 return (&bhnd_nvram_val_bool_fmt); 180 case BHND_NVRAM_TYPE_NULL: 181 return (&bhnd_nvram_val_null_fmt); 182 case BHND_NVRAM_TYPE_DATA: 183 return (&bhnd_nvram_val_data_fmt); 184 case BHND_NVRAM_TYPE_UINT8_ARRAY: 185 return (&bhnd_nvram_val_uint8_array_fmt); 186 case BHND_NVRAM_TYPE_UINT16_ARRAY: 187 return (&bhnd_nvram_val_uint16_array_fmt); 188 case BHND_NVRAM_TYPE_UINT32_ARRAY: 189 return (&bhnd_nvram_val_uint32_array_fmt); 190 case BHND_NVRAM_TYPE_UINT64_ARRAY: 191 return (&bhnd_nvram_val_uint64_array_fmt); 192 case BHND_NVRAM_TYPE_INT8_ARRAY: 193 return (&bhnd_nvram_val_int8_array_fmt); 194 case BHND_NVRAM_TYPE_INT16_ARRAY: 195 return (&bhnd_nvram_val_int16_array_fmt); 196 case BHND_NVRAM_TYPE_INT32_ARRAY: 197 return (&bhnd_nvram_val_int32_array_fmt); 198 case BHND_NVRAM_TYPE_INT64_ARRAY: 199 return (&bhnd_nvram_val_int64_array_fmt); 200 case BHND_NVRAM_TYPE_CHAR_ARRAY: 201 return (&bhnd_nvram_val_char_array_fmt); 202 case BHND_NVRAM_TYPE_STRING_ARRAY: 203 return (&bhnd_nvram_val_string_array_fmt); 204 case BHND_NVRAM_TYPE_BOOL_ARRAY: 205 return (&bhnd_nvram_val_bool_array_fmt); 206 } 207 208 /* Quiesce gcc4.2 */ 209 BHND_NV_PANIC("bhnd nvram type %u unknown", type); 210 } 211 212 /** 213 * Determine whether @p fmt (or new format delegated to by @p fmt) is 214 * capable of direct initialization from buffer @p inp. 215 * 216 * @param[in,out] fmt Indirect pointer to the NVRAM value format. If 217 * the format instance cannot handle the data type 218 * directly, it may delegate to a new format 219 * instance. On success, this parameter will be 220 * set to the format that should be used when 221 * performing initialization from @p inp. 222 * @param inp Input data. 223 * @param ilen Input data length. 224 * @param itype Input data type. 225 * 226 * @retval 0 If initialization from @p inp is supported. 227 * @retval EFTYPE If initialization from @p inp is unsupported. 228 * @retval EFAULT if @p ilen is not correctly aligned for elements of 229 * @p itype. 230 */ 231 static int 232 bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp, 233 size_t ilen, bhnd_nvram_type itype) 234 { 235 const bhnd_nvram_val_fmt *ofmt, *nfmt; 236 int error; 237 238 nfmt = ofmt = *fmt; 239 240 /* Validate alignment */ 241 if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) 242 return (error); 243 244 /* If the format does not provide a filter function, it only supports 245 * direct initialization from its native type */ 246 if (ofmt->op_filter == NULL) { 247 if (itype == ofmt->native_type) 248 return (0); 249 250 return (EFTYPE); 251 } 252 253 /* Use the filter function to determine whether direct initialization 254 * from itype is permitted */ 255 error = ofmt->op_filter(&nfmt, inp, ilen, itype); 256 if (error) 257 return (error); 258 259 /* Retry filter with new format? */ 260 if (ofmt != nfmt) { 261 error = bhnd_nvram_val_fmt_filter(&nfmt, inp, ilen, itype); 262 if (error) 263 return (error); 264 265 /* Success -- provide delegated format to caller */ 266 *fmt = nfmt; 267 } 268 269 /* Value can be initialized with provided format and input type */ 270 return (0); 271 } 272 273 /* Common initialization support for bhnd_nvram_val_init() and 274 * bhnd_nvram_val_new() */ 275 static int 276 bhnd_nvram_val_init_common(bhnd_nvram_val *value, 277 bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt, 278 const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) 279 { 280 void *outp; 281 bhnd_nvram_type otype; 282 size_t olen; 283 int error; 284 285 /* If the value format is unspecified, we use the default format 286 * for the input data type */ 287 if (fmt == NULL) 288 fmt = bhnd_nvram_val_default_fmt(itype); 289 290 /* Determine expected data type, and allow the format to delegate to 291 * a new format instance */ 292 if ((error = bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype))) { 293 /* Direct initialization from the provided input type is 294 * not supported; alue must be initialized with the format's 295 * native type */ 296 otype = fmt->native_type; 297 } else { 298 /* Value can be initialized with provided input type */ 299 otype = itype; 300 } 301 302 /* Initialize value instance */ 303 *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); 304 305 /* If input data already in native format, init directly. */ 306 if (otype == itype) { 307 error = bhnd_nvram_val_set(value, inp, ilen, itype, flags); 308 if (error) 309 return (error); 310 311 return (0); 312 } 313 314 /* Determine size when encoded in native format */ 315 error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype); 316 if (error) 317 return (error); 318 319 /* Fetch reference to (or allocate) an appropriately sized buffer */ 320 outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); 321 if (outp == NULL) 322 return (ENOMEM); 323 324 /* Perform encode */ 325 error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype); 326 if (error) 327 return (error); 328 329 return (0); 330 } 331 332 /** 333 * Initialize an externally allocated instance of @p value with @p fmt from the 334 * given @p inp buffer of @p itype and @p ilen. 335 * 336 * On success, the caller owns a reference to @p value, and is responsible for 337 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 338 * 339 * @param value The externally allocated value instance to be 340 * initialized. 341 * @param fmt The value's format, or NULL to use the default format 342 * for @p itype. 343 * @param inp Input buffer. 344 * @param ilen Input buffer length. 345 * @param itype Input buffer type. 346 * @param flags Value flags (see BHND_NVRAM_VAL_*). 347 * 348 * @retval 0 success 349 * @retval ENOMEM If allocation fails. 350 * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. 351 * @retval EFAULT if @p ilen is not correctly aligned for elements of 352 * @p itype. 353 * @retval ERANGE If value coercion would overflow (or underflow) the 354 * @p fmt representation. 355 */ 356 int 357 bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt, 358 const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) 359 { 360 int error; 361 362 error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO, 363 fmt, inp, ilen, itype, flags); 364 if (error) 365 bhnd_nvram_val_release(value); 366 367 return (error); 368 } 369 370 /** 371 * Allocate a value instance with @p fmt, and attempt to initialize its internal 372 * representation from the given @p inp buffer of @p itype and @p ilen. 373 * 374 * On success, the caller owns a reference to @p value, and is responsible for 375 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 376 * 377 * @param[out] value On success, the allocated value instance. 378 * @param fmt The value's format, or NULL to use the default format 379 * for @p itype. 380 * @param inp Input buffer. 381 * @param ilen Input buffer length. 382 * @param itype Input buffer type. 383 * @param flags Value flags (see BHND_NVRAM_VAL_*). 384 * 385 * @retval 0 success 386 * @retval ENOMEM If allocation fails. 387 * @retval EFTYPE If @p fmt initialization from @p itype is unsupported. 388 * @retval EFAULT if @p ilen is not correctly aligned for elements of 389 * @p itype. 390 * @retval ERANGE If value coercion would overflow (or underflow) the 391 * @p fmt representation. 392 */ 393 int 394 bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt, 395 const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags) 396 { 397 int error; 398 399 /* Allocate new instance */ 400 if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) 401 return (ENOMEM); 402 403 /* Perform common initialization. */ 404 error = bhnd_nvram_val_init_common(*value, 405 BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags); 406 if (error) { 407 /* Will also free() the value allocation */ 408 bhnd_nvram_val_release(*value); 409 } 410 411 return (error); 412 } 413 414 415 /* Common initialization support for bhnd_nvram_val_convert_init() and 416 * bhnd_nvram_val_convert_new() */ 417 static int 418 bhnd_nvram_val_convert_common(bhnd_nvram_val *value, 419 bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt, 420 bhnd_nvram_val *src, uint32_t flags) 421 { 422 const void *inp; 423 void *outp; 424 bhnd_nvram_type itype, otype; 425 size_t ilen, olen; 426 int error; 427 428 /* Determine whether direct initialization from the source value's 429 * existing data type is supported by the new format */ 430 inp = bhnd_nvram_val_bytes(src, &ilen, &itype); 431 if (bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype) == 0) { 432 /* Adjust value flags based on the source data storage */ 433 switch (src->data_storage) { 434 case BHND_NVRAM_VAL_DATA_NONE: 435 case BHND_NVRAM_VAL_DATA_INLINE: 436 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 437 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 438 break; 439 440 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 441 /* If the source data has static storage duration, 442 * we should apply that transitively */ 443 if (flags & BHND_NVRAM_VAL_BORROW_DATA) 444 flags |= BHND_NVRAM_VAL_STATIC_DATA; 445 446 break; 447 } 448 449 /* Delegate to standard initialization */ 450 return (bhnd_nvram_val_init_common(value, val_storage, fmt, inp, 451 ilen, itype, flags)); 452 } 453 454 /* Value must be initialized with the format's native type */ 455 otype = fmt->native_type; 456 457 /* Initialize value instance */ 458 *value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage); 459 460 /* Determine size when encoded in native format */ 461 if ((error = bhnd_nvram_val_encode(src, NULL, &olen, otype))) 462 return (error); 463 464 /* Fetch reference to (or allocate) an appropriately sized buffer */ 465 outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags); 466 if (outp == NULL) 467 return (ENOMEM); 468 469 /* Perform encode */ 470 if ((error = bhnd_nvram_val_encode(src, outp, &olen, otype))) 471 return (error); 472 473 return (0); 474 } 475 476 /** 477 * Initialize an externally allocated instance of @p value with @p fmt, and 478 * attempt to initialize its internal representation from the given @p src 479 * value. 480 * 481 * On success, the caller owns a reference to @p value, and is responsible for 482 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 483 * 484 * @param value The externally allocated value instance to be 485 * initialized. 486 * @param fmt The value's format. 487 * @param src Input value to be converted. 488 * @param flags Value flags (see BHND_NVRAM_VAL_*). 489 * 490 * @retval 0 success 491 * @retval ENOMEM If allocation fails. 492 * @retval EFTYPE If @p fmt initialization from @p src is unsupported. 493 * @retval EFAULT if @p ilen is not correctly aligned for elements of 494 * @p itype. 495 * @retval ERANGE If value coercion of @p src would overflow 496 * (or underflow) the @p fmt representation. 497 */ 498 int 499 bhnd_nvram_val_convert_init(bhnd_nvram_val *value, 500 const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags) 501 { 502 int error; 503 504 error = bhnd_nvram_val_convert_common(value, 505 BHND_NVRAM_VAL_STORAGE_AUTO, fmt, src, flags); 506 if (error) 507 bhnd_nvram_val_release(value); 508 509 return (error); 510 } 511 512 /** 513 * Allocate a value instance with @p fmt, and attempt to initialize its internal 514 * representation from the given @p src value. 515 * 516 * On success, the caller owns a reference to @p value, and is responsible for 517 * freeing any resources allocated for @p value via bhnd_nvram_val_release(). 518 * 519 * @param[out] value On success, the allocated value instance. 520 * @param fmt The value's format. 521 * @param src Input value to be converted. 522 * @param flags Value flags (see BHND_NVRAM_VAL_*). 523 * 524 * @retval 0 success 525 * @retval ENOMEM If allocation fails. 526 * @retval EFTYPE If @p fmt initialization from @p src is unsupported. 527 * @retval EFAULT if @p ilen is not correctly aligned for elements of 528 * @p itype. 529 * @retval ERANGE If value coercion of @p src would overflow 530 * (or underflow) the @p fmt representation. 531 */ 532 int 533 bhnd_nvram_val_convert_new(bhnd_nvram_val **value, 534 const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags) 535 { 536 int error; 537 538 /* Allocate new instance */ 539 if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL) 540 return (ENOMEM); 541 542 /* Perform common initialization. */ 543 error = bhnd_nvram_val_convert_common(*value, 544 BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, src, flags); 545 if (error) { 546 /* Will also free() the value allocation */ 547 bhnd_nvram_val_release(*value); 548 } 549 550 return (error); 551 } 552 553 /** 554 * Copy or retain a reference to @p value. 555 * 556 * On success, the caller is responsible for freeing the result via 557 * bhnd_nvram_val_release(). 558 * 559 * @param value The value to be copied (or retained). 560 * 561 * @retval bhnd_nvram_val if @p value was successfully copied or retained. 562 * @retval NULL if allocation failed. 563 */ 564 bhnd_nvram_val * 565 bhnd_nvram_val_copy(bhnd_nvram_val *value) 566 { 567 bhnd_nvram_val *result; 568 const void *bytes; 569 bhnd_nvram_type type; 570 size_t len; 571 uint32_t flags; 572 int error; 573 574 switch (value->val_storage) { 575 case BHND_NVRAM_VAL_STORAGE_STATIC: 576 /* If static, can return as-is */ 577 return (value); 578 579 case BHND_NVRAM_VAL_STORAGE_DYNAMIC: 580 if (!BHND_NVRAM_VAL_NEED_COPY(value)) { 581 refcount_acquire(&value->refs); 582 return (value); 583 } 584 585 /* Perform copy below */ 586 break; 587 588 case BHND_NVRAM_VAL_STORAGE_AUTO: 589 BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has " 590 "active refcount (%u)", value->refs)); 591 592 /* Perform copy below */ 593 break; 594 } 595 596 597 /* Compute the new value's flags based on the source value */ 598 switch (value->data_storage) { 599 case BHND_NVRAM_VAL_DATA_NONE: 600 case BHND_NVRAM_VAL_DATA_INLINE: 601 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 602 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 603 /* Copy the source data and permit additional allocation if the 604 * value cannot be represented inline */ 605 flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC; 606 break; 607 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 608 flags = BHND_NVRAM_VAL_STATIC_DATA; 609 break; 610 default: 611 BHND_NV_PANIC("invalid storage type: %d", value->data_storage); 612 } 613 614 /* Allocate new value copy */ 615 bytes = bhnd_nvram_val_bytes(value, &len, &type); 616 error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type, 617 flags); 618 if (error) { 619 BHND_NV_LOG("copy failed: %d", error); 620 return (NULL); 621 } 622 623 return (result); 624 } 625 626 /** 627 * Release a reference to @p value. 628 * 629 * If this is the last reference, all associated resources will be freed. 630 * 631 * @param value The value to be released. 632 */ 633 void 634 bhnd_nvram_val_release(bhnd_nvram_val *value) 635 { 636 BHND_NV_ASSERT(value->refs >= 1, ("value over-released")); 637 638 /* Skip if value is static */ 639 if (value->val_storage == BHND_NVRAM_VAL_STORAGE_STATIC) 640 return; 641 642 /* Drop reference */ 643 if (!refcount_release(&value->refs)) 644 return; 645 646 /* Free allocated external representation data */ 647 switch (value->data_storage) { 648 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 649 bhnd_nv_free(__DECONST(void *, value->data.ptr)); 650 break; 651 case BHND_NVRAM_VAL_DATA_NONE: 652 case BHND_NVRAM_VAL_DATA_INLINE: 653 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 654 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 655 /* Nothing to free */ 656 break; 657 } 658 659 /* Free instance if dynamically allocated */ 660 if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) 661 bhnd_nv_free(value); 662 } 663 664 /** 665 * Standard BHND_NVRAM_TYPE_NULL encoding implementation. 666 */ 667 static int 668 bhnd_nvram_val_encode_null(const void *inp, size_t ilen, bhnd_nvram_type itype, 669 void *outp, size_t *olen, bhnd_nvram_type otype) 670 { 671 size_t limit, nbytes; 672 673 BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_NULL, 674 ("unsupported type: %d", itype)); 675 676 /* Determine output byte limit */ 677 if (outp != NULL) 678 limit = *olen; 679 else 680 limit = 0; 681 682 nbytes = 0; 683 684 /* Write to output */ 685 switch (otype) { 686 case BHND_NVRAM_TYPE_NULL: 687 /* Can be directly encoded as a zero-length NULL value */ 688 nbytes = 0; 689 break; 690 default: 691 /* Not representable */ 692 return (EFTYPE); 693 } 694 695 /* Provide required length */ 696 *olen = nbytes; 697 if (limit < *olen) { 698 if (outp == NULL) 699 return (0); 700 701 return (ENOMEM); 702 } 703 704 return (0); 705 } 706 707 /** 708 * Standard BHND_NVRAM_TYPE_BOOL encoding implementation. 709 */ 710 static int 711 bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, bhnd_nvram_type itype, 712 void *outp, size_t *olen, bhnd_nvram_type otype) 713 { 714 bhnd_nvram_bool_t bval; 715 size_t limit, nbytes, nelem; 716 int error; 717 718 BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_BOOL, 719 ("unsupported type: %d", itype)); 720 721 /* Determine output byte limit */ 722 if (outp != NULL) 723 limit = *olen; 724 else 725 limit = 0; 726 727 /* Must be exactly one element in input */ 728 if ((error = bhnd_nvram_value_nelem(inp, ilen, itype, &nelem))) 729 return (error); 730 731 if (nelem != 1) 732 return (EFTYPE); 733 734 /* Fetch (and normalize) boolean value */ 735 bval = (*(const bhnd_nvram_bool_t *)inp != 0) ? true : false; 736 737 /* Write to output */ 738 switch (otype) { 739 case BHND_NVRAM_TYPE_NULL: 740 /* False can be directly encoded as a zero-length NULL value */ 741 if (bval != false) 742 return (EFTYPE); 743 744 nbytes = 0; 745 break; 746 747 case BHND_NVRAM_TYPE_STRING: 748 case BHND_NVRAM_TYPE_STRING_ARRAY: { 749 /* Can encode as "true" or "false" */ 750 const char *str = bval ? "true" : "false"; 751 752 nbytes = strlen(str) + 1; 753 if (limit > nbytes) 754 strcpy(outp, str); 755 756 break; 757 } 758 759 default: 760 /* If output type is an integer, we can delegate to standard 761 * integer encoding to encode as zero or one. */ 762 if (bhnd_nvram_is_int_type(otype)) { 763 uint8_t ival = bval ? 1 : 0; 764 765 return (bhnd_nvram_val_encode_int(&ival, sizeof(ival), 766 BHND_NVRAM_TYPE_UINT8, outp, olen, otype)); 767 } 768 769 /* Otherwise not representable */ 770 return (EFTYPE); 771 } 772 773 /* Provide required length */ 774 *olen = nbytes; 775 if (limit < *olen) { 776 if (outp == NULL) 777 return (0); 778 779 return (ENOMEM); 780 } 781 782 return (0); 783 } 784 785 /** 786 * Standard BHND_NVRAM_TYPE_DATA encoding implementation. 787 */ 788 static int 789 bhnd_nvram_val_encode_data(const void *inp, size_t ilen, bhnd_nvram_type itype, 790 void *outp, size_t *olen, bhnd_nvram_type otype) 791 { 792 BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_DATA, 793 ("unsupported type: %d", itype)); 794 795 /* Write to output */ 796 switch (otype) { 797 case BHND_NVRAM_TYPE_STRING: 798 case BHND_NVRAM_TYPE_STRING_ARRAY: 799 /* If encoding as a string, produce an EFI-style hexadecimal 800 * byte array (HF1F...) by interpreting the octet string 801 * as an array of uint8 values */ 802 return (bhnd_nvram_value_printf("H%[]02hhX", inp, ilen, 803 BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, "")); 804 805 default: 806 /* Fall back on direct interpretation as an array of 8-bit 807 * integers array */ 808 return (bhnd_nvram_value_coerce(inp, ilen, 809 BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, otype)); 810 } 811 } 812 813 814 /** 815 * Standard string/char array/char encoding implementation. 816 * 817 * Input type must be one of: 818 * - BHND_NVRAM_TYPE_STRING 819 * - BHND_NVRAM_TYPE_CHAR 820 * - BHND_NVRAM_TYPE_CHAR_ARRAY 821 */ 822 static int 823 bhnd_nvram_val_encode_string(const void *inp, size_t ilen, 824 bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype) 825 { 826 const char *cstr; 827 bhnd_nvram_type otype_base; 828 size_t cstr_size, cstr_len; 829 size_t limit, nbytes; 830 831 BHND_NV_ASSERT( 832 itype == BHND_NVRAM_TYPE_STRING || 833 itype == BHND_NVRAM_TYPE_CHAR || 834 itype == BHND_NVRAM_TYPE_CHAR_ARRAY, 835 ("unsupported type: %d", itype)); 836 837 cstr = inp; 838 cstr_size = ilen; 839 nbytes = 0; 840 otype_base = bhnd_nvram_base_type(otype); 841 842 /* Determine output byte limit */ 843 if (outp != NULL) 844 limit = *olen; 845 else 846 limit = 0; 847 848 /* Determine string length, minus trailing NUL (if any) */ 849 cstr_len = strnlen(cstr, cstr_size); 850 851 /* Parse the string data and write to output */ 852 switch (otype) { 853 case BHND_NVRAM_TYPE_NULL: 854 /* Only an empty string may be represented as a NULL value */ 855 if (cstr_len != 0) 856 return (EFTYPE); 857 858 *olen = 0; 859 return (0); 860 861 case BHND_NVRAM_TYPE_CHAR: 862 case BHND_NVRAM_TYPE_CHAR_ARRAY: 863 /* String must contain exactly 1 non-terminating-NUL character 864 * to be represented as a single char */ 865 if (!bhnd_nvram_is_array_type(otype)) { 866 if (cstr_len != 1) 867 return (EFTYPE); 868 } 869 870 /* Copy out the characters directly (excluding trailing NUL) */ 871 for (size_t i = 0; i < cstr_len; i++) { 872 if (limit > nbytes) 873 *((uint8_t *)outp + nbytes) = cstr[i]; 874 nbytes++; 875 } 876 877 /* Provide required length */ 878 *olen = nbytes; 879 if (limit < *olen && outp != NULL) 880 return (ENOMEM); 881 882 return (0); 883 884 case BHND_NVRAM_TYPE_BOOL: 885 case BHND_NVRAM_TYPE_BOOL_ARRAY: { 886 const char *p; 887 size_t plen; 888 bhnd_nvram_bool_t bval; 889 890 /* Trim leading/trailing whitespace */ 891 p = cstr; 892 plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); 893 894 /* Parse string representation */ 895 if (strncasecmp(p, "true", plen) == 0 || 896 strncasecmp(p, "yes", plen) == 0 || 897 strncmp(p, "1", plen) == 0) 898 { 899 bval = true; 900 } else if (strncasecmp(p, "false", plen) == 0 || 901 strncasecmp(p, "no", plen) == 0 || 902 strncmp(p, "0", plen) == 0) 903 { 904 bval = false; 905 } else { 906 /* Not a recognized boolean string */ 907 return (EFTYPE); 908 } 909 910 /* Write to output */ 911 nbytes = sizeof(bhnd_nvram_bool_t); 912 if (limit >= nbytes) 913 *((bhnd_nvram_bool_t *)outp) = bval; 914 915 /* Provide required length */ 916 *olen = nbytes; 917 if (limit < *olen && outp != NULL) 918 return (ENOMEM); 919 920 return (0); 921 } 922 923 case BHND_NVRAM_TYPE_DATA: { 924 const char *p; 925 size_t plen, parsed_len; 926 int error; 927 928 /* Trim leading/trailing whitespace */ 929 p = cstr; 930 plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); 931 932 /* Check for EFI-style hexadecimal byte array string format. 933 * Must have a 'H' prefix */ 934 if (plen < 1 || bhnd_nv_toupper(*p) != 'H') 935 return (EFTYPE); 936 937 /* Skip leading 'H' */ 938 p++; 939 plen--; 940 941 /* Parse the input string's two-char octets until the end 942 * of input is reached. The last octet may contain only 943 * one char */ 944 while (plen > 0) { 945 uint8_t byte; 946 size_t byte_len = sizeof(byte); 947 948 /* Parse next two-character hex octet */ 949 error = bhnd_nvram_parse_int(p, bhnd_nv_ummin(plen, 2), 950 16, &parsed_len, &byte, &byte_len, otype_base); 951 if (error) { 952 BHND_NV_DEBUG("error parsing '%.*s' as " 953 "integer: %d\n", BHND_NV_PRINT_WIDTH(plen), 954 p, error); 955 956 return (error); 957 } 958 959 /* Write to output */ 960 if (limit > nbytes) 961 *((uint8_t *)outp + nbytes) = byte; 962 nbytes++; 963 964 /* Advance input */ 965 p += parsed_len; 966 plen -= parsed_len; 967 } 968 969 /* Provide required length */ 970 *olen = nbytes; 971 if (limit < *olen && outp != NULL) 972 return (ENOMEM); 973 974 return (0); 975 } 976 977 case BHND_NVRAM_TYPE_UINT8: 978 case BHND_NVRAM_TYPE_UINT8_ARRAY: 979 case BHND_NVRAM_TYPE_UINT16: 980 case BHND_NVRAM_TYPE_UINT16_ARRAY: 981 case BHND_NVRAM_TYPE_UINT32: 982 case BHND_NVRAM_TYPE_UINT32_ARRAY: 983 case BHND_NVRAM_TYPE_UINT64: 984 case BHND_NVRAM_TYPE_UINT64_ARRAY: 985 case BHND_NVRAM_TYPE_INT8: 986 case BHND_NVRAM_TYPE_INT8_ARRAY: 987 case BHND_NVRAM_TYPE_INT16: 988 case BHND_NVRAM_TYPE_INT16_ARRAY: 989 case BHND_NVRAM_TYPE_INT32: 990 case BHND_NVRAM_TYPE_INT32_ARRAY: 991 case BHND_NVRAM_TYPE_INT64: 992 case BHND_NVRAM_TYPE_INT64_ARRAY: { 993 const char *p; 994 size_t plen, parsed_len; 995 int error; 996 997 /* Trim leading/trailing whitespace */ 998 p = cstr; 999 plen = bhnd_nvram_trim_field(&p, cstr_len, '\0'); 1000 1001 /* Try to parse the integer value */ 1002 error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp, 1003 olen, otype_base); 1004 if (error) { 1005 BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n", 1006 BHND_NV_PRINT_WIDTH(plen), p, error); 1007 return (error); 1008 } 1009 1010 /* Do additional bytes remain unparsed? */ 1011 if (plen != parsed_len) { 1012 BHND_NV_DEBUG("error parsing '%.*s' as a single " 1013 "integer value; trailing garbage '%.*s'\n", 1014 BHND_NV_PRINT_WIDTH(plen), p, 1015 BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len); 1016 return (EFTYPE); 1017 } 1018 1019 return (0); 1020 } 1021 1022 case BHND_NVRAM_TYPE_STRING: 1023 case BHND_NVRAM_TYPE_STRING_ARRAY: 1024 /* Copy out the string representation as-is */ 1025 *olen = cstr_size; 1026 1027 /* Need additional space for trailing NUL? */ 1028 if (cstr_len == cstr_size) 1029 (*olen)++; 1030 1031 /* Skip output? */ 1032 if (outp == NULL) 1033 return (0); 1034 1035 /* Verify required length */ 1036 if (limit < *olen) 1037 return (ENOMEM); 1038 1039 /* Copy and NUL terminate */ 1040 strncpy(outp, cstr, cstr_len); 1041 *((char *)outp + cstr_len) = '\0'; 1042 1043 return (0); 1044 } 1045 1046 BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype)); 1047 } 1048 1049 /** 1050 * Standard integer encoding implementation. 1051 */ 1052 static int 1053 bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype, 1054 void *outp, size_t *olen, bhnd_nvram_type otype) 1055 { 1056 bhnd_nvram_type otype_base; 1057 size_t limit, nbytes; 1058 bool itype_signed, otype_signed, otype_int; 1059 union { 1060 uint64_t u64; 1061 int64_t i64; 1062 } intv; 1063 1064 BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type")); 1065 1066 /* Determine output byte limit */ 1067 if (outp != NULL) 1068 limit = *olen; 1069 else 1070 limit = 0; 1071 1072 /* Fetch output type info */ 1073 otype_base = bhnd_nvram_base_type(otype); 1074 otype_int = bhnd_nvram_is_int_type(otype); 1075 otype_signed = bhnd_nvram_is_signed_type(otype_base); 1076 1077 /* 1078 * Promote integer value to a common 64-bit representation. 1079 */ 1080 switch (itype) { 1081 case BHND_NVRAM_TYPE_UINT8: 1082 if (ilen != sizeof(uint8_t)) 1083 return (EFAULT); 1084 1085 itype_signed = false; 1086 intv.u64 = *(const uint8_t *)inp; 1087 break; 1088 1089 case BHND_NVRAM_TYPE_UINT16: 1090 if (ilen != sizeof(uint16_t)) 1091 return (EFAULT); 1092 1093 itype_signed = false; 1094 intv.u64 = *(const uint16_t *)inp; 1095 break; 1096 1097 case BHND_NVRAM_TYPE_UINT32: 1098 if (ilen != sizeof(uint32_t)) 1099 return (EFAULT); 1100 1101 itype_signed = false; 1102 intv.u64 = *(const uint32_t *)inp; 1103 break; 1104 1105 case BHND_NVRAM_TYPE_UINT64: 1106 if (ilen != sizeof(uint64_t)) 1107 return (EFAULT); 1108 1109 itype_signed = false; 1110 intv.u64 = *(const uint64_t *)inp; 1111 break; 1112 1113 case BHND_NVRAM_TYPE_INT8: 1114 if (ilen != sizeof(int8_t)) 1115 return (EFAULT); 1116 1117 itype_signed = true; 1118 intv.i64 = *(const int8_t *)inp; 1119 break; 1120 1121 case BHND_NVRAM_TYPE_INT16: 1122 if (ilen != sizeof(int16_t)) 1123 return (EFAULT); 1124 1125 itype_signed = true; 1126 intv.i64 = *(const int16_t *)inp; 1127 break; 1128 1129 case BHND_NVRAM_TYPE_INT32: 1130 if (ilen != sizeof(int32_t)) 1131 return (EFAULT); 1132 1133 itype_signed = true; 1134 intv.i64 = *(const int32_t *)inp; 1135 break; 1136 1137 case BHND_NVRAM_TYPE_INT64: 1138 if (ilen != sizeof(int32_t)) 1139 return (EFAULT); 1140 1141 itype_signed = true; 1142 intv.i64 = *(const int32_t *)inp; 1143 break; 1144 1145 default: 1146 BHND_NV_PANIC("invalid type %d\n", itype); 1147 } 1148 1149 /* Perform signed/unsigned conversion */ 1150 if (itype_signed && otype_int && !otype_signed) { 1151 if (intv.i64 < 0) { 1152 /* Can't represent negative value */ 1153 BHND_NV_LOG("cannot represent %" PRId64 " as %s\n", 1154 intv.i64, bhnd_nvram_type_name(otype)); 1155 1156 return (ERANGE); 1157 } 1158 1159 /* Convert to unsigned representation */ 1160 intv.u64 = intv.i64; 1161 1162 } else if (!itype_signed && otype_int && otype_signed) { 1163 /* Handle unsigned -> signed coercions */ 1164 if (intv.u64 > INT64_MAX) { 1165 /* Can't represent positive value */ 1166 BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n", 1167 intv.u64, bhnd_nvram_type_name(otype)); 1168 return (ERANGE); 1169 } 1170 1171 /* Convert to signed representation */ 1172 intv.i64 = intv.u64; 1173 } 1174 1175 /* Write output */ 1176 switch (otype) { 1177 case BHND_NVRAM_TYPE_NULL: 1178 /* Cannot encode an integer value as NULL */ 1179 return (EFTYPE); 1180 1181 case BHND_NVRAM_TYPE_BOOL: { 1182 bhnd_nvram_bool_t bval; 1183 1184 if (intv.u64 == 0 || intv.u64 == 1) { 1185 bval = intv.u64; 1186 } else { 1187 /* Encoding as a bool would lose information */ 1188 return (ERANGE); 1189 } 1190 1191 nbytes = sizeof(bhnd_nvram_bool_t); 1192 if (limit >= nbytes) 1193 *((bhnd_nvram_bool_t *)outp) = bval; 1194 1195 break; 1196 } 1197 1198 case BHND_NVRAM_TYPE_CHAR: 1199 case BHND_NVRAM_TYPE_CHAR_ARRAY: 1200 case BHND_NVRAM_TYPE_DATA: 1201 case BHND_NVRAM_TYPE_UINT8: 1202 case BHND_NVRAM_TYPE_UINT8_ARRAY: 1203 if (intv.u64 > UINT8_MAX) 1204 return (ERANGE); 1205 1206 nbytes = sizeof(uint8_t); 1207 if (limit >= nbytes) 1208 *((uint8_t *)outp) = (uint8_t)intv.u64; 1209 break; 1210 1211 case BHND_NVRAM_TYPE_UINT16: 1212 case BHND_NVRAM_TYPE_UINT16_ARRAY: 1213 if (intv.u64 > UINT16_MAX) 1214 return (ERANGE); 1215 1216 nbytes = sizeof(uint16_t); 1217 if (limit >= nbytes) 1218 *((uint16_t *)outp) = (uint16_t)intv.u64; 1219 break; 1220 1221 case BHND_NVRAM_TYPE_UINT32: 1222 case BHND_NVRAM_TYPE_UINT32_ARRAY: 1223 if (intv.u64 > UINT32_MAX) 1224 return (ERANGE); 1225 1226 nbytes = sizeof(uint32_t); 1227 if (limit >= nbytes) 1228 *((uint32_t *)outp) = (uint32_t)intv.u64; 1229 break; 1230 1231 case BHND_NVRAM_TYPE_UINT64: 1232 case BHND_NVRAM_TYPE_UINT64_ARRAY: 1233 nbytes = sizeof(uint64_t); 1234 if (limit >= nbytes) 1235 *((uint64_t *)outp) = intv.u64; 1236 break; 1237 1238 case BHND_NVRAM_TYPE_INT8: 1239 case BHND_NVRAM_TYPE_INT8_ARRAY: 1240 if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX) 1241 return (ERANGE); 1242 1243 nbytes = sizeof(int8_t); 1244 if (limit >= nbytes) 1245 *((int8_t *)outp) = (int8_t)intv.i64; 1246 break; 1247 1248 case BHND_NVRAM_TYPE_INT16: 1249 case BHND_NVRAM_TYPE_INT16_ARRAY: 1250 if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX) 1251 return (ERANGE); 1252 1253 nbytes = sizeof(int16_t); 1254 if (limit >= nbytes) 1255 *((int16_t *)outp) = (int16_t)intv.i64; 1256 break; 1257 1258 case BHND_NVRAM_TYPE_INT32: 1259 case BHND_NVRAM_TYPE_INT32_ARRAY: 1260 if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX) 1261 return (ERANGE); 1262 1263 nbytes = sizeof(int32_t); 1264 if (limit >= nbytes) 1265 *((int32_t *)outp) = (int32_t)intv.i64; 1266 break; 1267 1268 case BHND_NVRAM_TYPE_INT64: 1269 case BHND_NVRAM_TYPE_INT64_ARRAY: 1270 nbytes = sizeof(int64_t); 1271 if (limit >= nbytes) 1272 *((int64_t *)outp) = intv.i64; 1273 break; 1274 1275 case BHND_NVRAM_TYPE_STRING: 1276 case BHND_NVRAM_TYPE_STRING_ARRAY: { 1277 ssize_t len; 1278 1279 /* Attempt to write the entry + NUL */ 1280 if (otype_signed) { 1281 len = snprintf(outp, limit, "%" PRId64, intv.i64); 1282 } else { 1283 len = snprintf(outp, limit, "%" PRIu64, intv.u64); 1284 } 1285 1286 if (len < 0) { 1287 BHND_NV_LOG("snprintf() failed: %zd\n", len); 1288 return (EFTYPE); 1289 } 1290 1291 /* Set total length to the formatted string length, plus 1292 * trailing NUL */ 1293 nbytes = len + 1; 1294 break; 1295 } 1296 1297 default: 1298 BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype)); 1299 return (EFTYPE); 1300 } 1301 1302 /* Provide required length */ 1303 *olen = nbytes; 1304 if (limit < *olen) { 1305 if (outp == NULL) 1306 return (0); 1307 1308 return (ENOMEM); 1309 } 1310 1311 return (0); 1312 } 1313 1314 /** 1315 * Encode the given @p value as @p otype, writing the result to @p outp. 1316 * 1317 * @param value The value to be encoded. 1318 * @param[out] outp On success, the value will be written to this 1319 * buffer. This argment may be NULL if the value is 1320 * not desired. 1321 * @param[in,out] olen The capacity of @p outp. On success, will be set 1322 * to the actual size of the requested value. 1323 * @param otype The data type to be written to @p outp. 1324 * 1325 * @retval 0 success 1326 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 1327 * is too small to hold the encoded value. 1328 * @retval EFTYPE If value coercion from @p value to @p otype is 1329 * impossible. 1330 * @retval ERANGE If value coercion would overflow (or underflow) the 1331 * a @p otype representation. 1332 */ 1333 int 1334 bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen, 1335 bhnd_nvram_type otype) 1336 { 1337 /* Prefer format implementation */ 1338 if (value->fmt->op_encode != NULL) 1339 return (value->fmt->op_encode(value, outp, olen, otype)); 1340 1341 return (bhnd_nvram_val_generic_encode(value, outp, olen, otype)); 1342 } 1343 1344 /** 1345 * Encode the given @p value's element as @p otype, writing the result to 1346 * @p outp. 1347 * 1348 * @param inp The element to be be encoded. Must be a value 1349 * previously returned by bhnd_nvram_val_next() 1350 * or bhnd_nvram_val_elem(). 1351 * @param ilen The size of @p inp, as returned by 1352 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). 1353 * @param[out] outp On success, the value will be written to this 1354 * buffer. This argment may be NULL if the value is 1355 * not desired. 1356 * @param[in,out] olen The capacity of @p outp. On success, will be set 1357 * to the actual size of the requested value. 1358 * @param otype The data type to be written to @p outp. 1359 * 1360 * @retval 0 success 1361 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 1362 * is too small to hold the encoded value. 1363 * @retval EFTYPE If value coercion from @p value to @p otype is 1364 * impossible. 1365 * @retval ERANGE If value coercion would overflow (or underflow) the 1366 * a @p otype representation. 1367 */ 1368 int 1369 bhnd_nvram_val_encode_elem(bhnd_nvram_val *value, const void *inp, 1370 size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) 1371 { 1372 /* Prefer format implementation */ 1373 if (value->fmt->op_encode_elem != NULL) { 1374 return (value->fmt->op_encode_elem(value, inp, ilen, outp, 1375 olen, otype)); 1376 } 1377 1378 return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp, 1379 olen, otype)); 1380 } 1381 1382 /** 1383 * Return the type, size, and a pointer to the internal representation 1384 * of @p value. 1385 * 1386 * @param value The value to be queried. 1387 * @param[out] olen Size of the returned data, in bytes. 1388 * @param[out] otype Data type. 1389 */ 1390 const void * 1391 bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen, 1392 bhnd_nvram_type *otype) 1393 { 1394 /* Provide type and length */ 1395 *otype = value->data_type; 1396 *olen = value->data_len; 1397 1398 switch (value->data_storage) { 1399 case BHND_NVRAM_VAL_DATA_EXT_ALLOC: 1400 case BHND_NVRAM_VAL_DATA_EXT_STATIC: 1401 case BHND_NVRAM_VAL_DATA_EXT_WEAK: 1402 /* Return a pointer to external storage */ 1403 return (value->data.ptr); 1404 1405 case BHND_NVRAM_VAL_DATA_INLINE: 1406 /* Return a pointer to inline storage */ 1407 return (&value->data); 1408 1409 case BHND_NVRAM_VAL_DATA_NONE: 1410 BHND_NV_PANIC("uninitialized value"); 1411 } 1412 1413 BHND_NV_PANIC("unknown storage type: %d", value->data_storage); 1414 } 1415 1416 /** 1417 * Iterate over all array elements in @p value. 1418 * 1419 * @param value The value to be iterated 1420 * @param prev A value pointer previously returned by 1421 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(), 1422 * or NULL to begin iteration at the first element. 1423 * @param[in,out] olen If @p prev is non-NULL, @p olen must be a 1424 * pointer to the length previously returned by 1425 * bhnd_nvram_val_next() or bhnd_nvram_val_elem(). 1426 * On success, will be set to the next element's 1427 * length, in bytes. 1428 * 1429 * @retval non-NULL A borrowed reference to the element data. 1430 * @retval NULL If the end of the element array is reached. 1431 */ 1432 const void * 1433 bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen) 1434 { 1435 /* Prefer the format implementation */ 1436 if (value->fmt->op_next != NULL) 1437 return (value->fmt->op_next(value, prev, olen)); 1438 1439 return (bhnd_nvram_val_generic_next(value, prev, olen)); 1440 } 1441 1442 /** 1443 * Return the value's data type. 1444 * 1445 * @param value The value to be queried. 1446 */ 1447 bhnd_nvram_type 1448 bhnd_nvram_val_type(bhnd_nvram_val *value) 1449 { 1450 return (value->data_type); 1451 } 1452 1453 /** 1454 * Return value's element data type. 1455 * 1456 * @param value The value to be queried. 1457 */ 1458 bhnd_nvram_type 1459 bhnd_nvram_val_elem_type(bhnd_nvram_val *value) 1460 { 1461 return (bhnd_nvram_base_type(value->data_type)); 1462 } 1463 1464 /** 1465 * Return the total number of elements represented by @p value. 1466 */ 1467 size_t 1468 bhnd_nvram_val_nelem(bhnd_nvram_val *value) 1469 { 1470 const void *bytes; 1471 bhnd_nvram_type type; 1472 size_t nelem, len; 1473 int error; 1474 1475 /* Prefer format implementation */ 1476 if (value->fmt->op_nelem != NULL) 1477 return (value->fmt->op_nelem(value)); 1478 1479 /* 1480 * If a custom op_next() is defined, bhnd_nvram_value_nelem() almost 1481 * certainly cannot produce a valid element count; it assumes a standard 1482 * data format that may not apply when custom iteration is required. 1483 * 1484 * Instead, use bhnd_nvram_val_next() to parse the backing data and 1485 * produce a total count. 1486 */ 1487 if (value->fmt->op_next != NULL) { 1488 const void *next; 1489 1490 next = NULL; 1491 nelem = 0; 1492 while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL) 1493 nelem++; 1494 1495 return (nelem); 1496 } 1497 1498 /* Otherwise, compute the standard element count */ 1499 bytes = bhnd_nvram_val_bytes(value, &len, &type); 1500 if ((error = bhnd_nvram_value_nelem(bytes, len, type, &nelem))) { 1501 /* Should always succeed */ 1502 BHND_NV_PANIC("error calculating element count for type '%s' " 1503 "with length %zu: %d\n", bhnd_nvram_type_name(type), len, 1504 error); 1505 } 1506 1507 return (nelem); 1508 } 1509 1510 /** 1511 * Generic implementation of bhnd_nvram_val_op_encode(), compatible with 1512 * all supported NVRAM data types. 1513 */ 1514 int 1515 bhnd_nvram_val_generic_encode(bhnd_nvram_val *value, void *outp, size_t *olen, 1516 bhnd_nvram_type otype) 1517 { 1518 const void *inp; 1519 bhnd_nvram_type itype; 1520 size_t ilen; 1521 const void *next; 1522 bhnd_nvram_type otype_base; 1523 size_t limit, nelem, nbytes; 1524 size_t next_len; 1525 int error; 1526 1527 nbytes = 0; 1528 nelem = 0; 1529 otype_base = bhnd_nvram_base_type(otype); 1530 inp = bhnd_nvram_val_bytes(value, &ilen, &itype); 1531 1532 /* 1533 * Normally, an array type is not universally representable as 1534 * non-array type. 1535 * 1536 * As exceptions, we support conversion directly to/from: 1537 * - CHAR_ARRAY/STRING: 1538 * ->STRING Interpret the character array as a 1539 * non-NUL-terminated string. 1540 * ->CHAR_ARRAY Trim the trailing NUL from the string. 1541 */ 1542 #define BHND_NV_IS_ISO_CONV(_lhs, _rhs) \ 1543 ((itype == BHND_NVRAM_TYPE_ ## _lhs && \ 1544 otype == BHND_NVRAM_TYPE_ ## _rhs) || \ 1545 (itype == BHND_NVRAM_TYPE_ ## _rhs && \ 1546 otype == BHND_NVRAM_TYPE_ ## _lhs)) 1547 1548 if (BHND_NV_IS_ISO_CONV(CHAR_ARRAY, STRING)) { 1549 return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, 1550 otype)); 1551 } 1552 1553 #undef BHND_NV_IS_ISO_CONV 1554 1555 /* 1556 * If both input and output are non-array types, try to encode them 1557 * without performing element iteration. 1558 */ 1559 if (!bhnd_nvram_is_array_type(itype) && 1560 !bhnd_nvram_is_array_type(otype)) 1561 { 1562 return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen, 1563 otype)); 1564 } 1565 1566 /* Determine output byte limit */ 1567 if (outp != NULL) 1568 limit = *olen; 1569 else 1570 limit = 0; 1571 1572 /* Iterate over our array elements and encode as the requested 1573 * type */ 1574 next = NULL; 1575 while ((next = bhnd_nvram_val_next(value, next, &next_len))) { 1576 void *elem_outp; 1577 size_t elem_nbytes; 1578 1579 /* If the output type is not an array type, we can only encode 1580 * one element */ 1581 nelem++; 1582 if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) { 1583 return (EFTYPE); 1584 } 1585 1586 /* Determine output offset / limit */ 1587 if (nbytes >= limit) { 1588 elem_nbytes = 0; 1589 elem_outp = NULL; 1590 } else { 1591 elem_nbytes = limit - nbytes; 1592 elem_outp = (uint8_t *)outp + nbytes; 1593 } 1594 1595 /* Attempt encode */ 1596 error = bhnd_nvram_val_encode_elem(value, next, next_len, 1597 elem_outp, &elem_nbytes, otype_base); 1598 1599 /* If encoding failed for any reason other than ENOMEM (which 1600 * we'll detect and report below), return immediately */ 1601 if (error && error != ENOMEM) 1602 return (error); 1603 1604 /* Add to total length */ 1605 if (SIZE_MAX - nbytes < elem_nbytes) 1606 return (EFTYPE); /* would overflow size_t */ 1607 1608 nbytes += elem_nbytes; 1609 } 1610 1611 /* Provide the actual length */ 1612 *olen = nbytes; 1613 1614 /* If no output was requested, nothing left to do */ 1615 if (outp == NULL) 1616 return (0); 1617 1618 /* Otherwise, report a memory error if the output buffer was too 1619 * small */ 1620 if (limit < nbytes) 1621 return (ENOMEM); 1622 1623 return (0); 1624 } 1625 1626 /** 1627 * Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with 1628 * all supported NVRAM data types. 1629 */ 1630 int 1631 bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val *value, const void *inp, 1632 size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype) 1633 { 1634 bhnd_nvram_type itype; 1635 1636 itype = bhnd_nvram_val_elem_type(value); 1637 switch (itype) { 1638 case BHND_NVRAM_TYPE_NULL: 1639 return (bhnd_nvram_val_encode_null(inp, ilen, itype, outp, olen, 1640 otype)); 1641 1642 case BHND_NVRAM_TYPE_DATA: 1643 return (bhnd_nvram_val_encode_data(inp, ilen, itype, outp, 1644 olen, otype)); 1645 1646 case BHND_NVRAM_TYPE_STRING: 1647 case BHND_NVRAM_TYPE_CHAR: 1648 return (bhnd_nvram_val_encode_string(inp, ilen, itype, outp, 1649 olen, otype)); 1650 1651 case BHND_NVRAM_TYPE_BOOL: 1652 return (bhnd_nvram_val_encode_bool(inp, ilen, itype, outp, olen, 1653 otype)); 1654 1655 case BHND_NVRAM_TYPE_UINT8: 1656 case BHND_NVRAM_TYPE_UINT16: 1657 case BHND_NVRAM_TYPE_UINT32: 1658 case BHND_NVRAM_TYPE_UINT64: 1659 case BHND_NVRAM_TYPE_INT8: 1660 case BHND_NVRAM_TYPE_INT16: 1661 case BHND_NVRAM_TYPE_INT32: 1662 case BHND_NVRAM_TYPE_INT64: 1663 return (bhnd_nvram_val_encode_int(inp, ilen, itype, outp, olen, 1664 otype)); 1665 default: 1666 BHND_NV_PANIC("missing encode_elem() implementation"); 1667 } 1668 } 1669 1670 /** 1671 * Generic implementation of bhnd_nvram_val_op_next(), compatible with 1672 * all supported NVRAM data types. 1673 */ 1674 const void * 1675 bhnd_nvram_val_generic_next(bhnd_nvram_val *value, const void *prev, 1676 size_t *olen) 1677 { 1678 const uint8_t *inp; 1679 bhnd_nvram_type itype; 1680 size_t ilen; 1681 1682 /* Iterate over the backing representation */ 1683 inp = bhnd_nvram_val_bytes(value, &ilen, &itype); 1684 return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, olen)); 1685 } 1686 1687 /** 1688 * Initialize the representation of @p value with @p ptr. 1689 * 1690 * @param value The value to be initialized. 1691 * @param inp The external representation. 1692 * @param ilen The external representation length, in bytes. 1693 * @param itype The external representation's data type. 1694 * @param flags Value flags. 1695 * 1696 * @retval 0 success. 1697 * @retval ENOMEM if allocation fails 1698 * @retval EFTYPE if @p itype is not an array type, and @p ilen is not 1699 * equal to the size of a single element of @p itype. 1700 * @retval EFAULT if @p ilen is not correctly aligned for elements of 1701 * @p itype. 1702 */ 1703 static int 1704 bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen, 1705 bhnd_nvram_type itype, uint32_t flags) 1706 { 1707 void *bytes; 1708 int error; 1709 1710 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1711 1712 /* Validate alignment */ 1713 if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) 1714 return (error); 1715 1716 /* Reference the external data */ 1717 if ((flags & BHND_NVRAM_VAL_BORROW_DATA) || 1718 (flags & BHND_NVRAM_VAL_STATIC_DATA)) 1719 { 1720 if (flags & BHND_NVRAM_VAL_STATIC_DATA) 1721 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC; 1722 else 1723 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK; 1724 1725 value->data.ptr = inp; 1726 value->data_type = itype; 1727 value->data_len = ilen; 1728 return (0); 1729 } 1730 1731 /* Fetch reference to (or allocate) an appropriately sized buffer */ 1732 bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags); 1733 if (bytes == NULL) 1734 return (ENOMEM); 1735 1736 /* Copy data */ 1737 memcpy(bytes, inp, ilen); 1738 1739 return (0); 1740 } 1741 1742 /** 1743 * Initialize the internal inline representation of @p value with a copy of 1744 * the data referenced by @p inp of @p itype. 1745 * 1746 * If @p inp is NULL, @p itype and @p ilen will be validated, but no data will 1747 * be copied. 1748 * 1749 * @param value The value to be initialized. 1750 * @param inp The input data to be copied, or NULL to verify 1751 * that data of @p ilen and @p itype can be represented 1752 * inline. 1753 * @param ilen The size of the external buffer to be allocated. 1754 * @param itype The type of the external buffer to be allocated. 1755 * 1756 * @retval 0 success 1757 * @retval ENOMEM if @p ilen is too large to be represented inline. 1758 * @retval EFAULT if @p ilen is not correctly aligned for elements of 1759 * @p itype. 1760 */ 1761 static int 1762 bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen, 1763 bhnd_nvram_type itype) 1764 { 1765 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1766 1767 #define NV_STORE_INIT_INLINE() do { \ 1768 value->data_len = ilen; \ 1769 value->data_type = itype; \ 1770 } while(0) 1771 1772 #define NV_STORE_INLINE(_type, _dest) do { \ 1773 if (ilen != sizeof(_type)) \ 1774 return (EFAULT); \ 1775 \ 1776 if (inp != NULL) { \ 1777 value->data._dest[0] = *(const _type *)inp; \ 1778 NV_STORE_INIT_INLINE(); \ 1779 } \ 1780 } while (0) 1781 1782 #define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \ 1783 if (ilen % sizeof(_type) != 0) \ 1784 return (EFAULT); \ 1785 \ 1786 if (ilen > nitems(value->data. _dest)) \ 1787 return (ENOMEM); \ 1788 \ 1789 if (inp == NULL) \ 1790 return (0); \ 1791 \ 1792 memcpy(&value->data._dest, inp, ilen); \ 1793 if (inp != NULL) { \ 1794 memcpy(&value->data._dest, inp, ilen); \ 1795 NV_STORE_INIT_INLINE(); \ 1796 } \ 1797 } while (0) 1798 1799 /* Attempt to copy to inline storage */ 1800 switch (itype) { 1801 case BHND_NVRAM_TYPE_NULL: 1802 if (ilen != 0) 1803 return (EFAULT); 1804 1805 /* Nothing to copy */ 1806 NV_STORE_INIT_INLINE(); 1807 return (0); 1808 1809 case BHND_NVRAM_TYPE_CHAR: 1810 NV_STORE_INLINE(uint8_t, ch); 1811 return (0); 1812 1813 case BHND_NVRAM_TYPE_BOOL: 1814 NV_STORE_INLINE(bhnd_nvram_bool_t, b); 1815 return(0); 1816 1817 case BHND_NVRAM_TYPE_UINT8: 1818 case BHND_NVRAM_TYPE_INT8: 1819 NV_STORE_INLINE(uint8_t, u8); 1820 return (0); 1821 1822 case BHND_NVRAM_TYPE_UINT16: 1823 case BHND_NVRAM_TYPE_INT16: 1824 NV_STORE_INLINE(uint16_t, u16); 1825 return (0); 1826 1827 case BHND_NVRAM_TYPE_UINT32: 1828 case BHND_NVRAM_TYPE_INT32: 1829 NV_STORE_INLINE(uint32_t, u32); 1830 return (0); 1831 1832 case BHND_NVRAM_TYPE_UINT64: 1833 case BHND_NVRAM_TYPE_INT64: 1834 NV_STORE_INLINE(uint32_t, u32); 1835 return (0); 1836 1837 case BHND_NVRAM_TYPE_CHAR_ARRAY: 1838 NV_COPY_ARRRAY_INLINE(uint8_t, ch); 1839 return (0); 1840 1841 case BHND_NVRAM_TYPE_DATA: 1842 case BHND_NVRAM_TYPE_UINT8_ARRAY: 1843 case BHND_NVRAM_TYPE_INT8_ARRAY: 1844 NV_COPY_ARRRAY_INLINE(uint8_t, u8); 1845 return (0); 1846 1847 case BHND_NVRAM_TYPE_UINT16_ARRAY: 1848 case BHND_NVRAM_TYPE_INT16_ARRAY: 1849 NV_COPY_ARRRAY_INLINE(uint16_t, u16); 1850 return (0); 1851 1852 case BHND_NVRAM_TYPE_UINT32_ARRAY: 1853 case BHND_NVRAM_TYPE_INT32_ARRAY: 1854 NV_COPY_ARRRAY_INLINE(uint32_t, u32); 1855 return (0); 1856 1857 case BHND_NVRAM_TYPE_UINT64_ARRAY: 1858 case BHND_NVRAM_TYPE_INT64_ARRAY: 1859 NV_COPY_ARRRAY_INLINE(uint64_t, u64); 1860 return (0); 1861 1862 case BHND_NVRAM_TYPE_BOOL_ARRAY: 1863 NV_COPY_ARRRAY_INLINE(bhnd_nvram_bool_t, b); 1864 return(0); 1865 1866 case BHND_NVRAM_TYPE_STRING: 1867 case BHND_NVRAM_TYPE_STRING_ARRAY: 1868 if (ilen > sizeof(value->data.ch)) 1869 return (ENOMEM); 1870 1871 if (inp != NULL) { 1872 memcpy(&value->data.ch, inp, ilen); 1873 NV_STORE_INIT_INLINE(); 1874 } 1875 1876 return (0); 1877 } 1878 1879 #undef NV_STORE_INIT_INLINE 1880 #undef NV_STORE_INLINE 1881 #undef NV_COPY_ARRRAY_INLINE 1882 1883 BHND_NV_PANIC("unknown data type %d", itype); 1884 } 1885 1886 /** 1887 * Initialize the internal representation of @p value with a buffer allocation 1888 * of @p len and @p itype, returning a pointer to the allocated buffer. 1889 * 1890 * If a buffer of @p len and @p itype can be represented inline, no 1891 * external buffer will be allocated, and instead a pointer to the inline 1892 * data representation will be returned. 1893 * 1894 * @param value The value to be initialized. 1895 * @param ilen The size of the external buffer to be allocated. 1896 * @param itype The type of the external buffer to be allocated. 1897 * @param flags Value flags. 1898 * 1899 * @retval non-null The newly allocated buffer. 1900 * @retval NULL If allocation failed. 1901 * @retval NULL If @p value is an externally allocated instance. 1902 */ 1903 static void * 1904 bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen, 1905 bhnd_nvram_type itype, uint32_t flags) 1906 { 1907 void *ptr; 1908 1909 BHND_NVRAM_VAL_ASSERT_EMPTY(value); 1910 1911 /* Can we use inline storage? */ 1912 if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) { 1913 BHND_NV_ASSERT(sizeof(value->data) >= ilen, 1914 ("ilen exceeds inline storage")); 1915 1916 value->data_type = itype; 1917 value->data_len = ilen; 1918 value->data_storage = BHND_NVRAM_VAL_DATA_INLINE; 1919 return (&value->data); 1920 } 1921 1922 /* Is allocation permitted? */ 1923 if (!(flags & BHND_NVRAM_VAL_DYNAMIC)) 1924 return (NULL); 1925 1926 /* Allocate external storage */ 1927 if ((ptr = bhnd_nv_malloc(ilen)) == NULL) 1928 return (NULL); 1929 1930 value->data.ptr = ptr; 1931 value->data_len = ilen; 1932 value->data_type = itype; 1933 value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC; 1934 1935 return (ptr); 1936 } 1937