Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_value_subr.c
39536 views
/*-1* Copyright (c) 2015-2016 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <sys/param.h>3031#ifdef _KERNEL3233#include <sys/systm.h>3435#else /* !_KERNEL */3637#include <errno.h>38#include <string.h>3940#endif /* _KERNEL */4142#include "bhnd_nvram_private.h"43#include "bhnd_nvram_valuevar.h"4445/**46* Validate the alignment of a value of @p type.47*48* @param inp The value data.49* @param ilen The value length, in bytes.50* @param itype The value type.51*52* @retval 0 success53* @retval EFTYPE if @p type is not an array type, and @p len is not54* equal to the size of a single element of @p type.55* @retval EFAULT if @p data is not correctly aligned to the required56* host alignment.57* @retval EFAULT if @p len is not aligned to the @p type width.58*/59int60bhnd_nvram_value_check_aligned(const void *inp, size_t ilen,61bhnd_nvram_type itype)62{63size_t align, width;6465/* As a special case, NULL values have no alignment, but must66* always have a length of zero */67if (itype == BHND_NVRAM_TYPE_NULL) {68if (ilen != 0)69return (EFAULT);7071return (0);72}7374/* Check pointer alignment against the required host alignment */75align = bhnd_nvram_type_host_align(itype);76BHND_NV_ASSERT(align != 0, ("invalid zero alignment"));77if ((uintptr_t)inp % align != 0)78return (EFAULT);7980/* If type is not fixed width, nothing else to check */81width = bhnd_nvram_type_width(itype);82if (width == 0)83return (0);8485/* Length must be aligned to the element width */86if (ilen % width != 0)87return (EFAULT);8889/* If the type is not an array type, the length must be equal to the90* size of a single element of @p type. */91if (!bhnd_nvram_is_array_type(itype) && ilen != width)92return (EFTYPE);9394return (0);95}9697/**98* Calculate the number of elements represented by a value of @p ilen bytes99* with @p itype.100*101* @param inp The value data.102* @param ilen The value length.103* @param itype The value type.104* @param[out] nelem On success, the number of elements.105*106* @retval 0 success107* @retval EINVAL if @p inp is NULL and the element count of @p itype108* cannot be determined without parsing the value data.109* @retval EFTYPE if @p itype is not an array type, and @p ilen is not110* equal to the size of a single element of @p itype.111* @retval EFAULT if @p ilen is not correctly aligned for elements of112* @p itype.113*/114int115bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype,116size_t *nelem)117{118int error;119120BHND_NV_ASSERT(inp != NULL, ("NULL inp"));121122/* Check alignment */123if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))124return (error);125126switch (itype) {127case BHND_NVRAM_TYPE_DATA:128/* Always exactly one element */129*nelem = 1;130return (0);131132case BHND_NVRAM_TYPE_NULL:133/* Must be zero length */134if (ilen != 0)135return (EFAULT);136137/* Always exactly one element */138*nelem = 1;139return (0);140141case BHND_NVRAM_TYPE_STRING:142/* Always exactly one element */143*nelem = 1;144return (0);145146case BHND_NVRAM_TYPE_STRING_ARRAY: {147const char *p;148size_t nleft;149150/* Iterate over the NUL-terminated strings to calculate151* total element count */152p = inp;153nleft = ilen;154*nelem = 0;155while (nleft > 0) {156size_t slen;157158/* Increment element count */159(*nelem)++;160161/* Determine string length */162slen = strnlen(p, nleft);163nleft -= slen;164165/* Advance input */166p += slen;167168/* Account for trailing NUL, if we haven't hit the end169* of the input */170if (nleft > 0) {171nleft--;172p++;173}174}175176return (0);177}178179case BHND_NVRAM_TYPE_UINT8_ARRAY:180case BHND_NVRAM_TYPE_UINT16_ARRAY:181case BHND_NVRAM_TYPE_UINT32_ARRAY:182case BHND_NVRAM_TYPE_UINT64_ARRAY:183case BHND_NVRAM_TYPE_INT8_ARRAY:184case BHND_NVRAM_TYPE_INT16_ARRAY:185case BHND_NVRAM_TYPE_INT32_ARRAY:186case BHND_NVRAM_TYPE_INT64_ARRAY:187case BHND_NVRAM_TYPE_CHAR_ARRAY:188case BHND_NVRAM_TYPE_BOOL_ARRAY: {189size_t width = bhnd_nvram_type_width(itype);190BHND_NV_ASSERT(width != 0, ("invalid width"));191192*nelem = ilen / width;193return (0);194}195196case BHND_NVRAM_TYPE_INT8:197case BHND_NVRAM_TYPE_UINT8:198case BHND_NVRAM_TYPE_CHAR:199case BHND_NVRAM_TYPE_INT16:200case BHND_NVRAM_TYPE_UINT16:201case BHND_NVRAM_TYPE_INT32:202case BHND_NVRAM_TYPE_UINT32:203case BHND_NVRAM_TYPE_INT64:204case BHND_NVRAM_TYPE_UINT64:205case BHND_NVRAM_TYPE_BOOL:206/* Length must be equal to the size of exactly one207* element (arrays can represent zero elements -- non-array208* types cannot) */209if (ilen != bhnd_nvram_type_width(itype))210return (EFTYPE);211*nelem = 1;212return (0);213}214215/* Quiesce gcc4.2 */216BHND_NV_PANIC("bhnd nvram type %u unknown", itype);217}218219/**220* Return the size, in bytes, of a value of @p itype with @p nelem elements.221*222* @param inp The actual data to be queried, or NULL if unknown. If223* NULL and the base type is not a fixed width type224* (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned.225* @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL.226* @param itype The value type.227* @param nelem The number of elements. If @p itype is not an array228* type, this value must be 1.229*230* @retval 0 If @p itype has a variable width, and @p inp is NULL.231* @retval 0 If a @p nelem value greater than 1 is provided for a232* non-array @p itype.233* @retval 0 If a @p nelem value of 0 is provided.234* @retval 0 If the result would exceed the maximum value235* representable by size_t.236* @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL.237* @retval non-zero The size, in bytes, of @p itype with @p nelem elements.238*/239size_t240bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype,241size_t nelem)242{243/* If nelem 0, nothing to do */244if (nelem == 0)245return (0);246247/* Non-array types must have an nelem value of 1 */248if (!bhnd_nvram_is_array_type(itype) && nelem != 1)249return (0);250251switch (itype) {252case BHND_NVRAM_TYPE_UINT8_ARRAY:253case BHND_NVRAM_TYPE_UINT16_ARRAY:254case BHND_NVRAM_TYPE_UINT32_ARRAY:255case BHND_NVRAM_TYPE_UINT64_ARRAY:256case BHND_NVRAM_TYPE_INT8_ARRAY:257case BHND_NVRAM_TYPE_INT16_ARRAY:258case BHND_NVRAM_TYPE_INT32_ARRAY:259case BHND_NVRAM_TYPE_INT64_ARRAY:260case BHND_NVRAM_TYPE_CHAR_ARRAY:261case BHND_NVRAM_TYPE_BOOL_ARRAY:{262size_t width;263264width = bhnd_nvram_type_width(itype);265266/* Would nelem * width overflow? */267if (SIZE_MAX / nelem < width) {268BHND_NV_LOG("cannot represent size %s[%zu]\n",269bhnd_nvram_type_name(bhnd_nvram_base_type(itype)),270nelem);271return (0);272}273274return (nelem * width);275}276277case BHND_NVRAM_TYPE_STRING_ARRAY: {278const char *p;279size_t total_size;280281if (inp == NULL)282return (0);283284/* Iterate over the NUL-terminated strings to calculate285* total byte length */286p = inp;287total_size = 0;288for (size_t i = 0; i < nelem; i++) {289size_t elem_size;290291elem_size = strnlen(p, ilen - total_size);292p += elem_size;293294/* Check for (and skip) terminating NUL */295if (total_size < ilen && *p == '\0') {296elem_size++;297p++;298}299300/* Would total_size + elem_size overflow?301*302* A memory range larger than SIZE_MAX shouldn't be,303* possible, but include the check for completeness */304if (SIZE_MAX - total_size < elem_size)305return (0);306307total_size += elem_size;308}309310return (total_size);311}312313case BHND_NVRAM_TYPE_STRING: {314size_t size;315316if (inp == NULL)317return (0);318319/* Find length */320size = strnlen(inp, ilen);321322/* Is there a terminating NUL, or did we just hit the323* end of the string input */324if (size < ilen)325size++;326327return (size);328}329330case BHND_NVRAM_TYPE_NULL:331return (0);332333case BHND_NVRAM_TYPE_DATA:334if (inp == NULL)335return (0);336337return (ilen);338339case BHND_NVRAM_TYPE_BOOL:340return (sizeof(bhnd_nvram_bool_t));341342case BHND_NVRAM_TYPE_INT8:343case BHND_NVRAM_TYPE_UINT8:344case BHND_NVRAM_TYPE_CHAR:345return (sizeof(uint8_t));346347case BHND_NVRAM_TYPE_INT16:348case BHND_NVRAM_TYPE_UINT16:349return (sizeof(uint16_t));350351case BHND_NVRAM_TYPE_INT32:352case BHND_NVRAM_TYPE_UINT32:353return (sizeof(uint32_t));354355case BHND_NVRAM_TYPE_UINT64:356case BHND_NVRAM_TYPE_INT64:357return (sizeof(uint64_t));358}359360/* Quiesce gcc4.2 */361BHND_NV_PANIC("bhnd nvram type %u unknown", itype);362}363364/**365* Format a string representation of @p inp using @p fmt, with, writing the366* result to @p outp.367*368* Refer to bhnd_nvram_val_vprintf() for full format string documentation.369*370* @param fmt The format string.371* @param inp The value to be formatted.372* @param ilen The size of @p inp, in bytes.373* @param itype The type of @p inp.374* @param[out] outp On success, the string value will be written to375* this buffer. This argment may be NULL if the376* value is not desired.377* @param[in,out] olen The capacity of @p outp. On success, will be set378* to the actual size of the formatted string.379*380* @retval 0 success381* @retval EINVAL If @p fmt contains unrecognized format string382* specifiers.383* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen384* is too small to hold the encoded value.385* @retval EFTYPE If value coercion from @p inp to a string value via386* @p fmt is unsupported.387* @retval ERANGE If value coercion of @p value would overflow (or388* underflow) the representation defined by @p fmt.389*/390int391bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen,392bhnd_nvram_type itype, char *outp, size_t *olen, ...)393{394va_list ap;395int error;396397va_start(ap, olen);398error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap);399va_end(ap);400401return (error);402}403404/**405* Format a string representation of @p inp using @p fmt, with, writing the406* result to @p outp.407*408* Refer to bhnd_nvram_val_vprintf() for full format string documentation.409*410* @param fmt The format string.411* @param inp The value to be formatted.412* @param ilen The size of @p inp, in bytes.413* @param itype The type of @p inp.414* @param[out] outp On success, the string value will be written to415* this buffer. This argment may be NULL if the416* value is not desired.417* @param[in,out] olen The capacity of @p outp. On success, will be set418* to the actual size of the formatted string.419* @param ap Argument list.420*421* @retval 0 success422* @retval EINVAL If @p fmt contains unrecognized format string423* specifiers.424* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen425* is too small to hold the encoded value.426* @retval EFTYPE If value coercion from @p inp to a string value via427* @p fmt is unsupported.428* @retval ERANGE If value coercion of @p value would overflow (or429* underflow) the representation defined by @p fmt.430*/431int432bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen,433bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap)434{435bhnd_nvram_val val;436int error;437438/* Map input buffer as a value instance */439error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,440BHND_NVRAM_VAL_BORROW_DATA);441if (error)442return (error);443444/* Attempt to format the value */445error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap);446447/* Clean up */448bhnd_nvram_val_release(&val);449return (error);450}451452/**453* Iterate over all elements in @p inp.454*455* @param inp The value to be iterated.456* @param ilen The size, in bytes, of @p inp.457* @param itype The data type of @p inp.458* @param prev The value previously returned by459* bhnd_nvram_value_array_next(), or NULL to begin460* iteration.461* @param[in,out] olen If @p prev is non-NULL, @p olen must be a462* pointer to the length previously returned by463* bhnd_nvram_value_array_next(). On success, will464* be set to the next element's length, in bytes.465*466* @retval non-NULL A borrowed reference to the next element of @p inp.467* @retval NULL If the end of the array is reached.468*/469const void *470bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype,471const void *prev, size_t *olen)472{473const u_char *next;474size_t offset;475476/* Handle first element */477if (prev == NULL) {478/* Zero-length array? */479if (ilen == 0)480return (NULL);481482*olen = bhnd_nvram_value_size(inp, ilen, itype, 1);483return (inp);484}485486/* Advance to next element */487BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep"));488next = (const u_char *)prev + *olen;489offset = (size_t)(next - (const u_char *)inp);490491if (offset >= ilen) {492/* Hit end of the array */493return (NULL);494}495496/* Determine element size */497*olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1);498if (ilen - offset < *olen) {499BHND_NV_LOG("short element of type %s -- misaligned "500"representation", bhnd_nvram_type_name(itype));501return (NULL);502}503504return (next);505}506507/**508* Coerce value @p inp of type @p itype to @p otype, writing the509* result to @p outp.510*511* @param inp The value to be coerced.512* @param ilen The size of @p inp, in bytes.513* @param itype The base data type of @p inp.514* @param[out] outp On success, the value will be written to this515* buffer. This argment may be NULL if the value516* is not desired.517* @param[in,out] olen The capacity of @p outp. On success, will be set518* to the actual size of the requested value.519* @param otype The data type to be written to @p outp.520*521* @retval 0 success522* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too523* small to hold the requested value.524* @retval EFTYPE If the variable data cannot be coerced to @p otype.525* @retval ERANGE If value coercion would overflow @p otype.526*/527int528bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype,529void *outp, size_t *olen, bhnd_nvram_type otype)530{531bhnd_nvram_val val;532int error;533534/* Wrap input buffer in a value instance */535error = bhnd_nvram_val_init(&val, NULL, inp, ilen,536itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED);537if (error)538return (error);539540/* Try to encode as requested type */541error = bhnd_nvram_val_encode(&val, outp, olen, otype);542543/* Clean up and return error */544bhnd_nvram_val_release(&val);545return (error);546}547548549