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