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 *
bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt * fmt)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 *
bhnd_nvram_val_default_fmt(bhnd_nvram_type type)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
bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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
bhnd_nvram_val_init_common(bhnd_nvram_val * value,bhnd_nvram_val_storage val_storage,const bhnd_nvram_val_fmt * fmt,const void * inp,size_t ilen,bhnd_nvram_type itype,uint32_t flags)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
bhnd_nvram_val_init(bhnd_nvram_val * value,const bhnd_nvram_val_fmt * fmt,const void * inp,size_t ilen,bhnd_nvram_type itype,uint32_t flags)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
bhnd_nvram_val_new(bhnd_nvram_val ** value,const bhnd_nvram_val_fmt * fmt,const void * inp,size_t ilen,bhnd_nvram_type itype,uint32_t flags)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
bhnd_nvram_val_convert_common(bhnd_nvram_val * value,bhnd_nvram_val_storage val_storage,const bhnd_nvram_val_fmt * fmt,bhnd_nvram_val * src,uint32_t flags)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
bhnd_nvram_val_convert_init(bhnd_nvram_val * value,const bhnd_nvram_val_fmt * fmt,bhnd_nvram_val * src,uint32_t flags)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
bhnd_nvram_val_convert_new(bhnd_nvram_val ** value,const bhnd_nvram_val_fmt * fmt,bhnd_nvram_val * src,uint32_t flags)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 *
bhnd_nvram_val_copy(bhnd_nvram_val * value)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
bhnd_nvram_val_release(bhnd_nvram_val * value)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
bhnd_nvram_val_encode_null(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode_bool(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode_data(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode_string(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode_int(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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 *
bhnd_nvram_val_bytes(bhnd_nvram_val * value,size_t * olen,bhnd_nvram_type * otype)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 *
bhnd_nvram_val_next(bhnd_nvram_val * value,const void * prev,size_t * olen)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
bhnd_nvram_val_type(bhnd_nvram_val * value)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
bhnd_nvram_val_elem_type(bhnd_nvram_val * value)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
bhnd_nvram_val_nelem(bhnd_nvram_val * value)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
bhnd_nvram_val_generic_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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 *
bhnd_nvram_val_generic_next(bhnd_nvram_val * value,const void * prev,size_t * olen)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
bhnd_nvram_val_set(bhnd_nvram_val * value,const void * inp,size_t ilen,bhnd_nvram_type itype,uint32_t flags)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
bhnd_nvram_val_set_inline(bhnd_nvram_val * value,const void * inp,size_t ilen,bhnd_nvram_type itype)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 *
bhnd_nvram_val_alloc_bytes(bhnd_nvram_val * value,size_t ilen,bhnd_nvram_type itype,uint32_t flags)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