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