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