/*- * Copyright (c) 2015-2016 Landon Fuller * 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 __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #else /* !_KERNEL */ #include #include #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; /* As a special case, NULL values have no alignment, but must * always have a length of zero */ if (itype == BHND_NVRAM_TYPE_NULL) { if (ilen != 0) return (EFAULT); return (0); } /* 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_DATA: /* Always exactly one element */ *nelem = 1; return (0); case BHND_NVRAM_TYPE_NULL: /* Must be zero length */ if (ilen != 0) return (EFAULT); /* Always exactly one element */ *nelem = 1; return (0); 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: case BHND_NVRAM_TYPE_BOOL_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: case BHND_NVRAM_TYPE_BOOL: /* 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: case BHND_NVRAM_TYPE_BOOL_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_NULL: return (0); case BHND_NVRAM_TYPE_DATA: if (inp == NULL) return (0); return (ilen); case BHND_NVRAM_TYPE_BOOL: return (sizeof(bhnd_nvram_bool_t)); 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); }