]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c
zfs: merge openzfs/zfs@f3678d70f (master) into main
[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  * 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 }