1 /*-
2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29
30 #include <sys/param.h>
31
32 #ifdef _KERNEL
33
34 #include <sys/systm.h>
35
36 #else /* !_KERNEL */
37
38 #include <errno.h>
39 #include <string.h>
40
41 #endif /* _KERNEL */
42
43 #include "bhnd_nvram_private.h"
44 #include "bhnd_nvram_valuevar.h"
45
46 /**
47 * Validate the alignment of a value of @p type.
48 *
49 * @param inp The value data.
50 * @param ilen The value length, in bytes.
51 * @param itype The value type.
52 *
53 * @retval 0 success
54 * @retval EFTYPE if @p type is not an array type, and @p len is not
55 * equal to the size of a single element of @p type.
56 * @retval EFAULT if @p data is not correctly aligned to the required
57 * host alignment.
58 * @retval EFAULT if @p len is not aligned to the @p type width.
59 */
60 int
bhnd_nvram_value_check_aligned(const void * inp,size_t ilen,bhnd_nvram_type itype)61 bhnd_nvram_value_check_aligned(const void *inp, size_t ilen,
62 bhnd_nvram_type itype)
63 {
64 size_t align, width;
65
66 /* As a special case, NULL values have no alignment, but must
67 * always have a length of zero */
68 if (itype == BHND_NVRAM_TYPE_NULL) {
69 if (ilen != 0)
70 return (EFAULT);
71
72 return (0);
73 }
74
75 /* Check pointer alignment against the required host alignment */
76 align = bhnd_nvram_type_host_align(itype);
77 BHND_NV_ASSERT(align != 0, ("invalid zero alignment"));
78 if ((uintptr_t)inp % align != 0)
79 return (EFAULT);
80
81 /* If type is not fixed width, nothing else to check */
82 width = bhnd_nvram_type_width(itype);
83 if (width == 0)
84 return (0);
85
86 /* Length must be aligned to the element width */
87 if (ilen % width != 0)
88 return (EFAULT);
89
90 /* If the type is not an array type, the length must be equal to the
91 * size of a single element of @p type. */
92 if (!bhnd_nvram_is_array_type(itype) && ilen != width)
93 return (EFTYPE);
94
95 return (0);
96 }
97
98 /**
99 * Calculate the number of elements represented by a value of @p ilen bytes
100 * with @p itype.
101 *
102 * @param inp The value data.
103 * @param ilen The value length.
104 * @param itype The value type.
105 * @param[out] nelem On success, the number of elements.
106 *
107 * @retval 0 success
108 * @retval EINVAL if @p inp is NULL and the element count of @p itype
109 * cannot be determined without parsing the value data.
110 * @retval EFTYPE if @p itype is not an array type, and @p ilen is not
111 * equal to the size of a single element of @p itype.
112 * @retval EFAULT if @p ilen is not correctly aligned for elements of
113 * @p itype.
114 */
115 int
bhnd_nvram_value_nelem(const void * inp,size_t ilen,bhnd_nvram_type itype,size_t * nelem)116 bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype,
117 size_t *nelem)
118 {
119 int error;
120
121 BHND_NV_ASSERT(inp != NULL, ("NULL inp"));
122
123 /* Check alignment */
124 if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
125 return (error);
126
127 switch (itype) {
128 case BHND_NVRAM_TYPE_DATA:
129 /* Always exactly one element */
130 *nelem = 1;
131 return (0);
132
133 case BHND_NVRAM_TYPE_NULL:
134 /* Must be zero length */
135 if (ilen != 0)
136 return (EFAULT);
137
138 /* Always exactly one element */
139 *nelem = 1;
140 return (0);
141
142 case BHND_NVRAM_TYPE_STRING:
143 /* Always exactly one element */
144 *nelem = 1;
145 return (0);
146
147 case BHND_NVRAM_TYPE_STRING_ARRAY: {
148 const char *p;
149 size_t nleft;
150
151 /* Iterate over the NUL-terminated strings to calculate
152 * total element count */
153 p = inp;
154 nleft = ilen;
155 *nelem = 0;
156 while (nleft > 0) {
157 size_t slen;
158
159 /* Increment element count */
160 (*nelem)++;
161
162 /* Determine string length */
163 slen = strnlen(p, nleft);
164 nleft -= slen;
165
166 /* Advance input */
167 p += slen;
168
169 /* Account for trailing NUL, if we haven't hit the end
170 * of the input */
171 if (nleft > 0) {
172 nleft--;
173 p++;
174 }
175 }
176
177 return (0);
178 }
179
180 case BHND_NVRAM_TYPE_UINT8_ARRAY:
181 case BHND_NVRAM_TYPE_UINT16_ARRAY:
182 case BHND_NVRAM_TYPE_UINT32_ARRAY:
183 case BHND_NVRAM_TYPE_UINT64_ARRAY:
184 case BHND_NVRAM_TYPE_INT8_ARRAY:
185 case BHND_NVRAM_TYPE_INT16_ARRAY:
186 case BHND_NVRAM_TYPE_INT32_ARRAY:
187 case BHND_NVRAM_TYPE_INT64_ARRAY:
188 case BHND_NVRAM_TYPE_CHAR_ARRAY:
189 case BHND_NVRAM_TYPE_BOOL_ARRAY: {
190 size_t width = bhnd_nvram_type_width(itype);
191 BHND_NV_ASSERT(width != 0, ("invalid width"));
192
193 *nelem = ilen / width;
194 return (0);
195 }
196
197 case BHND_NVRAM_TYPE_INT8:
198 case BHND_NVRAM_TYPE_UINT8:
199 case BHND_NVRAM_TYPE_CHAR:
200 case BHND_NVRAM_TYPE_INT16:
201 case BHND_NVRAM_TYPE_UINT16:
202 case BHND_NVRAM_TYPE_INT32:
203 case BHND_NVRAM_TYPE_UINT32:
204 case BHND_NVRAM_TYPE_INT64:
205 case BHND_NVRAM_TYPE_UINT64:
206 case BHND_NVRAM_TYPE_BOOL:
207 /* Length must be equal to the size of exactly one
208 * element (arrays can represent zero elements -- non-array
209 * types cannot) */
210 if (ilen != bhnd_nvram_type_width(itype))
211 return (EFTYPE);
212 *nelem = 1;
213 return (0);
214 }
215
216 /* Quiesce gcc4.2 */
217 BHND_NV_PANIC("bhnd nvram type %u unknown", itype);
218 }
219
220 /**
221 * Return the size, in bytes, of a value of @p itype with @p nelem elements.
222 *
223 * @param inp The actual data to be queried, or NULL if unknown. If
224 * NULL and the base type is not a fixed width type
225 * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned.
226 * @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL.
227 * @param itype The value type.
228 * @param nelem The number of elements. If @p itype is not an array
229 * type, this value must be 1.
230 *
231 * @retval 0 If @p itype has a variable width, and @p inp is NULL.
232 * @retval 0 If a @p nelem value greater than 1 is provided for a
233 * non-array @p itype.
234 * @retval 0 If a @p nelem value of 0 is provided.
235 * @retval 0 If the result would exceed the maximum value
236 * representable by size_t.
237 * @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL.
238 * @retval non-zero The size, in bytes, of @p itype with @p nelem elements.
239 */
240 size_t
bhnd_nvram_value_size(const void * inp,size_t ilen,bhnd_nvram_type itype,size_t nelem)241 bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype,
242 size_t nelem)
243 {
244 /* If nelem 0, nothing to do */
245 if (nelem == 0)
246 return (0);
247
248 /* Non-array types must have an nelem value of 1 */
249 if (!bhnd_nvram_is_array_type(itype) && nelem != 1)
250 return (0);
251
252 switch (itype) {
253 case BHND_NVRAM_TYPE_UINT8_ARRAY:
254 case BHND_NVRAM_TYPE_UINT16_ARRAY:
255 case BHND_NVRAM_TYPE_UINT32_ARRAY:
256 case BHND_NVRAM_TYPE_UINT64_ARRAY:
257 case BHND_NVRAM_TYPE_INT8_ARRAY:
258 case BHND_NVRAM_TYPE_INT16_ARRAY:
259 case BHND_NVRAM_TYPE_INT32_ARRAY:
260 case BHND_NVRAM_TYPE_INT64_ARRAY:
261 case BHND_NVRAM_TYPE_CHAR_ARRAY:
262 case BHND_NVRAM_TYPE_BOOL_ARRAY:{
263 size_t width;
264
265 width = bhnd_nvram_type_width(itype);
266
267 /* Would nelem * width overflow? */
268 if (SIZE_MAX / nelem < width) {
269 BHND_NV_LOG("cannot represent size %s[%zu]\n",
270 bhnd_nvram_type_name(bhnd_nvram_base_type(itype)),
271 nelem);
272 return (0);
273 }
274
275 return (nelem * width);
276 }
277
278 case BHND_NVRAM_TYPE_STRING_ARRAY: {
279 const char *p;
280 size_t total_size;
281
282 if (inp == NULL)
283 return (0);
284
285 /* Iterate over the NUL-terminated strings to calculate
286 * total byte length */
287 p = inp;
288 total_size = 0;
289 for (size_t i = 0; i < nelem; i++) {
290 size_t elem_size;
291
292 elem_size = strnlen(p, ilen - total_size);
293 p += elem_size;
294
295 /* Check for (and skip) terminating NUL */
296 if (total_size < ilen && *p == '\0') {
297 elem_size++;
298 p++;
299 }
300
301 /* Would total_size + elem_size overflow?
302 *
303 * A memory range larger than SIZE_MAX shouldn't be,
304 * possible, but include the check for completeness */
305 if (SIZE_MAX - total_size < elem_size)
306 return (0);
307
308 total_size += elem_size;
309 }
310
311 return (total_size);
312 }
313
314 case BHND_NVRAM_TYPE_STRING: {
315 size_t size;
316
317 if (inp == NULL)
318 return (0);
319
320 /* Find length */
321 size = strnlen(inp, ilen);
322
323 /* Is there a terminating NUL, or did we just hit the
324 * end of the string input */
325 if (size < ilen)
326 size++;
327
328 return (size);
329 }
330
331 case BHND_NVRAM_TYPE_NULL:
332 return (0);
333
334 case BHND_NVRAM_TYPE_DATA:
335 if (inp == NULL)
336 return (0);
337
338 return (ilen);
339
340 case BHND_NVRAM_TYPE_BOOL:
341 return (sizeof(bhnd_nvram_bool_t));
342
343 case BHND_NVRAM_TYPE_INT8:
344 case BHND_NVRAM_TYPE_UINT8:
345 case BHND_NVRAM_TYPE_CHAR:
346 return (sizeof(uint8_t));
347
348 case BHND_NVRAM_TYPE_INT16:
349 case BHND_NVRAM_TYPE_UINT16:
350 return (sizeof(uint16_t));
351
352 case BHND_NVRAM_TYPE_INT32:
353 case BHND_NVRAM_TYPE_UINT32:
354 return (sizeof(uint32_t));
355
356 case BHND_NVRAM_TYPE_UINT64:
357 case BHND_NVRAM_TYPE_INT64:
358 return (sizeof(uint64_t));
359 }
360
361 /* Quiesce gcc4.2 */
362 BHND_NV_PANIC("bhnd nvram type %u unknown", itype);
363 }
364
365 /**
366 * Format a string representation of @p inp using @p fmt, with, writing the
367 * result to @p outp.
368 *
369 * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
370 *
371 * @param fmt The format string.
372 * @param inp The value to be formatted.
373 * @param ilen The size of @p inp, in bytes.
374 * @param itype The type of @p inp.
375 * @param[out] outp On success, the string value will be written to
376 * this buffer. This argment may be NULL if the
377 * value is not desired.
378 * @param[in,out] olen The capacity of @p outp. On success, will be set
379 * to the actual size of the formatted string.
380 *
381 * @retval 0 success
382 * @retval EINVAL If @p fmt contains unrecognized format string
383 * specifiers.
384 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
385 * is too small to hold the encoded value.
386 * @retval EFTYPE If value coercion from @p inp to a string value via
387 * @p fmt is unsupported.
388 * @retval ERANGE If value coercion of @p value would overflow (or
389 * underflow) the representation defined by @p fmt.
390 */
391 int
bhnd_nvram_value_printf(const char * fmt,const void * inp,size_t ilen,bhnd_nvram_type itype,char * outp,size_t * olen,...)392 bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen,
393 bhnd_nvram_type itype, char *outp, size_t *olen, ...)
394 {
395 va_list ap;
396 int error;
397
398 va_start(ap, olen);
399 error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap);
400 va_end(ap);
401
402 return (error);
403 }
404
405 /**
406 * Format a string representation of @p inp using @p fmt, with, writing the
407 * result to @p outp.
408 *
409 * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
410 *
411 * @param fmt The format string.
412 * @param inp The value to be formatted.
413 * @param ilen The size of @p inp, in bytes.
414 * @param itype The type of @p inp.
415 * @param[out] outp On success, the string value will be written to
416 * this buffer. This argment may be NULL if the
417 * value is not desired.
418 * @param[in,out] olen The capacity of @p outp. On success, will be set
419 * to the actual size of the formatted string.
420 * @param ap Argument list.
421 *
422 * @retval 0 success
423 * @retval EINVAL If @p fmt contains unrecognized format string
424 * specifiers.
425 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
426 * is too small to hold the encoded value.
427 * @retval EFTYPE If value coercion from @p inp to a string value via
428 * @p fmt is unsupported.
429 * @retval ERANGE If value coercion of @p value would overflow (or
430 * underflow) the representation defined by @p fmt.
431 */
432 int
bhnd_nvram_value_vprintf(const char * fmt,const void * inp,size_t ilen,bhnd_nvram_type itype,char * outp,size_t * olen,va_list ap)433 bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen,
434 bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap)
435 {
436 bhnd_nvram_val val;
437 int error;
438
439 /* Map input buffer as a value instance */
440 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
441 BHND_NVRAM_VAL_BORROW_DATA);
442 if (error)
443 return (error);
444
445 /* Attempt to format the value */
446 error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap);
447
448 /* Clean up */
449 bhnd_nvram_val_release(&val);
450 return (error);
451 }
452
453 /**
454 * Iterate over all elements in @p inp.
455 *
456 * @param inp The value to be iterated.
457 * @param ilen The size, in bytes, of @p inp.
458 * @param itype The data type of @p inp.
459 * @param prev The value previously returned by
460 * bhnd_nvram_value_array_next(), or NULL to begin
461 * iteration.
462 * @param[in,out] olen If @p prev is non-NULL, @p olen must be a
463 * pointer to the length previously returned by
464 * bhnd_nvram_value_array_next(). On success, will
465 * be set to the next element's length, in bytes.
466 *
467 * @retval non-NULL A borrowed reference to the next element of @p inp.
468 * @retval NULL If the end of the array is reached.
469 */
470 const void *
bhnd_nvram_value_array_next(const void * inp,size_t ilen,bhnd_nvram_type itype,const void * prev,size_t * olen)471 bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype,
472 const void *prev, size_t *olen)
473 {
474 const u_char *next;
475 size_t offset;
476
477 /* Handle first element */
478 if (prev == NULL) {
479 /* Zero-length array? */
480 if (ilen == 0)
481 return (NULL);
482
483 *olen = bhnd_nvram_value_size(inp, ilen, itype, 1);
484 return (inp);
485 }
486
487 /* Advance to next element */
488 BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep"));
489 next = (const u_char *)prev + *olen;
490 offset = (size_t)(next - (const u_char *)inp);
491
492 if (offset >= ilen) {
493 /* Hit end of the array */
494 return (NULL);
495 }
496
497 /* Determine element size */
498 *olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1);
499 if (ilen - offset < *olen) {
500 BHND_NV_LOG("short element of type %s -- misaligned "
501 "representation", bhnd_nvram_type_name(itype));
502 return (NULL);
503 }
504
505 return (next);
506 }
507
508 /**
509 * Coerce value @p inp of type @p itype to @p otype, writing the
510 * result to @p outp.
511 *
512 * @param inp The value to be coerced.
513 * @param ilen The size of @p inp, in bytes.
514 * @param itype The base data type of @p inp.
515 * @param[out] outp On success, the value will be written to this
516 * buffer. This argment may be NULL if the value
517 * is not desired.
518 * @param[in,out] olen The capacity of @p outp. On success, will be set
519 * to the actual size of the requested value.
520 * @param otype The data type to be written to @p outp.
521 *
522 * @retval 0 success
523 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
524 * small to hold the requested value.
525 * @retval EFTYPE If the variable data cannot be coerced to @p otype.
526 * @retval ERANGE If value coercion would overflow @p otype.
527 */
528 int
bhnd_nvram_value_coerce(const void * inp,size_t ilen,bhnd_nvram_type itype,void * outp,size_t * olen,bhnd_nvram_type otype)529 bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype,
530 void *outp, size_t *olen, bhnd_nvram_type otype)
531 {
532 bhnd_nvram_val val;
533 int error;
534
535 /* Wrap input buffer in a value instance */
536 error = bhnd_nvram_val_init(&val, NULL, inp, ilen,
537 itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED);
538 if (error)
539 return (error);
540
541 /* Try to encode as requested type */
542 error = bhnd_nvram_val_encode(&val, outp, olen, otype);
543
544 /* Clean up and return error */
545 bhnd_nvram_val_release(&val);
546 return (error);
547 }
548