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