]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c
Merge compiler-rt trunk r321017 to contrib/compiler-rt.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_value_subr.c
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 /**
370  * Format a string representation of @p inp using @p fmt, with, writing the
371  * result to @p outp.
372  *
373  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
374  *
375  * @param               fmt     The format string.
376  * @param               inp     The value to be formatted.
377  * @param               ilen    The size of @p inp, in bytes.
378  * @param               itype   The type of @p inp.
379  * @param[out]          outp    On success, the string value will be written to
380  *                              this buffer. This argment may be NULL if the
381  *                              value is not desired.
382  * @param[in,out]       olen    The capacity of @p outp. On success, will be set
383  *                              to the actual size of the formatted string.
384  *
385  * @retval 0            success
386  * @retval EINVAL       If @p fmt contains unrecognized format string
387  *                      specifiers.
388  * @retval ENOMEM       If the @p outp is non-NULL, and the provided @p olen
389  *                      is too small to hold the encoded value.
390  * @retval EFTYPE       If value coercion from @p inp to a string value via
391  *                      @p fmt is unsupported.
392  * @retval ERANGE       If value coercion of @p value would overflow (or
393  *                      underflow) the representation defined by @p fmt.
394  */
395 int
396 bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen,
397     bhnd_nvram_type itype, char *outp, size_t *olen, ...)
398 {
399         va_list ap;
400         int     error;
401
402         va_start(ap, olen);
403         error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap);
404         va_end(ap);
405
406         return (error);
407 }
408
409 /**
410  * Format a string representation of @p inp using @p fmt, with, writing the
411  * result to @p outp.
412  *
413  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
414  *
415  * @param               fmt     The format string.
416  * @param               inp     The value to be formatted.
417  * @param               ilen    The size of @p inp, in bytes.
418  * @param               itype   The type of @p inp.
419  * @param[out]          outp    On success, the string value will be written to
420  *                              this buffer. This argment may be NULL if the
421  *                              value is not desired.
422  * @param[in,out]       olen    The capacity of @p outp. On success, will be set
423  *                              to the actual size of the formatted string.
424  * @param               ap      Argument list.
425  *
426  * @retval 0            success
427  * @retval EINVAL       If @p fmt contains unrecognized format string
428  *                      specifiers.
429  * @retval ENOMEM       If the @p outp is non-NULL, and the provided @p olen
430  *                      is too small to hold the encoded value.
431  * @retval EFTYPE       If value coercion from @p inp to a string value via
432  *                      @p fmt is unsupported.
433  * @retval ERANGE       If value coercion of @p value would overflow (or
434  *                      underflow) the representation defined by @p fmt.
435  */
436 int
437 bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen,
438     bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap)
439 {
440         bhnd_nvram_val  val;
441         int             error;
442
443         /* Map input buffer as a value instance */
444         error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
445             BHND_NVRAM_VAL_BORROW_DATA);
446         if (error)
447                 return (error);
448
449         /* Attempt to format the value */
450         error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap);
451
452         /* Clean up */
453         bhnd_nvram_val_release(&val);
454         return (error);
455 }
456
457 /**
458  * Iterate over all elements in @p inp.
459  *
460  * @param               inp     The value to be iterated.
461  * @param               ilen    The size, in bytes, of @p inp.
462  * @param               itype   The data type of @p inp.
463  * @param               prev    The value previously returned by
464  *                              bhnd_nvram_value_array_next(), or NULL to begin
465  *                              iteration.
466  * @param[in,out]       olen    If @p prev is non-NULL, @p olen must be a
467  *                              pointer to the length previously returned by
468  *                              bhnd_nvram_value_array_next(). On success, will
469  *                              be set to the next element's length, in bytes.
470  *
471  * @retval non-NULL     A borrowed reference to the next element of @p inp.
472  * @retval NULL         If the end of the array is reached.
473  */
474 const void *
475 bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype,
476     const void *prev, size_t *olen)
477 {
478         const u_char    *next;
479         size_t           offset;
480
481         /* Handle first element */
482         if (prev == NULL) {
483                 /* Zero-length array? */
484                 if (ilen == 0)
485                         return (NULL);
486
487                 *olen = bhnd_nvram_value_size(inp, ilen, itype, 1);
488                 return (inp);
489         }
490
491         /* Advance to next element */
492         BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep"));
493         next = (const u_char *)prev + *olen;
494         offset = (size_t)(next - (const u_char *)inp);
495
496         if (offset >= ilen) {
497                 /* Hit end of the array */
498                 return (NULL);
499         }
500
501         /* Determine element size */
502         *olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1);
503         if (ilen - offset < *olen) {
504                 BHND_NV_LOG("short element of type %s -- misaligned "
505                     "representation", bhnd_nvram_type_name(itype));
506                 return (NULL);
507         }
508
509         return (next);
510 }
511
512 /**
513  * Coerce value @p inp of type @p itype to @p otype, writing the
514  * result to @p outp.
515  *
516  * @param               inp     The value to be coerced.
517  * @param               ilen    The size of @p inp, in bytes.
518  * @param               itype   The base data type of @p inp.
519  * @param[out]          outp    On success, the value will be written to this 
520  *                              buffer. This argment may be NULL if the value
521  *                              is not desired.
522  * @param[in,out]       olen    The capacity of @p outp. On success, will be set
523  *                              to the actual size of the requested value.
524  * @param               otype   The data type to be written to @p outp.
525  *
526  * @retval 0            success
527  * @retval ENOMEM       If @p outp is non-NULL and a buffer of @p olen is too
528  *                      small to hold the requested value.
529  * @retval EFTYPE       If the variable data cannot be coerced to @p otype.
530  * @retval ERANGE       If value coercion would overflow @p otype.
531  */
532 int
533 bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype,
534     void *outp, size_t *olen, bhnd_nvram_type otype)
535 {
536         bhnd_nvram_val  val;
537         int             error;
538
539         /* Wrap input buffer in a value instance */
540         error = bhnd_nvram_val_init(&val, NULL, inp, ilen,
541             itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED);
542         if (error)
543                 return (error);
544
545         /* Try to encode as requested type */
546         error = bhnd_nvram_val_encode(&val, outp, olen, otype);
547
548         /* Clean up and return error */
549         bhnd_nvram_val_release(&val);
550         return (error);
551 }