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