diff options
author | Landon J. Fuller <landonf@FreeBSD.org> | 2016-12-19 20:20:33 +0000 |
---|---|---|
committer | Landon J. Fuller <landonf@FreeBSD.org> | 2016-12-19 20:20:33 +0000 |
commit | 9be0790d198726d551148198401dd3d1c35ffb56 (patch) | |
tree | 11366bc21a37f779b56072347fef4d3a0e4f6c9c /sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c | |
parent | eb6861497041fc25ee070562013bf8486dafc087 (diff) | |
download | src-9be0790d198726d551148198401dd3d1c35ffb56.tar.gz src-9be0790d198726d551148198401dd3d1c35ffb56.zip |
bhnd(4): support direct conversion of bhnd_nvram_val
This adds support for bhnd_nvram_val_convert_init() and
bhnd_nvram_val_convert_new(), which may be used to perform value
format-aware encoding of an NVRAM value to a new target format/type.
This will be used to simplify converting to/from serialized
format-specific NVRAM value representations to common external
representations.
Approved by: adrian (mentor)
Differential Revision: https://reviews.freebsd.org/D8757
Notes
Notes:
svn path=/head/; revision=310292
Diffstat (limited to 'sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c')
-rw-r--r-- | sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c new file mode 100644 index 000000000000..8cb7072d89f0 --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c @@ -0,0 +1,513 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#ifdef _KERNEL + +#include <sys/systm.h> + +#else /* !_KERNEL */ + +#include <errno.h> +#include <string.h> + +#endif /* _KERNEL */ + +#include "bhnd_nvram_private.h" +#include "bhnd_nvram_valuevar.h" + +/** + * Validate the alignment of a value of @p type. + * + * @param inp The value data. + * @param ilen The value length, in bytes. + * @param itype The value type. + * + * @retval 0 success + * @retval EFTYPE if @p type is not an array type, and @p len is not + * equal to the size of a single element of @p type. + * @retval EFAULT if @p data is not correctly aligned to the required + * host alignment. + * @retval EFAULT if @p len is not aligned to the @p type width. + */ +int +bhnd_nvram_value_check_aligned(const void *inp, size_t ilen, + bhnd_nvram_type itype) +{ + size_t align, width; + + /* Check pointer alignment against the required host alignment */ + align = bhnd_nvram_type_host_align(itype); + BHND_NV_ASSERT(align != 0, ("invalid zero alignment")); + if ((uintptr_t)inp % align != 0) + return (EFAULT); + + /* If type is not fixed width, nothing else to check */ + width = bhnd_nvram_type_width(itype); + if (width == 0) + return (0); + + /* Length must be aligned to the element width */ + if (ilen % width != 0) + return (EFAULT); + + /* If the type is not an array type, the length must be equal to the + * size of a single element of @p type. */ + if (!bhnd_nvram_is_array_type(itype) && ilen != width) + return (EFTYPE); + + return (0); +} + +/** + * Calculate the number of elements represented by a value of @p ilen bytes + * with @p itype. + * + * @param inp The value data. + * @param ilen The value length. + * @param itype The value type. + * @param[out] nelem On success, the number of elements. + * + * @retval 0 success + * @retval EINVAL if @p inp is NULL and the element count of @p itype + * cannot be determined without parsing the value data. + * @retval EFTYPE if @p itype is not an array type, and @p ilen is not + * equal to the size of a single element of @p itype. + * @retval EFAULT if @p ilen is not correctly aligned for elements of + * @p itype. + */ +int +bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype, + size_t *nelem) +{ + int error; + + BHND_NV_ASSERT(inp != NULL, ("NULL inp")); + + /* Check alignment */ + if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype))) + return (error); + + switch (itype) { + case BHND_NVRAM_TYPE_STRING: + /* Always exactly one element */ + *nelem = 1; + return (0); + + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t nleft; + + /* Iterate over the NUL-terminated strings to calculate + * total element count */ + p = inp; + nleft = ilen; + *nelem = 0; + while (nleft > 0) { + size_t slen; + + /* Increment element count */ + (*nelem)++; + + /* Determine string length */ + slen = strnlen(p, nleft); + nleft -= slen; + + /* Advance input */ + p += slen; + + /* Account for trailing NUL, if we haven't hit the end + * of the input */ + if (nleft > 0) { + nleft--; + p++; + } + } + + return (0); + } + + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: { + size_t width = bhnd_nvram_type_width(itype); + BHND_NV_ASSERT(width != 0, ("invalid width")); + + *nelem = ilen / width; + return (0); + } + + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_CHAR: + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_UINT16: + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_UINT32: + case BHND_NVRAM_TYPE_INT64: + case BHND_NVRAM_TYPE_UINT64: + /* Length must be equal to the size of exactly one + * element (arrays can represent zero elements -- non-array + * types cannot) */ + if (ilen != bhnd_nvram_type_width(itype)) + return (EFTYPE); + *nelem = 1; + return (0); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", itype); +} + +/** + * Return the size, in bytes, of a value of @p itype with @p nelem elements. + * + * @param inp The actual data to be queried, or NULL if unknown. If + * NULL and the base type is not a fixed width type + * (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned. + * @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL. + * @param itype The value type. + * @param nelem The number of elements. If @p itype is not an array + * type, this value must be 1. + * + * @retval 0 If @p itype has a variable width, and @p inp is NULL. + * @retval 0 If a @p nelem value greater than 1 is provided for a + * non-array @p itype. + * @retval 0 If a @p nelem value of 0 is provided. + * @retval 0 If the result would exceed the maximum value + * representable by size_t. + * @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL. + * @retval non-zero The size, in bytes, of @p itype with @p nelem elements. + */ +size_t +bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype, + size_t nelem) +{ + /* If nelem 0, nothing to do */ + if (nelem == 0) + return (0); + + /* Non-array types must have an nelem value of 1 */ + if (!bhnd_nvram_is_array_type(itype) && nelem != 1) + return (0); + + switch (itype) { + case BHND_NVRAM_TYPE_UINT8_ARRAY: + case BHND_NVRAM_TYPE_UINT16_ARRAY: + case BHND_NVRAM_TYPE_UINT32_ARRAY: + case BHND_NVRAM_TYPE_UINT64_ARRAY: + case BHND_NVRAM_TYPE_INT8_ARRAY: + case BHND_NVRAM_TYPE_INT16_ARRAY: + case BHND_NVRAM_TYPE_INT32_ARRAY: + case BHND_NVRAM_TYPE_INT64_ARRAY: + case BHND_NVRAM_TYPE_CHAR_ARRAY: { + size_t width; + + width = bhnd_nvram_type_width(itype); + + /* Would nelem * width overflow? */ + if (SIZE_MAX / nelem < width) { + BHND_NV_LOG("cannot represent size %s[%zu]\n", + bhnd_nvram_type_name(bhnd_nvram_base_type(itype)), + nelem); + return (0); + } + + return (nelem * width); + } + + case BHND_NVRAM_TYPE_STRING_ARRAY: { + const char *p; + size_t total_size; + + if (inp == NULL) + return (0); + + /* Iterate over the NUL-terminated strings to calculate + * total byte length */ + p = inp; + total_size = 0; + for (size_t i = 0; i < nelem; i++) { + size_t elem_size; + + elem_size = strnlen(p, ilen - total_size); + p += elem_size; + + /* Check for (and skip) terminating NUL */ + if (total_size < ilen && *p == '\0') { + elem_size++; + p++; + } + + /* Would total_size + elem_size overflow? + * + * A memory range larger than SIZE_MAX shouldn't be, + * possible, but include the check for completeness */ + if (SIZE_MAX - total_size < elem_size) + return (0); + + total_size += elem_size; + } + + return (total_size); + } + + case BHND_NVRAM_TYPE_STRING: { + size_t size; + + if (inp == NULL) + return (0); + + /* Find length */ + size = strnlen(inp, ilen); + + /* Is there a terminating NUL, or did we just hit the + * end of the string input */ + if (size < ilen) + size++; + + return (size); + } + + case BHND_NVRAM_TYPE_INT8: + case BHND_NVRAM_TYPE_UINT8: + case BHND_NVRAM_TYPE_CHAR: + return (sizeof(uint8_t)); + + case BHND_NVRAM_TYPE_INT16: + case BHND_NVRAM_TYPE_UINT16: + return (sizeof(uint16_t)); + + case BHND_NVRAM_TYPE_INT32: + case BHND_NVRAM_TYPE_UINT32: + return (sizeof(uint32_t)); + + case BHND_NVRAM_TYPE_UINT64: + case BHND_NVRAM_TYPE_INT64: + return (sizeof(uint64_t)); + } + + /* Quiesce gcc4.2 */ + BHND_NV_PANIC("bhnd nvram type %u unknown", itype); +} + + +/** + * Format a string representation of @p inp using @p fmt, with, writing the + * result to @p outp. + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @param fmt The format string. + * @param inp The value to be formatted. + * @param ilen The size of @p inp, in bytes. + * @param itype The type of @p inp. + * @param[out] outp On success, the string value will be written to + * this buffer. This argment may be NULL if the + * value is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the formatted string. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p inp to a string value via + * @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +int +bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen, + bhnd_nvram_type itype, char *outp, size_t *olen, ...) +{ + va_list ap; + int error; + + va_start(ap, olen); + error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap); + va_end(ap); + + return (error); +} + +/** + * Format a string representation of @p inp using @p fmt, with, writing the + * result to @p outp. + * + * Refer to bhnd_nvram_val_vprintf() for full format string documentation. + * + * @param fmt The format string. + * @param inp The value to be formatted. + * @param ilen The size of @p inp, in bytes. + * @param itype The type of @p inp. + * @param[out] outp On success, the string value will be written to + * this buffer. This argment may be NULL if the + * value is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the formatted string. + * @param ap Argument list. + * + * @retval 0 success + * @retval EINVAL If @p fmt contains unrecognized format string + * specifiers. + * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen + * is too small to hold the encoded value. + * @retval EFTYPE If value coercion from @p inp to a string value via + * @p fmt is unsupported. + * @retval ERANGE If value coercion of @p value would overflow (or + * underflow) the representation defined by @p fmt. + */ +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) +{ + bhnd_nvram_val val; + int error; + + /* Map input buffer as a value instance */ + error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, + BHND_NVRAM_VAL_BORROW_DATA); + if (error) + return (error); + + /* Attempt to format the value */ + error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); +} + +/** + * Iterate over all elements in @p inp. + * + * @param inp The value to be iterated. + * @param ilen The size, in bytes, of @p inp. + * @param itype The data type of @p inp. + * @param prev The value previously returned by + * bhnd_nvram_value_array_next(), or NULL to begin + * iteration. + * @param[in,out] olen If @p prev is non-NULL, @p olen must be a + * pointer to the length previously returned by + * bhnd_nvram_value_array_next(). On success, will + * be set to the next element's length, in bytes. + * + * @retval non-NULL A borrowed reference to the next element of @p inp. + * @retval NULL If the end of the array is reached. + */ +const void * +bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype, + const void *prev, size_t *olen) +{ + const u_char *next; + size_t offset; + + /* Handle first element */ + if (prev == NULL) { + /* Zero-length array? */ + if (ilen == 0) + return (NULL); + + *olen = bhnd_nvram_value_size(inp, ilen, itype, 1); + return (inp); + } + + /* Advance to next element */ + BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep")); + next = (const u_char *)prev + *olen; + offset = (size_t)(next - (const u_char *)inp); + + if (offset >= ilen) { + /* Hit end of the array */ + return (NULL); + } + + /* Determine element size */ + *olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1); + if (ilen - offset < *olen) { + BHND_NV_LOG("short element of type %s -- misaligned " + "representation", bhnd_nvram_type_name(itype)); + return (NULL); + } + + return (next); +} + +/** + * Coerce value @p inp of type @p itype to @p otype, writing the + * result to @p outp. + * + * @param inp The value to be coerced. + * @param ilen The size of @p inp, in bytes. + * @param itype The base data type of @p inp. + * @param[out] outp On success, the value will be written to this + * buffer. This argment may be NULL if the value + * is not desired. + * @param[in,out] olen The capacity of @p outp. On success, will be set + * to the actual size of the requested value. + * @param otype The data type to be written to @p outp. + * + * @retval 0 success + * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too + * small to hold the requested value. + * @retval EFTYPE If the variable data cannot be coerced to @p otype. + * @retval ERANGE If value coercion would overflow @p otype. + */ +int +bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + bhnd_nvram_val val; + int error; + + /* Wrap input buffer in a value instance */ + error = bhnd_nvram_val_init(&val, NULL, inp, ilen, + itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED); + if (error) + return (error); + + /* Try to encode as requested type */ + error = bhnd_nvram_val_encode(&val, outp, olen, otype); + + /* Clean up and return error */ + bhnd_nvram_val_release(&val); + return (error); +} |