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