2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
37 #include <sys/ctype.h>
38 #include <sys/kernel.h>
39 #include <sys/limits.h>
40 #include <sys/malloc.h>
41 #include <sys/systm.h>
43 #include <machine/_inttypes.h>
59 #include "bhnd_nvram_io.h"
60 #include "bhnd_nvram_private.h"
61 #include "bhnd_nvram_value.h"
63 #include "bhnd_nvram_map_data.h"
66 * Common NVRAM/SPROM support, including NVRAM variable map
71 MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");
75 * CRC-8 lookup table used to checksum SPROM and NVRAM data via
78 * Generated with following parameters:
79 * polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
80 * reflected bits: false
83 const uint8_t bhnd_nvram_crc8_tab[] = {
84 0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,
85 0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,
86 0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,
87 0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,
88 0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,
89 0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,
90 0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,
91 0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,
92 0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,
93 0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,
94 0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,
95 0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,
96 0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,
97 0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,
98 0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,
99 0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,
100 0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,
101 0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,
102 0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,
103 0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,
104 0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,
105 0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,
106 0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,
111 * Return a human readable name for @p type.
113 * @param type The type to query.
116 bhnd_nvram_type_name(bhnd_nvram_type type)
119 case BHND_NVRAM_TYPE_UINT8:
121 case BHND_NVRAM_TYPE_UINT16:
123 case BHND_NVRAM_TYPE_UINT32:
125 case BHND_NVRAM_TYPE_UINT64:
127 case BHND_NVRAM_TYPE_CHAR:
129 case BHND_NVRAM_TYPE_INT8:
131 case BHND_NVRAM_TYPE_INT16:
133 case BHND_NVRAM_TYPE_INT32:
135 case BHND_NVRAM_TYPE_INT64:
137 case BHND_NVRAM_TYPE_STRING:
139 case BHND_NVRAM_TYPE_BOOL:
141 case BHND_NVRAM_TYPE_NULL:
143 case BHND_NVRAM_TYPE_DATA:
145 case BHND_NVRAM_TYPE_UINT8_ARRAY:
147 case BHND_NVRAM_TYPE_UINT16_ARRAY:
149 case BHND_NVRAM_TYPE_UINT32_ARRAY:
151 case BHND_NVRAM_TYPE_UINT64_ARRAY:
153 case BHND_NVRAM_TYPE_INT8_ARRAY:
155 case BHND_NVRAM_TYPE_INT16_ARRAY:
157 case BHND_NVRAM_TYPE_INT32_ARRAY:
159 case BHND_NVRAM_TYPE_INT64_ARRAY:
161 case BHND_NVRAM_TYPE_CHAR_ARRAY:
163 case BHND_NVRAM_TYPE_STRING_ARRAY:
165 case BHND_NVRAM_TYPE_BOOL_ARRAY:
170 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
174 * Return true if @p type is a signed integer type, false otherwise.
176 * Will return false for all array types.
178 * @param type The type to query.
181 bhnd_nvram_is_signed_type(bhnd_nvram_type type)
184 case BHND_NVRAM_TYPE_INT8:
185 case BHND_NVRAM_TYPE_INT16:
186 case BHND_NVRAM_TYPE_INT32:
187 case BHND_NVRAM_TYPE_INT64:
188 BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?"));
191 case BHND_NVRAM_TYPE_CHAR:
192 case BHND_NVRAM_TYPE_UINT8:
193 case BHND_NVRAM_TYPE_UINT16:
194 case BHND_NVRAM_TYPE_UINT32:
195 case BHND_NVRAM_TYPE_UINT64:
196 case BHND_NVRAM_TYPE_STRING:
197 case BHND_NVRAM_TYPE_BOOL:
198 case BHND_NVRAM_TYPE_NULL:
199 case BHND_NVRAM_TYPE_DATA:
200 case BHND_NVRAM_TYPE_UINT8_ARRAY:
201 case BHND_NVRAM_TYPE_UINT16_ARRAY:
202 case BHND_NVRAM_TYPE_UINT32_ARRAY:
203 case BHND_NVRAM_TYPE_UINT64_ARRAY:
204 case BHND_NVRAM_TYPE_INT8_ARRAY:
205 case BHND_NVRAM_TYPE_INT16_ARRAY:
206 case BHND_NVRAM_TYPE_INT32_ARRAY:
207 case BHND_NVRAM_TYPE_INT64_ARRAY:
208 case BHND_NVRAM_TYPE_CHAR_ARRAY:
209 case BHND_NVRAM_TYPE_STRING_ARRAY:
210 case BHND_NVRAM_TYPE_BOOL_ARRAY:
215 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
219 * Return true if @p type is an unsigned integer type, false otherwise.
221 * @param type The type to query.
223 * @return Will return false for all array types.
224 * @return Will return true for BHND_NVRAM_TYPE_CHAR.
227 bhnd_nvram_is_unsigned_type(bhnd_nvram_type type)
229 /* If an integer type, must be either signed or unsigned */
230 if (!bhnd_nvram_is_int_type(type))
233 return (!bhnd_nvram_is_signed_type(type));
237 * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type()
238 * returns true for @p type.
240 * @param type The type to query.
243 bhnd_nvram_is_int_type(bhnd_nvram_type type)
246 case BHND_NVRAM_TYPE_UINT8:
247 case BHND_NVRAM_TYPE_UINT16:
248 case BHND_NVRAM_TYPE_UINT32:
249 case BHND_NVRAM_TYPE_UINT64:
250 case BHND_NVRAM_TYPE_INT8:
251 case BHND_NVRAM_TYPE_INT16:
252 case BHND_NVRAM_TYPE_INT32:
253 case BHND_NVRAM_TYPE_INT64:
256 case BHND_NVRAM_TYPE_CHAR:
257 case BHND_NVRAM_TYPE_STRING:
258 case BHND_NVRAM_TYPE_BOOL:
259 case BHND_NVRAM_TYPE_NULL:
260 case BHND_NVRAM_TYPE_DATA:
261 case BHND_NVRAM_TYPE_UINT8_ARRAY:
262 case BHND_NVRAM_TYPE_UINT16_ARRAY:
263 case BHND_NVRAM_TYPE_UINT32_ARRAY:
264 case BHND_NVRAM_TYPE_UINT64_ARRAY:
265 case BHND_NVRAM_TYPE_INT8_ARRAY:
266 case BHND_NVRAM_TYPE_INT16_ARRAY:
267 case BHND_NVRAM_TYPE_INT32_ARRAY:
268 case BHND_NVRAM_TYPE_INT64_ARRAY:
269 case BHND_NVRAM_TYPE_CHAR_ARRAY:
270 case BHND_NVRAM_TYPE_STRING_ARRAY:
271 case BHND_NVRAM_TYPE_BOOL_ARRAY:
276 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
280 * Return true if @p type is an array type, false otherwise.
282 * @param type The type to query.
285 bhnd_nvram_is_array_type(bhnd_nvram_type type)
288 case BHND_NVRAM_TYPE_UINT8:
289 case BHND_NVRAM_TYPE_UINT16:
290 case BHND_NVRAM_TYPE_UINT32:
291 case BHND_NVRAM_TYPE_UINT64:
292 case BHND_NVRAM_TYPE_INT8:
293 case BHND_NVRAM_TYPE_INT16:
294 case BHND_NVRAM_TYPE_INT32:
295 case BHND_NVRAM_TYPE_INT64:
296 case BHND_NVRAM_TYPE_CHAR:
297 case BHND_NVRAM_TYPE_STRING:
298 case BHND_NVRAM_TYPE_BOOL:
299 case BHND_NVRAM_TYPE_NULL:
300 case BHND_NVRAM_TYPE_DATA:
303 case BHND_NVRAM_TYPE_UINT8_ARRAY:
304 case BHND_NVRAM_TYPE_UINT16_ARRAY:
305 case BHND_NVRAM_TYPE_UINT32_ARRAY:
306 case BHND_NVRAM_TYPE_UINT64_ARRAY:
307 case BHND_NVRAM_TYPE_INT8_ARRAY:
308 case BHND_NVRAM_TYPE_INT16_ARRAY:
309 case BHND_NVRAM_TYPE_INT32_ARRAY:
310 case BHND_NVRAM_TYPE_INT64_ARRAY:
311 case BHND_NVRAM_TYPE_CHAR_ARRAY:
312 case BHND_NVRAM_TYPE_STRING_ARRAY:
313 case BHND_NVRAM_TYPE_BOOL_ARRAY:
318 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
322 * If @p type is an array type, return the base element type. Otherwise,
325 * @param type The type to query.
328 bhnd_nvram_base_type(bhnd_nvram_type type)
331 case BHND_NVRAM_TYPE_UINT8:
332 case BHND_NVRAM_TYPE_UINT16:
333 case BHND_NVRAM_TYPE_UINT32:
334 case BHND_NVRAM_TYPE_UINT64:
335 case BHND_NVRAM_TYPE_INT8:
336 case BHND_NVRAM_TYPE_INT16:
337 case BHND_NVRAM_TYPE_INT32:
338 case BHND_NVRAM_TYPE_INT64:
339 case BHND_NVRAM_TYPE_CHAR:
340 case BHND_NVRAM_TYPE_STRING:
341 case BHND_NVRAM_TYPE_BOOL:
342 case BHND_NVRAM_TYPE_NULL:
343 case BHND_NVRAM_TYPE_DATA:
346 case BHND_NVRAM_TYPE_UINT8_ARRAY: return (BHND_NVRAM_TYPE_UINT8);
347 case BHND_NVRAM_TYPE_UINT16_ARRAY: return (BHND_NVRAM_TYPE_UINT16);
348 case BHND_NVRAM_TYPE_UINT32_ARRAY: return (BHND_NVRAM_TYPE_UINT32);
349 case BHND_NVRAM_TYPE_UINT64_ARRAY: return (BHND_NVRAM_TYPE_UINT64);
350 case BHND_NVRAM_TYPE_INT8_ARRAY: return (BHND_NVRAM_TYPE_INT8);
351 case BHND_NVRAM_TYPE_INT16_ARRAY: return (BHND_NVRAM_TYPE_INT16);
352 case BHND_NVRAM_TYPE_INT32_ARRAY: return (BHND_NVRAM_TYPE_INT32);
353 case BHND_NVRAM_TYPE_INT64_ARRAY: return (BHND_NVRAM_TYPE_INT64);
354 case BHND_NVRAM_TYPE_CHAR_ARRAY: return (BHND_NVRAM_TYPE_CHAR);
355 case BHND_NVRAM_TYPE_STRING_ARRAY: return (BHND_NVRAM_TYPE_STRING);
356 case BHND_NVRAM_TYPE_BOOL_ARRAY: return (BHND_NVRAM_TYPE_BOOL);
360 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
364 * Return the raw data type used to represent values of @p type, or return
365 * @p type is @p type is not a complex type.
367 * @param type The type to query.
370 bhnd_nvram_raw_type(bhnd_nvram_type type)
373 case BHND_NVRAM_TYPE_CHAR:
374 return (BHND_NVRAM_TYPE_UINT8);
376 case BHND_NVRAM_TYPE_CHAR_ARRAY:
377 return (BHND_NVRAM_TYPE_UINT8_ARRAY);
379 case BHND_NVRAM_TYPE_BOOL: {
380 _Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t),
381 "bhnd_nvram_bool_t must be uint8-representable");
382 return (BHND_NVRAM_TYPE_UINT8);
385 case BHND_NVRAM_TYPE_BOOL_ARRAY:
386 return (BHND_NVRAM_TYPE_UINT8_ARRAY);
388 case BHND_NVRAM_TYPE_DATA:
389 return (BHND_NVRAM_TYPE_UINT8_ARRAY);
391 case BHND_NVRAM_TYPE_STRING:
392 case BHND_NVRAM_TYPE_STRING_ARRAY:
393 return (BHND_NVRAM_TYPE_UINT8_ARRAY);
395 case BHND_NVRAM_TYPE_UINT8:
396 case BHND_NVRAM_TYPE_UINT16:
397 case BHND_NVRAM_TYPE_UINT32:
398 case BHND_NVRAM_TYPE_UINT64:
399 case BHND_NVRAM_TYPE_INT8:
400 case BHND_NVRAM_TYPE_INT16:
401 case BHND_NVRAM_TYPE_INT32:
402 case BHND_NVRAM_TYPE_INT64:
403 case BHND_NVRAM_TYPE_NULL:
404 case BHND_NVRAM_TYPE_UINT8_ARRAY:
405 case BHND_NVRAM_TYPE_UINT16_ARRAY:
406 case BHND_NVRAM_TYPE_UINT32_ARRAY:
407 case BHND_NVRAM_TYPE_UINT64_ARRAY:
408 case BHND_NVRAM_TYPE_INT8_ARRAY:
409 case BHND_NVRAM_TYPE_INT16_ARRAY:
410 case BHND_NVRAM_TYPE_INT32_ARRAY:
411 case BHND_NVRAM_TYPE_INT64_ARRAY:
416 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
420 * Return the size, in bytes, of a single element of @p type, or 0
421 * if @p type is a variable-width type.
423 * @param type The type to query.
426 bhnd_nvram_type_width(bhnd_nvram_type type)
429 case BHND_NVRAM_TYPE_STRING:
430 case BHND_NVRAM_TYPE_STRING_ARRAY:
431 case BHND_NVRAM_TYPE_DATA:
434 case BHND_NVRAM_TYPE_NULL:
437 case BHND_NVRAM_TYPE_BOOL:
438 case BHND_NVRAM_TYPE_BOOL_ARRAY:
439 return (sizeof(bhnd_nvram_bool_t));
441 case BHND_NVRAM_TYPE_CHAR:
442 case BHND_NVRAM_TYPE_CHAR_ARRAY:
443 case BHND_NVRAM_TYPE_UINT8:
444 case BHND_NVRAM_TYPE_UINT8_ARRAY:
445 case BHND_NVRAM_TYPE_INT8:
446 case BHND_NVRAM_TYPE_INT8_ARRAY:
447 return (sizeof(uint8_t));
449 case BHND_NVRAM_TYPE_UINT16:
450 case BHND_NVRAM_TYPE_UINT16_ARRAY:
451 case BHND_NVRAM_TYPE_INT16:
452 case BHND_NVRAM_TYPE_INT16_ARRAY:
453 return (sizeof(uint16_t));
455 case BHND_NVRAM_TYPE_UINT32:
456 case BHND_NVRAM_TYPE_UINT32_ARRAY:
457 case BHND_NVRAM_TYPE_INT32:
458 case BHND_NVRAM_TYPE_INT32_ARRAY:
459 return (sizeof(uint32_t));
461 case BHND_NVRAM_TYPE_UINT64:
462 case BHND_NVRAM_TYPE_UINT64_ARRAY:
463 case BHND_NVRAM_TYPE_INT64:
464 case BHND_NVRAM_TYPE_INT64_ARRAY:
465 return (sizeof(uint64_t));
469 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
473 * Return the native host alignment for values of @p type.
475 * @param type The type to query.
478 bhnd_nvram_type_host_align(bhnd_nvram_type type)
481 case BHND_NVRAM_TYPE_CHAR:
482 case BHND_NVRAM_TYPE_CHAR_ARRAY:
483 case BHND_NVRAM_TYPE_DATA:
484 case BHND_NVRAM_TYPE_STRING:
485 case BHND_NVRAM_TYPE_STRING_ARRAY:
486 return (_Alignof(uint8_t));
487 case BHND_NVRAM_TYPE_BOOL:
488 case BHND_NVRAM_TYPE_BOOL_ARRAY: {
489 _Static_assert(sizeof(bhnd_nvram_bool_t) == sizeof(uint8_t),
490 "bhnd_nvram_bool_t must be uint8-representable");
491 return (_Alignof(uint8_t));
493 case BHND_NVRAM_TYPE_NULL:
495 case BHND_NVRAM_TYPE_UINT8:
496 case BHND_NVRAM_TYPE_UINT8_ARRAY:
497 return (_Alignof(uint8_t));
498 case BHND_NVRAM_TYPE_UINT16:
499 case BHND_NVRAM_TYPE_UINT16_ARRAY:
500 return (_Alignof(uint16_t));
501 case BHND_NVRAM_TYPE_UINT32:
502 case BHND_NVRAM_TYPE_UINT32_ARRAY:
503 return (_Alignof(uint32_t));
504 case BHND_NVRAM_TYPE_UINT64:
505 case BHND_NVRAM_TYPE_UINT64_ARRAY:
506 return (_Alignof(uint64_t));
507 case BHND_NVRAM_TYPE_INT8:
508 case BHND_NVRAM_TYPE_INT8_ARRAY:
509 return (_Alignof(int8_t));
510 case BHND_NVRAM_TYPE_INT16:
511 case BHND_NVRAM_TYPE_INT16_ARRAY:
512 return (_Alignof(int16_t));
513 case BHND_NVRAM_TYPE_INT32:
514 case BHND_NVRAM_TYPE_INT32_ARRAY:
515 return (_Alignof(int32_t));
516 case BHND_NVRAM_TYPE_INT64:
517 case BHND_NVRAM_TYPE_INT64_ARRAY:
518 return (_Alignof(int64_t));
522 BHND_NV_PANIC("bhnd nvram type %u unknown", type);
526 * Iterate over all strings in the @p inp string array (see
527 * BHND_NVRAM_TYPE_STRING_ARRAY).
529 * @param inp The string array to be iterated. This must be a
530 * buffer of one or more NUL-terminated strings.
531 * @param ilen The size, in bytes, of @p inp, including any
532 * terminating NUL character(s).
533 * @param prev The pointer previously returned by
534 * bhnd_nvram_string_array_next(), or NULL to begin
536 * @param[in,out] olen If @p prev is non-NULL, @p olen must be a
537 * pointer to the length previously returned by
538 * bhnd_nvram_string_array_next(). On success, will
539 * be set to the next element's length, in bytes.
541 * @retval non-NULL A reference to the next NUL-terminated string
542 * @retval NULL If the end of the string array is reached.
544 * @see BHND_NVRAM_TYPE_STRING_ARRAY
547 bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev,
550 return (bhnd_nvram_value_array_next(inp, ilen,
551 BHND_NVRAM_TYPE_STRING_ARRAY, prev, olen));
554 /* used by bhnd_nvram_find_vardefn() */
556 bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs)
558 const struct bhnd_nvram_vardefn *r = rhs;
560 return (strcmp((const char *)key, r->name));
564 * Find and return the variable definition for @p varname, if any.
566 * @param varname variable name
568 * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found.
569 * @retval NULL If no definition for @p varname is found.
571 const struct bhnd_nvram_vardefn *
572 bhnd_nvram_find_vardefn(const char *varname)
574 return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns,
575 sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare));
579 * Return the variable ID for a variable definition.
581 * @param defn Variable definition previously returned by
582 * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn().
585 bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn)
588 defn >= bhnd_nvram_vardefns &&
589 defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1],
590 ("invalid variable definition pointer %p", defn));
592 return (defn - bhnd_nvram_vardefns);
596 * Return the variable definition with the given @p id, or NULL
597 * if no such variable ID is defined.
599 * @param id variable ID.
601 * @retval bhnd_nvram_vardefn If a valid definition for @p id is found.
602 * @retval NULL If no definition for @p id is found.
604 const struct bhnd_nvram_vardefn *
605 bhnd_nvram_get_vardefn(size_t id)
607 if (id >= bhnd_nvram_num_vardefns)
610 return (&bhnd_nvram_vardefns[id]);
614 * Validate an NVRAM variable name.
616 * Scans for special characters (path delimiters, value delimiters, path
617 * alias prefixes), returning false if the given name cannot be used
618 * as a relative NVRAM key.
620 * @param name A relative NVRAM variable name to validate.
622 * @retval true If @p name is a valid relative NVRAM key.
623 * @retval false If @p name should not be used as a relative NVRAM key.
626 bhnd_nvram_validate_name(const char *name)
628 /* Reject path-prefixed variable names */
629 if (bhnd_nvram_trim_path_name(name) != name)
632 /* Reject device path alias declarations (devpath[1-9][0-9]*.*\0) */
633 if (strncmp(name, "devpath", strlen("devpath")) == 0) {
637 /* Check for trailing [1-9][0-9]* */
638 p = name + strlen("devpath");
639 strtoul(p, &endp, 10);
644 /* Scan for [^A-Za-z_0-9] */
645 for (const char *p = name; *p != '\0'; p++) {
648 case '0': case '1': case '2': case '3': case '4':
649 case '5': case '6': case '7': case '8': case '9':
655 if (!bhnd_nv_isalpha(*p))
665 * Parses the string in the optionally NUL-terminated @p str to as an integer
666 * value of @p otype, accepting any integer format supported by the standard
669 * - Any leading whitespace in @p str -- as defined by the equivalent of
670 * calling isspace_l() with an ASCII locale -- will be ignored.
671 * - A @p str may be prefixed with a single optional '+' or '-' sign denoting
673 * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
674 * base 16 integer follows.
675 * - An octal @p str may include a '0' prefix, denoting that an octal integer
678 * If a @p base of 0 is specified, the base will be determined according
679 * to the string's initial prefix, as per strtoul()'s documented behavior.
681 * When parsing a base 16 integer to a signed representation, if no explicit
682 * sign prefix is given, the string will be parsed as the raw two's complement
683 * representation of the signed integer value.
685 * @param str The string to be parsed.
686 * @param maxlen The maximum number of bytes to be read in
688 * @param base The input string's base (2-36), or 0.
689 * @param[out] nbytes On success or failure, will be set to the total
690 * number of parsed bytes. If the total number of
691 * bytes is not desired, a NULL pointer may be
693 * @param[out] outp On success, the parsed integer value will be
694 * written to @p outp. This argment may be NULL if
695 * the value is not desired.
696 * @param[in,out] olen The capacity of @p outp. On success, will be set
697 * to the actual size of the requested value.
698 * @param otype The integer type to be parsed.
701 * @retval EINVAL if an invalid @p base is specified.
702 * @retval EINVAL if an unsupported (or non-integer) @p otype is
704 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
705 * small to hold the requested value.
706 * @retval EFTYPE if @p str cannot be parsed as an integer of @p base.
707 * @retval ERANGE If the integer parsed from @p str is too large to be
708 * represented as a value of @p otype.
711 bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base,
712 size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype)
715 uint64_t carry_max, value_max;
717 size_t limit, local_nbytes;
719 bool negative, sign, twos_compl;
721 /* Must be an integer type */
722 if (!bhnd_nvram_is_int_type(otype))
725 /* Determine output byte limit */
731 /* We always need a byte count. If the caller provides a NULL nbytes,
732 * track our position in a stack variable */
734 nbytes = &local_nbytes;
742 /* Validate the specified base */
743 if (base != 0 && !(base >= 2 && base <= 36))
746 /* Skip any leading whitespace */
747 for (; *nbytes < maxlen; (*nbytes)++) {
748 if (!bhnd_nv_isspace(str[*nbytes]))
753 if (*nbytes == maxlen)
756 /* Parse and skip sign */
757 if (str[*nbytes] == '-') {
761 } else if (str[*nbytes] == '+') {
766 /* Truncated after sign character? */
767 if (*nbytes == maxlen)
770 /* Identify (or validate) hex base, skipping 0x/0X prefix */
771 if (base == 16 || base == 0) {
772 /* Check for (and skip) 0x/0X prefix */
773 if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' &&
774 (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X'))
781 /* Truncated after hex prefix? */
782 if (*nbytes == maxlen)
785 /* Differentiate decimal/octal by looking for a leading 0 */
787 if (str[*nbytes] == '0') {
794 /* Only enable twos-compliment signed integer parsing enabled if the
795 * input is base 16, and no explicit sign prefix was provided */
796 if (!sign && base == 16)
801 /* Determine the maximum value representable by the requested type */
803 case BHND_NVRAM_TYPE_CHAR:
804 case BHND_NVRAM_TYPE_UINT8:
805 type_max = (uint64_t)UINT8_MAX;
807 case BHND_NVRAM_TYPE_UINT16:
808 type_max = (uint64_t)UINT16_MAX;
810 case BHND_NVRAM_TYPE_UINT32:
811 type_max = (uint64_t)UINT32_MAX;
813 case BHND_NVRAM_TYPE_UINT64:
814 type_max = (uint64_t)UINT64_MAX;
817 case BHND_NVRAM_TYPE_INT8:
819 type_max = (uint64_t)UINT8_MAX;
821 type_max = -(uint64_t)INT8_MIN;
823 type_max = (uint64_t)INT8_MAX;
826 case BHND_NVRAM_TYPE_INT16:
828 type_max = (uint64_t)UINT16_MAX;
830 type_max = -(uint64_t)INT16_MIN;
832 type_max = (uint64_t)INT16_MAX;
835 case BHND_NVRAM_TYPE_INT32:
837 type_max = (uint64_t)UINT32_MAX;
839 type_max = -(uint64_t)INT32_MIN;
841 type_max = (uint64_t)INT32_MAX;
844 case BHND_NVRAM_TYPE_INT64:
846 type_max = (uint64_t)UINT64_MAX;
848 type_max = -(uint64_t)INT64_MIN;
850 type_max = (uint64_t)INT64_MAX;
854 BHND_NV_LOG("unsupported integer type: %d\n", otype);
858 /* The maximum value after which an additional carry would overflow */
859 value_max = type_max / (uint64_t)base;
861 /* The maximum carry value given a value equal to value_max */
862 carry_max = type_max % (uint64_t)base;
864 /* Consume input until we hit maxlen or a non-digit character */
865 for (; *nbytes < maxlen; (*nbytes)++) {
869 /* Parse carry value */
871 if (bhnd_nv_isdigit(c)) {
873 } else if (bhnd_nv_isxdigit(c)) {
874 if (bhnd_nv_isupper(c))
875 carry = (c - 'A') + 10;
877 carry = (c - 'a') + 10;
879 /* Hit first non-digit character */
883 /* If carry is outside the base, it's not a valid digit
884 * in the current parse context; consider it a non-digit
886 if (carry >= (uint64_t)base)
889 /* Increment count of parsed digits */
892 if (value > value_max) {
893 /* -Any- carry value would overflow */
895 } else if (value == value_max && carry > carry_max) {
896 /* -This- carry value would overflow */
900 value *= (uint64_t)base;
904 /* If we hit a non-digit character before parsing the first digit,
905 * we hit an empty integer string. */
912 /* Provide (and verify) required length */
913 *olen = bhnd_nvram_type_width(otype);
916 else if (limit < *olen)
921 case BHND_NVRAM_TYPE_CHAR:
922 case BHND_NVRAM_TYPE_UINT8:
923 *(uint8_t *)outp = (uint8_t)value;
925 case BHND_NVRAM_TYPE_UINT16:
926 *(uint16_t *)outp = (uint16_t)value;
928 case BHND_NVRAM_TYPE_UINT32:
929 *(uint32_t *)outp = (uint32_t)value;
931 case BHND_NVRAM_TYPE_UINT64:
932 *(uint64_t *)outp = (uint64_t)value;
935 case BHND_NVRAM_TYPE_INT8:
936 *(int8_t *)outp = (int8_t)(int64_t)value;
938 case BHND_NVRAM_TYPE_INT16:
939 *(int16_t *)outp = (int16_t)(int64_t)value;
941 case BHND_NVRAM_TYPE_INT32:
942 *(int32_t *)outp = (int32_t)(int64_t)value;
944 case BHND_NVRAM_TYPE_INT64:
945 *(int64_t *)outp = (int64_t)value;
949 BHND_NV_PANIC("unhandled type %d\n", otype);
956 * Trim leading path (pci/1/1) or path alias (0:) prefix from @p name, if any,
957 * returning a pointer to the start of the relative variable name.
962 * - "dev/pci/foo" -> "foo"
966 * @param name The string to be trimmed.
968 * @return A pointer to the start of the relative variable name in @p name.
971 bhnd_nvram_trim_path_name(const char *name)
975 /* path alias prefix? (0:varname) */
976 if (bhnd_nv_isdigit(*name)) {
977 /* Parse '0...:' alias prefix, if it exists */
978 strtoul(name, &endp, 10);
979 if (endp != name && *endp == ':') {
980 /* Variable name follows 0: prefix */
985 /* device path prefix? (pci/1/1/varname) */
986 if ((endp = strrchr(name, '/')) != NULL) {
987 /* Variable name follows the final path separator '/' */
991 /* variable name is not prefixed */
996 * Parse a 'name=value' string.
998 * @param env The string to be parsed.
999 * @param env_len The length of @p envp.
1000 * @param delim The delimiter used in @p envp. This will generally be '='.
1001 * @param[out] name If not NULL, a pointer to the name string. This argument
1003 * @param[out] name_len On success, the length of the name substring. This
1004 * argument may be NULL.
1005 * @param[out] value On success, a pointer to the value substring. This argument
1007 * @param[out] value_len On success, the length of the value substring. This
1008 * argument may be NULL.
1011 * @retval EINVAL if parsing @p envp fails.
1014 bhnd_nvram_parse_env(const char *env, size_t env_len, char delim,
1015 const char **name, size_t *name_len, const char **value, size_t *value_len)
1020 if ((p = memchr(env, delim, env_len)) == NULL) {
1021 BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim,
1022 BHND_NV_PRINT_WIDTH(env_len), env);
1029 if (name_len != NULL)
1030 *name_len = p - env;
1038 if (value_len != NULL)
1039 *value_len = env_len - (p - env);
1046 * Parse a field value, returning the actual pointer to the first
1047 * non-whitespace character and the total size of the field.
1049 * @param[in,out] inp The field string to parse. Will be updated to point
1050 * at the first non-whitespace character found.
1051 * @param ilen The length of @p inp, in bytes.
1052 * @param delim The field delimiter to search for.
1054 * @return Returns the actual size of the field data.
1057 bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim)
1061 /* Skip any leading whitespace */
1062 for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++)
1067 /* Find the last field character */
1068 for (p = *inp; (size_t)(p - *inp) < ilen; p++) {
1069 if (*p == delim || *p == '\0')
1077 * Parse a field value, returning the actual pointer to the first
1078 * non-whitespace character and the total size of the field, minus
1079 * any trailing whitespace.
1081 * @param[in,out] inp The field string to parse. Will be updated to point
1082 * at the first non-whitespace character found.
1083 * @param ilen The length of the parsed field, in bytes, excluding the
1084 * field elimiter and any trailing whitespace.
1085 * @param delim The field delimiter to search for.
1087 * @return Returns the actual size of the field data.
1090 bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim)
1095 plen = bhnd_nvram_parse_field(inp, ilen, delim);
1097 /* Trim trailing whitespace */
1100 if (!bhnd_nv_isspace(*(sp + plen - 1)))