xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_subr.c (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 
35 #ifdef _KERNEL
36 
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>
42 
43 #include <machine/_inttypes.h>
44 
45 #else /* !_KERNEL */
46 
47 #include <ctype.h>
48 #include <errno.h>
49 #include <inttypes.h>
50 #include <limits.h>
51 #include <stdbool.h>
52 #include <stdio.h>
53 #include <stdint.h>
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #endif /* _KERNEL */
58 
59 #include "bhnd_nvram_io.h"
60 #include "bhnd_nvram_private.h"
61 #include "bhnd_nvram_value.h"
62 
63 #include "bhnd_nvram_map_data.h"
64 
65 /*
66  * Common NVRAM/SPROM support, including NVRAM variable map
67  * lookup.
68  */
69 
70 #ifdef _KERNEL
71 MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");
72 #endif
73 
74 /*
75  * CRC-8 lookup table used to checksum SPROM and NVRAM data via
76  * bhnd_nvram_crc8().
77  *
78  * Generated with following parameters:
79  * 	polynomial:	CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
80  * 	reflected bits:	false
81  * 	reversed:	true
82  */
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,
107 	0x26, 0x68, 0x9f
108 };
109 
110 /**
111  * Return a human readable name for @p type.
112  *
113  * @param type The type to query.
114  */
115 const char *
116 bhnd_nvram_type_name(bhnd_nvram_type type)
117 {
118 	switch (type) {
119 	case BHND_NVRAM_TYPE_UINT8:
120 		return ("uint8");
121 	case BHND_NVRAM_TYPE_UINT16:
122 		return ("uint16");
123 	case BHND_NVRAM_TYPE_UINT32:
124 		return ("uint32");
125 	case BHND_NVRAM_TYPE_UINT64:
126 		return ("uint64");
127 	case BHND_NVRAM_TYPE_CHAR:
128 		return ("char");
129 	case BHND_NVRAM_TYPE_INT8:
130 		return ("int8");
131 	case BHND_NVRAM_TYPE_INT16:
132 		return ("int16");
133 	case BHND_NVRAM_TYPE_INT32:
134 		return ("int32");
135 	case BHND_NVRAM_TYPE_INT64:
136 		return ("int64");
137 	case BHND_NVRAM_TYPE_STRING:
138 		return ("string");
139 	case BHND_NVRAM_TYPE_BOOL:
140 		return ("bool");
141 	case BHND_NVRAM_TYPE_NULL:
142 		return ("null");
143 	case BHND_NVRAM_TYPE_DATA:
144 		return ("data");
145 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
146 		return ("uint8[]");
147 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
148 		return ("uint16[]");
149 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
150 		return ("uint32[]");
151 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
152 		return ("uint64[]");
153 	case BHND_NVRAM_TYPE_INT8_ARRAY:
154 		return ("int8[]");
155 	case BHND_NVRAM_TYPE_INT16_ARRAY:
156 		return ("int16[]");
157 	case BHND_NVRAM_TYPE_INT32_ARRAY:
158 		return ("int32[]");
159 	case BHND_NVRAM_TYPE_INT64_ARRAY:
160 		return ("int64[]");
161 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
162 		return ("char[]");
163 	case BHND_NVRAM_TYPE_STRING_ARRAY:
164 		return ("string[]");
165 	case BHND_NVRAM_TYPE_BOOL_ARRAY:
166 		return ("bool[]");
167 	}
168 
169 	/* Quiesce gcc4.2 */
170 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
171 }
172 
173 /**
174  * Return true if @p type is a signed integer type, false otherwise.
175  *
176  * Will return false for all array types.
177  *
178  * @param type The type to query.
179  */
180 bool
181 bhnd_nvram_is_signed_type(bhnd_nvram_type type)
182 {
183 	switch (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?"));
189 		return (true);
190 
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:
211 		return (false);
212 	}
213 
214 	/* Quiesce gcc4.2 */
215 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
216 }
217 
218 /**
219  * Return true if @p type is an unsigned integer type, false otherwise.
220  *
221  * @param type The type to query.
222  *
223  * @return Will return false for all array types.
224  * @return Will return true for BHND_NVRAM_TYPE_CHAR.
225  */
226 bool
227 bhnd_nvram_is_unsigned_type(bhnd_nvram_type type)
228 {
229 	/* If an integer type, must be either signed or unsigned */
230 	if (!bhnd_nvram_is_int_type(type))
231 		return (false);
232 
233 	return (!bhnd_nvram_is_signed_type(type));
234 }
235 
236 /**
237  * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type()
238  * returns true for @p type.
239  *
240  * @param type The type to query.
241  */
242 bool
243 bhnd_nvram_is_int_type(bhnd_nvram_type type)
244 {
245 	switch (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:
254 		return (true);
255 
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:
272 		return (false);
273 	}
274 
275 	/* Quiesce gcc4.2 */
276 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
277 }
278 
279 /**
280  * Return true if @p type is an array type, false otherwise.
281  *
282  * @param type The type to query.
283  */
284 bool
285 bhnd_nvram_is_array_type(bhnd_nvram_type type)
286 {
287 	switch (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:
301 		return (false);
302 
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:
314 		return (true);
315 	}
316 
317 	/* Quiesce gcc4.2 */
318 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
319 }
320 
321 /**
322  * If @p type is an array type, return the base element type. Otherwise,
323  * returns @p type.
324  *
325  * @param type The type to query.
326  */
327 bhnd_nvram_type
328 bhnd_nvram_base_type(bhnd_nvram_type type)
329 {
330 	switch (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:
344 		return (type);
345 
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);
357 	}
358 
359 	/* Quiesce gcc4.2 */
360 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
361 }
362 
363 /**
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.
366  *
367  * @param type The type to query.
368  */
369 bhnd_nvram_type
370 bhnd_nvram_raw_type(bhnd_nvram_type type)
371 {
372 	switch (type) {
373 	case BHND_NVRAM_TYPE_CHAR:
374 		return (BHND_NVRAM_TYPE_UINT8);
375 
376 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
377 		return (BHND_NVRAM_TYPE_UINT8_ARRAY);
378 
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);
383 	}
384 
385 	case BHND_NVRAM_TYPE_BOOL_ARRAY:
386 		return (BHND_NVRAM_TYPE_UINT8_ARRAY);
387 
388 	case BHND_NVRAM_TYPE_DATA:
389 		return (BHND_NVRAM_TYPE_UINT8_ARRAY);
390 
391 	case BHND_NVRAM_TYPE_STRING:
392 	case BHND_NVRAM_TYPE_STRING_ARRAY:
393 		return (BHND_NVRAM_TYPE_UINT8_ARRAY);
394 
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:
412 		return (type);
413 	}
414 
415 	/* Quiesce gcc4.2 */
416 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
417 }
418 
419 /**
420  * Return the size, in bytes, of a single element of @p type, or 0
421  * if @p type is a variable-width type.
422  *
423  * @param type	The type to query.
424  */
425 size_t
426 bhnd_nvram_type_width(bhnd_nvram_type type)
427 {
428 	switch (type) {
429 	case BHND_NVRAM_TYPE_STRING:
430 	case BHND_NVRAM_TYPE_STRING_ARRAY:
431 	case BHND_NVRAM_TYPE_DATA:
432 		return (0);
433 
434 	case BHND_NVRAM_TYPE_NULL:
435 		return (0);
436 
437 	case BHND_NVRAM_TYPE_BOOL:
438 	case BHND_NVRAM_TYPE_BOOL_ARRAY:
439 		return (sizeof(bhnd_nvram_bool_t));
440 
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));
448 
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));
454 
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));
460 
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));
466 	}
467 
468 	/* Quiesce gcc4.2 */
469 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
470 }
471 
472 /**
473  * Return the native host alignment for values of @p type.
474  *
475  * @param type The type to query.
476  */
477 size_t
478 bhnd_nvram_type_host_align(bhnd_nvram_type type)
479 {
480 	switch (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));
492 	}
493 	case BHND_NVRAM_TYPE_NULL:
494 		return (1);
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));
519 	}
520 
521 	/* Quiesce gcc4.2 */
522 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
523 }
524 
525 /**
526  * Iterate over all strings in the @p inp string array (see
527  * BHND_NVRAM_TYPE_STRING_ARRAY).
528  *
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
535  *				iteration.
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.
540  *
541  * @retval non-NULL	A reference to the next NUL-terminated string
542  * @retval NULL		If the end of the string array is reached.
543  *
544  * @see BHND_NVRAM_TYPE_STRING_ARRAY
545  */
546 const char *
547 bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev,
548     size_t *olen)
549 {
550 	return (bhnd_nvram_value_array_next(inp, ilen,
551 	    BHND_NVRAM_TYPE_STRING_ARRAY, prev, olen));
552 }
553 
554 /* used by bhnd_nvram_find_vardefn() */
555 static int
556 bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs)
557 {
558 	const struct bhnd_nvram_vardefn *r = rhs;
559 
560 	return (strcmp((const char *)key, r->name));
561 }
562 
563 /**
564  * Find and return the variable definition for @p varname, if any.
565  *
566  * @param varname variable name
567  *
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.
570  */
571 const struct bhnd_nvram_vardefn *
572 bhnd_nvram_find_vardefn(const char *varname)
573 {
574 	return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns,
575 	    sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare));
576 }
577 
578 /**
579  * Return the variable ID for a variable definition.
580  *
581  * @param defn Variable definition previously returned by
582  * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn().
583  */
584 size_t
585 bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn)
586 {
587 	BHND_NV_ASSERT(
588 	    defn >= bhnd_nvram_vardefns &&
589 	    defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1],
590 	    ("invalid variable definition pointer %p", defn));
591 
592 	return (defn - bhnd_nvram_vardefns);
593 }
594 
595 /**
596  * Return the variable definition with the given @p id, or NULL
597  * if no such variable ID is defined.
598  *
599  * @param id variable ID.
600  *
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.
603  */
604 const struct bhnd_nvram_vardefn *
605 bhnd_nvram_get_vardefn(size_t id)
606 {
607 	if (id >= bhnd_nvram_num_vardefns)
608 		return (NULL);
609 
610 	return (&bhnd_nvram_vardefns[id]);
611 }
612 
613 /**
614  * Validate an NVRAM variable name.
615  *
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.
619  *
620  * @param name A relative NVRAM variable name to validate.
621  *
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.
624  */
625 bool
626 bhnd_nvram_validate_name(const char *name)
627 {
628 	/* Reject path-prefixed variable names */
629 	if (bhnd_nvram_trim_path_name(name) != name)
630 		return (false);
631 
632 	/* Reject device path alias declarations (devpath[1-9][0-9]*.*\0) */
633 	if (strncmp(name, "devpath", strlen("devpath")) == 0) {
634 		const char	*p;
635 		char		*endp;
636 
637 		/* Check for trailing [1-9][0-9]* */
638 		p = name + strlen("devpath");
639 		strtoul(p, &endp, 10);
640 		if (endp != p)
641 			return (false);
642 	}
643 
644 	/* Scan for [^A-Za-z_0-9] */
645 	for (const char *p = name; *p != '\0'; p++) {
646 		switch (*p) {
647 		/* [0-9_] */
648 		case '0': case '1': case '2': case '3': case '4':
649 		case '5': case '6': case '7': case '8': case '9':
650 		case '_':
651 			break;
652 
653 		/* [A-Za-z] */
654 		default:
655 			if (!bhnd_nv_isalpha(*p))
656 				return (false);
657 			break;
658 		}
659 	}
660 
661 	return (true);
662 }
663 
664 /**
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
667  * strtoul().
668  *
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
672  *   signedness.
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
676  *   follows.
677  *
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.
680  *
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.
684  *
685  * @param		str	The string to be parsed.
686  * @param		maxlen	The maximum number of bytes to be read in
687  *				@p str.
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
692  *				provided.
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.
699  *
700  * @retval 0		success
701  * @retval EINVAL	if an invalid @p base is specified.
702  * @retval EINVAL	if an unsupported (or non-integer) @p otype is
703  *			specified.
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.
709  */
710 int
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)
713 {
714 	uint64_t	value;
715 	uint64_t	carry_max, value_max;
716 	uint64_t	type_max;
717 	size_t		limit, local_nbytes;
718 	size_t		ndigits;
719 	bool		negative, sign, twos_compl;
720 
721 	/* Must be an integer type */
722 	if (!bhnd_nvram_is_int_type(otype))
723 		return (EINVAL);
724 
725 	/* Determine output byte limit */
726 	if (outp != NULL)
727 		limit = *olen;
728 	else
729 		limit = 0;
730 
731 	/* We always need a byte count. If the caller provides a NULL nbytes,
732 	 * track our position in a stack variable */
733 	if (nbytes == NULL)
734 		nbytes = &local_nbytes;
735 
736 	value = 0;
737 	ndigits = 0;
738 	*nbytes = 0;
739 	negative = false;
740 	sign = false;
741 
742 	/* Validate the specified base */
743 	if (base != 0 && !(base >= 2 && base <= 36))
744 		return (EINVAL);
745 
746 	/* Skip any leading whitespace */
747 	for (; *nbytes < maxlen; (*nbytes)++) {
748 		if (!bhnd_nv_isspace(str[*nbytes]))
749 			break;
750 	}
751 
752 	/* Empty string? */
753 	if (*nbytes == maxlen)
754 		return (EFTYPE);
755 
756 	/* Parse and skip sign */
757 	if (str[*nbytes] == '-') {
758 		negative = true;
759 		sign = true;
760 		(*nbytes)++;
761 	} else if (str[*nbytes] == '+') {
762 		sign = true;
763 		(*nbytes)++;
764 	}
765 
766 	/* Truncated after sign character? */
767 	if (*nbytes == maxlen)
768 		return (EFTYPE);
769 
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'))
775 		{
776 			base = 16;
777 			(*nbytes) += 2;
778 		}
779 	}
780 
781 	/* Truncated after hex prefix? */
782 	if (*nbytes == maxlen)
783 		return (EFTYPE);
784 
785 	/* Differentiate decimal/octal by looking for a leading 0 */
786 	if (base == 0) {
787 		if (str[*nbytes] == '0') {
788 			base = 8;
789 		} else {
790 			base = 10;
791 		}
792 	}
793 
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)
797 		twos_compl = true;
798 	else
799 		twos_compl = false;
800 
801 	/* Determine the maximum value representable by the requested type */
802 	switch (otype) {
803 	case BHND_NVRAM_TYPE_CHAR:
804 	case BHND_NVRAM_TYPE_UINT8:
805 		type_max = (uint64_t)UINT8_MAX;
806 		break;
807 	case BHND_NVRAM_TYPE_UINT16:
808 		type_max = (uint64_t)UINT16_MAX;
809 		break;
810 	case BHND_NVRAM_TYPE_UINT32:
811 		type_max = (uint64_t)UINT32_MAX;
812 		break;
813 	case BHND_NVRAM_TYPE_UINT64:
814 		type_max = (uint64_t)UINT64_MAX;
815 		break;
816 
817 	case BHND_NVRAM_TYPE_INT8:
818 		if (twos_compl)
819 			type_max = (uint64_t)UINT8_MAX;
820 		else if (negative)
821 			type_max = -(uint64_t)INT8_MIN;
822 		else
823 			type_max = (uint64_t)INT8_MAX;
824 		break;
825 
826 	case BHND_NVRAM_TYPE_INT16:
827 		if (twos_compl)
828 			type_max = (uint64_t)UINT16_MAX;
829 		else if (negative)
830 			type_max = -(uint64_t)INT16_MIN;
831 		else
832 			type_max = (uint64_t)INT16_MAX;
833 		break;
834 
835 	case BHND_NVRAM_TYPE_INT32:
836 		if (twos_compl)
837 			type_max = (uint64_t)UINT32_MAX;
838 		else if (negative)
839 			type_max = -(uint64_t)INT32_MIN;
840 		else
841 			type_max = (uint64_t)INT32_MAX;
842 		break;
843 
844 	case BHND_NVRAM_TYPE_INT64:
845 		if (twos_compl)
846 			type_max = (uint64_t)UINT64_MAX;
847 		else if (negative)
848 			type_max = -(uint64_t)INT64_MIN;
849 		else
850 			type_max = (uint64_t)INT64_MAX;
851 		break;
852 
853 	default:
854 		BHND_NV_LOG("unsupported integer type: %d\n", otype);
855 		return (EINVAL);
856 	}
857 
858 	/* The maximum value after which an additional carry would overflow */
859 	value_max = type_max / (uint64_t)base;
860 
861 	/* The maximum carry value given a value equal to value_max */
862 	carry_max = type_max % (uint64_t)base;
863 
864 	/* Consume input until we hit maxlen or a non-digit character */
865 	for (; *nbytes < maxlen; (*nbytes)++) {
866 		u_long	carry;
867 		char	c;
868 
869 		/* Parse carry value */
870 		c = str[*nbytes];
871 		if (bhnd_nv_isdigit(c)) {
872 			carry = c - '0';
873 		} else if (bhnd_nv_isxdigit(c)) {
874 			if (bhnd_nv_isupper(c))
875 				carry = (c - 'A') + 10;
876 			else
877 				carry = (c - 'a') + 10;
878 		} else {
879 			/* Hit first non-digit character */
880 			break;
881 		}
882 
883 		/* If carry is outside the base, it's not a valid digit
884 		 * in the current parse context; consider it a non-digit
885 		 * character */
886 		if (carry >= (uint64_t)base)
887 			break;
888 
889 		/* Increment count of parsed digits */
890 		ndigits++;
891 
892 		if (value > value_max) {
893 			/* -Any- carry value would overflow */
894 			return (ERANGE);
895 		} else if (value == value_max && carry > carry_max) {
896 			/* -This- carry value would overflow */
897 			return (ERANGE);
898 		}
899 
900 		value *= (uint64_t)base;
901 		value += carry;
902 	}
903 
904 	/* If we hit a non-digit character before parsing the first digit,
905 	 * we hit an empty integer string. */
906 	if (ndigits == 0)
907 		return (EFTYPE);
908 
909 	if (negative)
910 		value = -value;
911 
912 	/* Provide (and verify) required length */
913 	*olen = bhnd_nvram_type_width(otype);
914 	if (outp == NULL)
915 		return (0);
916 	else if (limit < *olen)
917 		return (ENOMEM);
918 
919 	/* Provide result */
920 	switch (otype) {
921 	case BHND_NVRAM_TYPE_CHAR:
922 	case BHND_NVRAM_TYPE_UINT8:
923 		*(uint8_t *)outp = (uint8_t)value;
924 		break;
925 	case BHND_NVRAM_TYPE_UINT16:
926 		*(uint16_t *)outp = (uint16_t)value;
927 		break;
928 	case BHND_NVRAM_TYPE_UINT32:
929 		*(uint32_t *)outp = (uint32_t)value;
930 		break;
931 	case BHND_NVRAM_TYPE_UINT64:
932 		*(uint64_t *)outp = (uint64_t)value;
933 		break;
934 
935 	case BHND_NVRAM_TYPE_INT8:
936 		*(int8_t *)outp = (int8_t)(int64_t)value;
937 		break;
938 	case BHND_NVRAM_TYPE_INT16:
939 		*(int16_t *)outp = (int16_t)(int64_t)value;
940 		break;
941 	case BHND_NVRAM_TYPE_INT32:
942 		*(int32_t *)outp = (int32_t)(int64_t)value;
943 		break;
944 	case BHND_NVRAM_TYPE_INT64:
945 		*(int64_t *)outp = (int64_t)value;
946 		break;
947 	default:
948 		/* unreachable */
949 		BHND_NV_PANIC("unhandled type %d\n", otype);
950 	}
951 
952 	return (0);
953 }
954 
955 /**
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.
958  *
959  * @par Examples
960  *
961  * - "/foo"		-> "foo"
962  * - "dev/pci/foo"	-> "foo"
963  * - "0:foo"		-> "foo"
964  * - "foo"		-> "foo"
965  *
966  * @param name The string to be trimmed.
967  *
968  * @return A pointer to the start of the relative variable name in @p name.
969  */
970 const char *
971 bhnd_nvram_trim_path_name(const char *name)
972 {
973 	char *endp;
974 
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 */
981 			return (endp+1);
982 		}
983 	}
984 
985 	/* device path prefix? (pci/1/1/varname) */
986 	if ((endp = strrchr(name, '/')) != NULL) {
987 		/* Variable name follows the final path separator '/' */
988 		return (endp+1);
989 	}
990 
991 	/* variable name is not prefixed */
992 	return (name);
993 }
994 
995 /**
996  * Parse a 'name=value' string.
997  *
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
1002  * may be NULL.
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
1006  * may be NULL.
1007  * @param[out] value_len On success, the length of the value substring. This
1008  * argument may be NULL.
1009  *
1010  * @retval 0 success
1011  * @retval EINVAL if parsing @p envp fails.
1012  */
1013 int
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)
1016 {
1017 	const char *p;
1018 
1019 	/* Name */
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);
1023 		return (EINVAL);
1024 	}
1025 
1026 	/* Name */
1027 	if (name != NULL)
1028 		*name = env;
1029 	if (name_len != NULL)
1030 		*name_len = p - env;
1031 
1032 	/* Skip delim */
1033 	p++;
1034 
1035 	/* Value */
1036 	if (value != NULL)
1037 		*value = p;
1038 	if (value_len != NULL)
1039 		*value_len = env_len - (p - env);
1040 
1041 	return (0);
1042 }
1043 
1044 /**
1045  * Parse a field value, returning the actual pointer to the first
1046  * non-whitespace character and the total size of the field.
1047  *
1048  * @param[in,out] inp The field string to parse. Will be updated to point
1049  * at the first non-whitespace character found.
1050  * @param ilen The length of @p inp, in bytes.
1051  * @param delim The field delimiter to search for.
1052  *
1053  * @return Returns the actual size of the field data.
1054  */
1055 size_t
1056 bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim)
1057 {
1058 	const char	*p, *sp;
1059 
1060 	/* Skip any leading whitespace */
1061 	for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++)
1062 		continue;
1063 
1064 	*inp = sp;
1065 
1066 	/* Find the last field character */
1067 	for (p = *inp; (size_t)(p - *inp) < ilen; p++) {
1068 		if (*p == delim || *p == '\0')
1069 			break;
1070 	}
1071 
1072 	return (p - *inp);
1073 }
1074 
1075 /**
1076  * Parse a field value, returning the actual pointer to the first
1077  * non-whitespace character and the total size of the field, minus
1078  * any trailing whitespace.
1079  *
1080  * @param[in,out] inp The field string to parse. Will be updated to point
1081  * at the first non-whitespace character found.
1082  * @param ilen The length of the parsed field, in bytes, excluding the
1083  * field elimiter and any trailing whitespace.
1084  * @param delim The field delimiter to search for.
1085  *
1086  * @return Returns the actual size of the field data.
1087  */
1088 size_t
1089 bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim)
1090 {
1091 	const char	*sp;
1092 	size_t		 plen;
1093 
1094 	plen = bhnd_nvram_parse_field(inp, ilen, delim);
1095 
1096 	/* Trim trailing whitespace */
1097 	sp = *inp;
1098 	while (plen > 0) {
1099 		if (!bhnd_nv_isspace(*(sp + plen - 1)))
1100 			break;
1101 
1102 		plen--;
1103 	}
1104 
1105 	return (plen);
1106 }
1107