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