Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_value.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>30#include <sys/limits.h>31#include <sys/sbuf.h>3233#ifdef _KERNEL3435#include <sys/ctype.h>36#include <sys/kernel.h>37#include <sys/malloc.h>38#include <sys/systm.h>3940#include <machine/_inttypes.h>4142#else /* !_KERNEL */4344#include <ctype.h>45#include <inttypes.h>46#include <errno.h>47#include <stdlib.h>48#include <string.h>4950#endif /* _KERNEL */5152#include "bhnd_nvram_private.h"5354#include "bhnd_nvram_valuevar.h"5556static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt,57const void *inp, size_t ilen, bhnd_nvram_type itype);5859static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,60bhnd_nvram_type itype, uint32_t flags);61static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp,62size_t ilen, bhnd_nvram_type itype, uint32_t flags);63static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value,64const void *inp, size_t ilen, bhnd_nvram_type itype);6566static int bhnd_nvram_val_encode_data(const void *inp, size_t ilen,67bhnd_nvram_type itype, void *outp, size_t *olen,68bhnd_nvram_type otype);69static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen,70bhnd_nvram_type itype, void *outp, size_t *olen,71bhnd_nvram_type otype);72static int bhnd_nvram_val_encode_null(const void *inp, size_t ilen,73bhnd_nvram_type itype, void *outp, size_t *olen,74bhnd_nvram_type otype);75static int bhnd_nvram_val_encode_bool(const void *inp, size_t ilen,76bhnd_nvram_type itype, void *outp, size_t *olen,77bhnd_nvram_type otype);78static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen,79bhnd_nvram_type itype, void *outp, size_t *olen,80bhnd_nvram_type otype);8182/** Initialize an empty value instance with @p _fmt, @p _storage, and83* an implicit callee-owned reference */84#define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \85(bhnd_nvram_val) { \86.refs = 1, \87.val_storage = _storage, \88.fmt = _fmt, \89.data_storage = BHND_NVRAM_VAL_DATA_NONE, \90};9192/** Assert that @p value's backing representation state has initialized93* as empty. */94#define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \95BHND_NV_ASSERT( \96value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \97value->data_len == 0 && \98value->data.ptr == NULL, \99("previously initialized value"))100101/** Return true if BHND_NVRAM_VAL_BORROW_DATA or BHND_NVRAM_VAL_STATIC_DATA is102* set in @p _flags (e.g. we should attempt to directly reference external103* data */104#define BHND_NVRAM_VAL_EXTREF_BORROWED_DATA(_flags) \105(((_flags) & BHND_NVRAM_VAL_BORROW_DATA) || \106((_flags) & BHND_NVRAM_VAL_STATIC_DATA))107108/** Flags permitted when performing val-based initialization via109* bhnd_nvram_val_convert_init() or bhnd_nvram_val_convert_new() */110#define BHND_NVRAM_VALID_CONV_FLAGS \111(BHND_NVRAM_VAL_FIXED | \112BHND_NVRAM_VAL_DYNAMIC | \113BHND_NVRAM_VAL_COPY_DATA)114115/** Returns true if @p _val must be copied in bhnd_nvram_val_copy(), false116* if its reference count may be safely incremented */117#define BHND_NVRAM_VAL_NEED_COPY(_val) \118((_val)->val_storage == BHND_NVRAM_VAL_STORAGE_AUTO || \119(_val)->data_storage == BHND_NVRAM_VAL_DATA_EXT_WEAK)120121volatile u_int refs; /**< reference count */122bhnd_nvram_val_storage val_storage; /**< value structure storage */123const bhnd_nvram_val_fmt *fmt; /**< value format */124bhnd_nvram_val_data_storage data_storage; /**< data storage */125bhnd_nvram_type data_type; /**< data type */126size_t data_len; /**< data size */127128/* Shared NULL value instance */129bhnd_nvram_val bhnd_nvram_val_null = {130.refs = 1,131.val_storage = BHND_NVRAM_VAL_STORAGE_STATIC,132.fmt = &bhnd_nvram_val_null_fmt,133.data_storage = BHND_NVRAM_VAL_DATA_INLINE,134.data_type = BHND_NVRAM_TYPE_NULL,135.data_len = 0,136};137138/**139* Return the human-readable name of @p fmt.140*/141const char *142bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt *fmt)143{144return (fmt->name);145}146147/**148* Return the default format for values of @p type.149*/150const bhnd_nvram_val_fmt *151bhnd_nvram_val_default_fmt(bhnd_nvram_type type)152{153switch (type) {154case BHND_NVRAM_TYPE_UINT8:155return (&bhnd_nvram_val_uint8_fmt);156case BHND_NVRAM_TYPE_UINT16:157return (&bhnd_nvram_val_uint16_fmt);158case BHND_NVRAM_TYPE_UINT32:159return (&bhnd_nvram_val_uint32_fmt);160case BHND_NVRAM_TYPE_UINT64:161return (&bhnd_nvram_val_uint64_fmt);162case BHND_NVRAM_TYPE_INT8:163return (&bhnd_nvram_val_int8_fmt);164case BHND_NVRAM_TYPE_INT16:165return (&bhnd_nvram_val_int16_fmt);166case BHND_NVRAM_TYPE_INT32:167return (&bhnd_nvram_val_int32_fmt);168case BHND_NVRAM_TYPE_INT64:169return (&bhnd_nvram_val_int64_fmt);170case BHND_NVRAM_TYPE_CHAR:171return (&bhnd_nvram_val_char_fmt);172case BHND_NVRAM_TYPE_STRING:173return (&bhnd_nvram_val_string_fmt);174case BHND_NVRAM_TYPE_BOOL:175return (&bhnd_nvram_val_bool_fmt);176case BHND_NVRAM_TYPE_NULL:177return (&bhnd_nvram_val_null_fmt);178case BHND_NVRAM_TYPE_DATA:179return (&bhnd_nvram_val_data_fmt);180case BHND_NVRAM_TYPE_UINT8_ARRAY:181return (&bhnd_nvram_val_uint8_array_fmt);182case BHND_NVRAM_TYPE_UINT16_ARRAY:183return (&bhnd_nvram_val_uint16_array_fmt);184case BHND_NVRAM_TYPE_UINT32_ARRAY:185return (&bhnd_nvram_val_uint32_array_fmt);186case BHND_NVRAM_TYPE_UINT64_ARRAY:187return (&bhnd_nvram_val_uint64_array_fmt);188case BHND_NVRAM_TYPE_INT8_ARRAY:189return (&bhnd_nvram_val_int8_array_fmt);190case BHND_NVRAM_TYPE_INT16_ARRAY:191return (&bhnd_nvram_val_int16_array_fmt);192case BHND_NVRAM_TYPE_INT32_ARRAY:193return (&bhnd_nvram_val_int32_array_fmt);194case BHND_NVRAM_TYPE_INT64_ARRAY:195return (&bhnd_nvram_val_int64_array_fmt);196case BHND_NVRAM_TYPE_CHAR_ARRAY:197return (&bhnd_nvram_val_char_array_fmt);198case BHND_NVRAM_TYPE_STRING_ARRAY:199return (&bhnd_nvram_val_string_array_fmt);200case BHND_NVRAM_TYPE_BOOL_ARRAY:201return (&bhnd_nvram_val_bool_array_fmt);202}203204/* Quiesce gcc4.2 */205BHND_NV_PANIC("bhnd nvram type %u unknown", type);206}207208/**209* Determine whether @p fmt (or new format delegated to by @p fmt) is210* capable of direct initialization from buffer @p inp.211*212* @param[in,out] fmt Indirect pointer to the NVRAM value format. If213* the format instance cannot handle the data type214* directly, it may delegate to a new format215* instance. On success, this parameter will be216* set to the format that should be used when217* performing initialization from @p inp.218* @param inp Input data.219* @param ilen Input data length.220* @param itype Input data type.221*222* @retval 0 If initialization from @p inp is supported.223* @retval EFTYPE If initialization from @p inp is unsupported.224* @retval EFAULT if @p ilen is not correctly aligned for elements of225* @p itype.226*/227static int228bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,229size_t ilen, bhnd_nvram_type itype)230{231const bhnd_nvram_val_fmt *ofmt, *nfmt;232int error;233234nfmt = ofmt = *fmt;235236/* Validate alignment */237if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))238return (error);239240/* If the format does not provide a filter function, it only supports241* direct initialization from its native type */242if (ofmt->op_filter == NULL) {243if (itype == ofmt->native_type)244return (0);245246return (EFTYPE);247}248249/* Use the filter function to determine whether direct initialization250* from itype is permitted */251error = ofmt->op_filter(&nfmt, inp, ilen, itype);252if (error)253return (error);254255/* Retry filter with new format? */256if (ofmt != nfmt) {257error = bhnd_nvram_val_fmt_filter(&nfmt, inp, ilen, itype);258if (error)259return (error);260261/* Success -- provide delegated format to caller */262*fmt = nfmt;263}264265/* Value can be initialized with provided format and input type */266return (0);267}268269/* Common initialization support for bhnd_nvram_val_init() and270* bhnd_nvram_val_new() */271static int272bhnd_nvram_val_init_common(bhnd_nvram_val *value,273bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,274const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)275{276void *outp;277bhnd_nvram_type otype;278size_t olen;279int error;280281/* If the value format is unspecified, we use the default format282* for the input data type */283if (fmt == NULL)284fmt = bhnd_nvram_val_default_fmt(itype);285286/* Determine expected data type, and allow the format to delegate to287* a new format instance */288if ((error = bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype))) {289/* Direct initialization from the provided input type is290* not supported; alue must be initialized with the format's291* native type */292otype = fmt->native_type;293} else {294/* Value can be initialized with provided input type */295otype = itype;296}297298/* Initialize value instance */299*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);300301/* If input data already in native format, init directly. */302if (otype == itype) {303error = bhnd_nvram_val_set(value, inp, ilen, itype, flags);304if (error)305return (error);306307return (0);308}309310/* Determine size when encoded in native format */311error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype);312if (error)313return (error);314315/* Fetch reference to (or allocate) an appropriately sized buffer */316outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);317if (outp == NULL)318return (ENOMEM);319320/* Perform encode */321error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype);322if (error)323return (error);324325return (0);326}327328/**329* Initialize an externally allocated instance of @p value with @p fmt from the330* given @p inp buffer of @p itype and @p ilen.331*332* On success, the caller owns a reference to @p value, and is responsible for333* freeing any resources allocated for @p value via bhnd_nvram_val_release().334*335* @param value The externally allocated value instance to be336* initialized.337* @param fmt The value's format, or NULL to use the default format338* for @p itype.339* @param inp Input buffer.340* @param ilen Input buffer length.341* @param itype Input buffer type.342* @param flags Value flags (see BHND_NVRAM_VAL_*).343*344* @retval 0 success345* @retval ENOMEM If allocation fails.346* @retval EFTYPE If @p fmt initialization from @p itype is unsupported.347* @retval EFAULT if @p ilen is not correctly aligned for elements of348* @p itype.349* @retval ERANGE If value coercion would overflow (or underflow) the350* @p fmt representation.351*/352int353bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt,354const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)355{356int error;357358error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO,359fmt, inp, ilen, itype, flags);360if (error)361bhnd_nvram_val_release(value);362363return (error);364}365366/**367* Allocate a value instance with @p fmt, and attempt to initialize its internal368* representation from the given @p inp buffer of @p itype and @p ilen.369*370* On success, the caller owns a reference to @p value, and is responsible for371* freeing any resources allocated for @p value via bhnd_nvram_val_release().372*373* @param[out] value On success, the allocated value instance.374* @param fmt The value's format, or NULL to use the default format375* for @p itype.376* @param inp Input buffer.377* @param ilen Input buffer length.378* @param itype Input buffer type.379* @param flags Value flags (see BHND_NVRAM_VAL_*).380*381* @retval 0 success382* @retval ENOMEM If allocation fails.383* @retval EFTYPE If @p fmt initialization from @p itype is unsupported.384* @retval EFAULT if @p ilen is not correctly aligned for elements of385* @p itype.386* @retval ERANGE If value coercion would overflow (or underflow) the387* @p fmt representation.388*/389int390bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt,391const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)392{393int error;394395/* Allocate new instance */396if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)397return (ENOMEM);398399/* Perform common initialization. */400error = bhnd_nvram_val_init_common(*value,401BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags);402if (error) {403/* Will also free() the value allocation */404bhnd_nvram_val_release(*value);405}406407return (error);408}409410/* Common initialization support for bhnd_nvram_val_convert_init() and411* bhnd_nvram_val_convert_new() */412static int413bhnd_nvram_val_convert_common(bhnd_nvram_val *value,414bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,415bhnd_nvram_val *src, uint32_t flags)416{417const void *inp;418void *outp;419bhnd_nvram_type itype, otype;420size_t ilen, olen;421int error;422423/* Determine whether direct initialization from the source value's424* existing data type is supported by the new format */425inp = bhnd_nvram_val_bytes(src, &ilen, &itype);426if (bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype) == 0) {427/* Adjust value flags based on the source data storage */428switch (src->data_storage) {429case BHND_NVRAM_VAL_DATA_NONE:430case BHND_NVRAM_VAL_DATA_INLINE:431case BHND_NVRAM_VAL_DATA_EXT_WEAK:432case BHND_NVRAM_VAL_DATA_EXT_ALLOC:433break;434435case BHND_NVRAM_VAL_DATA_EXT_STATIC:436/* If the source data has static storage duration,437* we should apply that transitively */438if (flags & BHND_NVRAM_VAL_BORROW_DATA)439flags |= BHND_NVRAM_VAL_STATIC_DATA;440441break;442}443444/* Delegate to standard initialization */445return (bhnd_nvram_val_init_common(value, val_storage, fmt, inp,446ilen, itype, flags));447}448449/* Value must be initialized with the format's native type */450otype = fmt->native_type;451452/* Initialize value instance */453*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);454455/* Determine size when encoded in native format */456if ((error = bhnd_nvram_val_encode(src, NULL, &olen, otype)))457return (error);458459/* Fetch reference to (or allocate) an appropriately sized buffer */460outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);461if (outp == NULL)462return (ENOMEM);463464/* Perform encode */465if ((error = bhnd_nvram_val_encode(src, outp, &olen, otype)))466return (error);467468return (0);469}470471/**472* Initialize an externally allocated instance of @p value with @p fmt, and473* attempt to initialize its internal representation from the given @p src474* value.475*476* On success, the caller owns a reference to @p value, and is responsible for477* freeing any resources allocated for @p value via bhnd_nvram_val_release().478*479* @param value The externally allocated value instance to be480* initialized.481* @param fmt The value's format.482* @param src Input value to be converted.483* @param flags Value flags (see BHND_NVRAM_VAL_*).484*485* @retval 0 success486* @retval ENOMEM If allocation fails.487* @retval EFTYPE If @p fmt initialization from @p src is unsupported.488* @retval EFAULT if @p ilen is not correctly aligned for elements of489* @p itype.490* @retval ERANGE If value coercion of @p src would overflow491* (or underflow) the @p fmt representation.492*/493int494bhnd_nvram_val_convert_init(bhnd_nvram_val *value,495const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)496{497int error;498499error = bhnd_nvram_val_convert_common(value,500BHND_NVRAM_VAL_STORAGE_AUTO, fmt, src, flags);501if (error)502bhnd_nvram_val_release(value);503504return (error);505}506507/**508* Allocate a value instance with @p fmt, and attempt to initialize its internal509* representation from the given @p src value.510*511* On success, the caller owns a reference to @p value, and is responsible for512* freeing any resources allocated for @p value via bhnd_nvram_val_release().513*514* @param[out] value On success, the allocated value instance.515* @param fmt The value's format.516* @param src Input value to be converted.517* @param flags Value flags (see BHND_NVRAM_VAL_*).518*519* @retval 0 success520* @retval ENOMEM If allocation fails.521* @retval EFTYPE If @p fmt initialization from @p src is unsupported.522* @retval EFAULT if @p ilen is not correctly aligned for elements of523* @p itype.524* @retval ERANGE If value coercion of @p src would overflow525* (or underflow) the @p fmt representation.526*/527int528bhnd_nvram_val_convert_new(bhnd_nvram_val **value,529const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)530{531int error;532533/* Allocate new instance */534if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)535return (ENOMEM);536537/* Perform common initialization. */538error = bhnd_nvram_val_convert_common(*value,539BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, src, flags);540if (error) {541/* Will also free() the value allocation */542bhnd_nvram_val_release(*value);543}544545return (error);546}547548/**549* Copy or retain a reference to @p value.550*551* On success, the caller is responsible for freeing the result via552* bhnd_nvram_val_release().553*554* @param value The value to be copied (or retained).555*556* @retval bhnd_nvram_val if @p value was successfully copied or retained.557* @retval NULL if allocation failed.558*/559bhnd_nvram_val *560bhnd_nvram_val_copy(bhnd_nvram_val *value)561{562bhnd_nvram_val *result;563const void *bytes;564bhnd_nvram_type type;565size_t len;566uint32_t flags;567int error;568569switch (value->val_storage) {570case BHND_NVRAM_VAL_STORAGE_STATIC:571/* If static, can return as-is */572return (value);573574case BHND_NVRAM_VAL_STORAGE_DYNAMIC:575if (!BHND_NVRAM_VAL_NEED_COPY(value)) {576refcount_acquire(&value->refs);577return (value);578}579580/* Perform copy below */581break;582583case BHND_NVRAM_VAL_STORAGE_AUTO:584BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has "585"active refcount (%u)", value->refs));586587/* Perform copy below */588break;589}590591/* Compute the new value's flags based on the source value */592switch (value->data_storage) {593case BHND_NVRAM_VAL_DATA_NONE:594case BHND_NVRAM_VAL_DATA_INLINE:595case BHND_NVRAM_VAL_DATA_EXT_WEAK:596case BHND_NVRAM_VAL_DATA_EXT_ALLOC:597/* Copy the source data and permit additional allocation if the598* value cannot be represented inline */599flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC;600break;601case BHND_NVRAM_VAL_DATA_EXT_STATIC:602flags = BHND_NVRAM_VAL_STATIC_DATA;603break;604default:605BHND_NV_PANIC("invalid storage type: %d", value->data_storage);606}607608/* Allocate new value copy */609bytes = bhnd_nvram_val_bytes(value, &len, &type);610error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type,611flags);612if (error) {613BHND_NV_LOG("copy failed: %d", error);614return (NULL);615}616617return (result);618}619620/**621* Release a reference to @p value.622*623* If this is the last reference, all associated resources will be freed.624*625* @param value The value to be released.626*/627void628bhnd_nvram_val_release(bhnd_nvram_val *value)629{630BHND_NV_ASSERT(value->refs >= 1, ("value over-released"));631632/* Skip if value is static */633if (value->val_storage == BHND_NVRAM_VAL_STORAGE_STATIC)634return;635636/* Drop reference */637if (!refcount_release(&value->refs))638return;639640/* Free allocated external representation data */641switch (value->data_storage) {642case BHND_NVRAM_VAL_DATA_EXT_ALLOC:643bhnd_nv_free(__DECONST(void *, value->data.ptr));644break;645case BHND_NVRAM_VAL_DATA_NONE:646case BHND_NVRAM_VAL_DATA_INLINE:647case BHND_NVRAM_VAL_DATA_EXT_WEAK:648case BHND_NVRAM_VAL_DATA_EXT_STATIC:649/* Nothing to free */650break;651}652653/* Free instance if dynamically allocated */654if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC)655bhnd_nv_free(value);656}657658/**659* Standard BHND_NVRAM_TYPE_NULL encoding implementation.660*/661static int662bhnd_nvram_val_encode_null(const void *inp, size_t ilen, bhnd_nvram_type itype,663void *outp, size_t *olen, bhnd_nvram_type otype)664{665size_t limit, nbytes;666667BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_NULL,668("unsupported type: %d", itype));669670/* Determine output byte limit */671if (outp != NULL)672limit = *olen;673else674limit = 0;675676nbytes = 0;677678/* Write to output */679switch (otype) {680case BHND_NVRAM_TYPE_NULL:681/* Can be directly encoded as a zero-length NULL value */682nbytes = 0;683break;684default:685/* Not representable */686return (EFTYPE);687}688689/* Provide required length */690*olen = nbytes;691if (limit < *olen) {692if (outp == NULL)693return (0);694695return (ENOMEM);696}697698return (0);699}700701/**702* Standard BHND_NVRAM_TYPE_BOOL encoding implementation.703*/704static int705bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, bhnd_nvram_type itype,706void *outp, size_t *olen, bhnd_nvram_type otype)707{708bhnd_nvram_bool_t bval;709size_t limit, nbytes, nelem;710int error;711712BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_BOOL,713("unsupported type: %d", itype));714715/* Determine output byte limit */716if (outp != NULL)717limit = *olen;718else719limit = 0;720721/* Must be exactly one element in input */722if ((error = bhnd_nvram_value_nelem(inp, ilen, itype, &nelem)))723return (error);724725if (nelem != 1)726return (EFTYPE);727728/* Fetch (and normalize) boolean value */729bval = (*(const bhnd_nvram_bool_t *)inp != 0) ? true : false;730731/* Write to output */732switch (otype) {733case BHND_NVRAM_TYPE_NULL:734/* False can be directly encoded as a zero-length NULL value */735if (bval != false)736return (EFTYPE);737738nbytes = 0;739break;740741case BHND_NVRAM_TYPE_STRING:742case BHND_NVRAM_TYPE_STRING_ARRAY: {743/* Can encode as "true" or "false" */744const char *str = bval ? "true" : "false";745746nbytes = strlen(str) + 1;747if (limit > nbytes)748strcpy(outp, str);749750break;751}752753default:754/* If output type is an integer, we can delegate to standard755* integer encoding to encode as zero or one. */756if (bhnd_nvram_is_int_type(otype)) {757uint8_t ival = bval ? 1 : 0;758759return (bhnd_nvram_val_encode_int(&ival, sizeof(ival),760BHND_NVRAM_TYPE_UINT8, outp, olen, otype));761}762763/* Otherwise not representable */764return (EFTYPE);765}766767/* Provide required length */768*olen = nbytes;769if (limit < *olen) {770if (outp == NULL)771return (0);772773return (ENOMEM);774}775776return (0);777}778779/**780* Standard BHND_NVRAM_TYPE_DATA encoding implementation.781*/782static int783bhnd_nvram_val_encode_data(const void *inp, size_t ilen, bhnd_nvram_type itype,784void *outp, size_t *olen, bhnd_nvram_type otype)785{786BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_DATA,787("unsupported type: %d", itype));788789/* Write to output */790switch (otype) {791case BHND_NVRAM_TYPE_STRING:792case BHND_NVRAM_TYPE_STRING_ARRAY:793/* If encoding as a string, produce an EFI-style hexadecimal794* byte array (HF1F...) by interpreting the octet string795* as an array of uint8 values */796return (bhnd_nvram_value_printf("H%[]02hhX", inp, ilen,797BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, ""));798799default:800/* Fall back on direct interpretation as an array of 8-bit801* integers array */802return (bhnd_nvram_value_coerce(inp, ilen,803BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, otype));804}805}806807/**808* Standard string/char array/char encoding implementation.809*810* Input type must be one of:811* - BHND_NVRAM_TYPE_STRING812* - BHND_NVRAM_TYPE_CHAR813* - BHND_NVRAM_TYPE_CHAR_ARRAY814*/815static int816bhnd_nvram_val_encode_string(const void *inp, size_t ilen,817bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype)818{819const char *cstr;820bhnd_nvram_type otype_base;821size_t cstr_size, cstr_len;822size_t limit, nbytes;823824BHND_NV_ASSERT(825itype == BHND_NVRAM_TYPE_STRING ||826itype == BHND_NVRAM_TYPE_CHAR ||827itype == BHND_NVRAM_TYPE_CHAR_ARRAY,828("unsupported type: %d", itype));829830cstr = inp;831cstr_size = ilen;832nbytes = 0;833otype_base = bhnd_nvram_base_type(otype);834835/* Determine output byte limit */836if (outp != NULL)837limit = *olen;838else839limit = 0;840841/* Determine string length, minus trailing NUL (if any) */842cstr_len = strnlen(cstr, cstr_size);843844/* Parse the string data and write to output */845switch (otype) {846case BHND_NVRAM_TYPE_NULL:847/* Only an empty string may be represented as a NULL value */848if (cstr_len != 0)849return (EFTYPE);850851*olen = 0;852return (0);853854case BHND_NVRAM_TYPE_CHAR:855case BHND_NVRAM_TYPE_CHAR_ARRAY:856/* String must contain exactly 1 non-terminating-NUL character857* to be represented as a single char */858if (!bhnd_nvram_is_array_type(otype)) {859if (cstr_len != 1)860return (EFTYPE);861}862863/* Copy out the characters directly (excluding trailing NUL) */864for (size_t i = 0; i < cstr_len; i++) {865if (limit > nbytes)866*((uint8_t *)outp + nbytes) = cstr[i];867nbytes++;868}869870/* Provide required length */871*olen = nbytes;872if (limit < *olen && outp != NULL)873return (ENOMEM);874875return (0);876877case BHND_NVRAM_TYPE_BOOL:878case BHND_NVRAM_TYPE_BOOL_ARRAY: {879const char *p;880size_t plen;881bhnd_nvram_bool_t bval;882883/* Trim leading/trailing whitespace */884p = cstr;885plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');886887/* Parse string representation */888if (strncasecmp(p, "true", plen) == 0 ||889strncasecmp(p, "yes", plen) == 0 ||890strncmp(p, "1", plen) == 0)891{892bval = true;893} else if (strncasecmp(p, "false", plen) == 0 ||894strncasecmp(p, "no", plen) == 0 ||895strncmp(p, "0", plen) == 0)896{897bval = false;898} else {899/* Not a recognized boolean string */900return (EFTYPE);901}902903/* Write to output */904nbytes = sizeof(bhnd_nvram_bool_t);905if (limit >= nbytes)906*((bhnd_nvram_bool_t *)outp) = bval;907908/* Provide required length */909*olen = nbytes;910if (limit < *olen && outp != NULL)911return (ENOMEM);912913return (0);914}915916case BHND_NVRAM_TYPE_DATA: {917const char *p;918size_t plen, parsed_len;919int error;920921/* Trim leading/trailing whitespace */922p = cstr;923plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');924925/* Check for EFI-style hexadecimal byte array string format.926* Must have a 'H' prefix */927if (plen < 1 || bhnd_nv_toupper(*p) != 'H')928return (EFTYPE);929930/* Skip leading 'H' */931p++;932plen--;933934/* Parse the input string's two-char octets until the end935* of input is reached. The last octet may contain only936* one char */937while (plen > 0) {938uint8_t byte;939size_t byte_len = sizeof(byte);940941/* Parse next two-character hex octet */942error = bhnd_nvram_parse_int(p, bhnd_nv_ummin(plen, 2),94316, &parsed_len, &byte, &byte_len, otype_base);944if (error) {945BHND_NV_DEBUG("error parsing '%.*s' as "946"integer: %d\n", BHND_NV_PRINT_WIDTH(plen),947p, error);948949return (error);950}951952/* Write to output */953if (limit > nbytes)954*((uint8_t *)outp + nbytes) = byte;955nbytes++;956957/* Advance input */958p += parsed_len;959plen -= parsed_len;960}961962/* Provide required length */963*olen = nbytes;964if (limit < *olen && outp != NULL)965return (ENOMEM);966967return (0);968}969970case BHND_NVRAM_TYPE_UINT8:971case BHND_NVRAM_TYPE_UINT8_ARRAY:972case BHND_NVRAM_TYPE_UINT16:973case BHND_NVRAM_TYPE_UINT16_ARRAY:974case BHND_NVRAM_TYPE_UINT32:975case BHND_NVRAM_TYPE_UINT32_ARRAY:976case BHND_NVRAM_TYPE_UINT64:977case BHND_NVRAM_TYPE_UINT64_ARRAY:978case BHND_NVRAM_TYPE_INT8:979case BHND_NVRAM_TYPE_INT8_ARRAY:980case BHND_NVRAM_TYPE_INT16:981case BHND_NVRAM_TYPE_INT16_ARRAY:982case BHND_NVRAM_TYPE_INT32:983case BHND_NVRAM_TYPE_INT32_ARRAY:984case BHND_NVRAM_TYPE_INT64:985case BHND_NVRAM_TYPE_INT64_ARRAY: {986const char *p;987size_t plen, parsed_len;988int error;989990/* Trim leading/trailing whitespace */991p = cstr;992plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');993994/* Try to parse the integer value */995error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp,996olen, otype_base);997if (error) {998BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n",999BHND_NV_PRINT_WIDTH(plen), p, error);1000return (error);1001}10021003/* Do additional bytes remain unparsed? */1004if (plen != parsed_len) {1005BHND_NV_DEBUG("error parsing '%.*s' as a single "1006"integer value; trailing garbage '%.*s'\n",1007BHND_NV_PRINT_WIDTH(plen), p,1008BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len);1009return (EFTYPE);1010}10111012return (0);1013}10141015case BHND_NVRAM_TYPE_STRING:1016case BHND_NVRAM_TYPE_STRING_ARRAY:1017/* Copy out the string representation as-is */1018*olen = cstr_size;10191020/* Need additional space for trailing NUL? */1021if (cstr_len == cstr_size)1022(*olen)++;10231024/* Skip output? */1025if (outp == NULL)1026return (0);10271028/* Verify required length */1029if (limit < *olen)1030return (ENOMEM);10311032/* Copy and NUL terminate */1033strncpy(outp, cstr, cstr_len);1034*((char *)outp + cstr_len) = '\0';10351036return (0);1037}10381039BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype));1040}10411042/**1043* Standard integer encoding implementation.1044*/1045static int1046bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype,1047void *outp, size_t *olen, bhnd_nvram_type otype)1048{1049bhnd_nvram_type otype_base;1050size_t limit, nbytes;1051bool itype_signed, otype_signed, otype_int;1052union {1053uint64_t u64;1054int64_t i64;1055} intv;10561057BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type"));10581059/* Determine output byte limit */1060if (outp != NULL)1061limit = *olen;1062else1063limit = 0;10641065/* Fetch output type info */1066otype_base = bhnd_nvram_base_type(otype);1067otype_int = bhnd_nvram_is_int_type(otype);1068otype_signed = bhnd_nvram_is_signed_type(otype_base);10691070/*1071* Promote integer value to a common 64-bit representation.1072*/1073switch (itype) {1074case BHND_NVRAM_TYPE_UINT8:1075if (ilen != sizeof(uint8_t))1076return (EFAULT);10771078itype_signed = false;1079intv.u64 = *(const uint8_t *)inp;1080break;10811082case BHND_NVRAM_TYPE_UINT16:1083if (ilen != sizeof(uint16_t))1084return (EFAULT);10851086itype_signed = false;1087intv.u64 = *(const uint16_t *)inp;1088break;10891090case BHND_NVRAM_TYPE_UINT32:1091if (ilen != sizeof(uint32_t))1092return (EFAULT);10931094itype_signed = false;1095intv.u64 = *(const uint32_t *)inp;1096break;10971098case BHND_NVRAM_TYPE_UINT64:1099if (ilen != sizeof(uint64_t))1100return (EFAULT);11011102itype_signed = false;1103intv.u64 = *(const uint64_t *)inp;1104break;11051106case BHND_NVRAM_TYPE_INT8:1107if (ilen != sizeof(int8_t))1108return (EFAULT);11091110itype_signed = true;1111intv.i64 = *(const int8_t *)inp;1112break;11131114case BHND_NVRAM_TYPE_INT16:1115if (ilen != sizeof(int16_t))1116return (EFAULT);11171118itype_signed = true;1119intv.i64 = *(const int16_t *)inp;1120break;11211122case BHND_NVRAM_TYPE_INT32:1123if (ilen != sizeof(int32_t))1124return (EFAULT);11251126itype_signed = true;1127intv.i64 = *(const int32_t *)inp;1128break;11291130case BHND_NVRAM_TYPE_INT64:1131if (ilen != sizeof(int32_t))1132return (EFAULT);11331134itype_signed = true;1135intv.i64 = *(const int32_t *)inp;1136break;11371138default:1139BHND_NV_PANIC("invalid type %d\n", itype);1140}11411142/* Perform signed/unsigned conversion */1143if (itype_signed && otype_int && !otype_signed) {1144if (intv.i64 < 0) {1145/* Can't represent negative value */1146BHND_NV_LOG("cannot represent %" PRId64 " as %s\n",1147intv.i64, bhnd_nvram_type_name(otype));11481149return (ERANGE);1150}11511152/* Convert to unsigned representation */1153intv.u64 = intv.i64;11541155} else if (!itype_signed && otype_int && otype_signed) {1156/* Handle unsigned -> signed coercions */1157if (intv.u64 > INT64_MAX) {1158/* Can't represent positive value */1159BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n",1160intv.u64, bhnd_nvram_type_name(otype));1161return (ERANGE);1162}11631164/* Convert to signed representation */1165intv.i64 = intv.u64;1166}11671168/* Write output */1169switch (otype) {1170case BHND_NVRAM_TYPE_NULL:1171/* Cannot encode an integer value as NULL */1172return (EFTYPE);11731174case BHND_NVRAM_TYPE_BOOL: {1175bhnd_nvram_bool_t bval;11761177if (intv.u64 == 0 || intv.u64 == 1) {1178bval = intv.u64;1179} else {1180/* Encoding as a bool would lose information */1181return (ERANGE);1182}11831184nbytes = sizeof(bhnd_nvram_bool_t);1185if (limit >= nbytes)1186*((bhnd_nvram_bool_t *)outp) = bval;11871188break;1189}11901191case BHND_NVRAM_TYPE_CHAR:1192case BHND_NVRAM_TYPE_CHAR_ARRAY:1193case BHND_NVRAM_TYPE_DATA:1194case BHND_NVRAM_TYPE_UINT8:1195case BHND_NVRAM_TYPE_UINT8_ARRAY:1196if (intv.u64 > UINT8_MAX)1197return (ERANGE);11981199nbytes = sizeof(uint8_t);1200if (limit >= nbytes)1201*((uint8_t *)outp) = (uint8_t)intv.u64;1202break;12031204case BHND_NVRAM_TYPE_UINT16:1205case BHND_NVRAM_TYPE_UINT16_ARRAY:1206if (intv.u64 > UINT16_MAX)1207return (ERANGE);12081209nbytes = sizeof(uint16_t);1210if (limit >= nbytes)1211*((uint16_t *)outp) = (uint16_t)intv.u64;1212break;12131214case BHND_NVRAM_TYPE_UINT32:1215case BHND_NVRAM_TYPE_UINT32_ARRAY:1216if (intv.u64 > UINT32_MAX)1217return (ERANGE);12181219nbytes = sizeof(uint32_t);1220if (limit >= nbytes)1221*((uint32_t *)outp) = (uint32_t)intv.u64;1222break;12231224case BHND_NVRAM_TYPE_UINT64:1225case BHND_NVRAM_TYPE_UINT64_ARRAY:1226nbytes = sizeof(uint64_t);1227if (limit >= nbytes)1228*((uint64_t *)outp) = intv.u64;1229break;12301231case BHND_NVRAM_TYPE_INT8:1232case BHND_NVRAM_TYPE_INT8_ARRAY:1233if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX)1234return (ERANGE);12351236nbytes = sizeof(int8_t);1237if (limit >= nbytes)1238*((int8_t *)outp) = (int8_t)intv.i64;1239break;12401241case BHND_NVRAM_TYPE_INT16:1242case BHND_NVRAM_TYPE_INT16_ARRAY:1243if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX)1244return (ERANGE);12451246nbytes = sizeof(int16_t);1247if (limit >= nbytes)1248*((int16_t *)outp) = (int16_t)intv.i64;1249break;12501251case BHND_NVRAM_TYPE_INT32:1252case BHND_NVRAM_TYPE_INT32_ARRAY:1253if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX)1254return (ERANGE);12551256nbytes = sizeof(int32_t);1257if (limit >= nbytes)1258*((int32_t *)outp) = (int32_t)intv.i64;1259break;12601261case BHND_NVRAM_TYPE_INT64:1262case BHND_NVRAM_TYPE_INT64_ARRAY:1263nbytes = sizeof(int64_t);1264if (limit >= nbytes)1265*((int64_t *)outp) = intv.i64;1266break;12671268case BHND_NVRAM_TYPE_STRING:1269case BHND_NVRAM_TYPE_STRING_ARRAY: {1270ssize_t len;12711272/* Attempt to write the entry + NUL */1273if (otype_signed) {1274len = snprintf(outp, limit, "%" PRId64, intv.i64);1275} else {1276len = snprintf(outp, limit, "%" PRIu64, intv.u64);1277}12781279if (len < 0) {1280BHND_NV_LOG("snprintf() failed: %zd\n", len);1281return (EFTYPE);1282}12831284/* Set total length to the formatted string length, plus1285* trailing NUL */1286nbytes = len + 1;1287break;1288}12891290default:1291BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype));1292return (EFTYPE);1293}12941295/* Provide required length */1296*olen = nbytes;1297if (limit < *olen) {1298if (outp == NULL)1299return (0);13001301return (ENOMEM);1302}13031304return (0);1305}13061307/**1308* Encode the given @p value as @p otype, writing the result to @p outp.1309*1310* @param value The value to be encoded.1311* @param[out] outp On success, the value will be written to this1312* buffer. This argment may be NULL if the value is1313* not desired.1314* @param[in,out] olen The capacity of @p outp. On success, will be set1315* to the actual size of the requested value.1316* @param otype The data type to be written to @p outp.1317*1318* @retval 0 success1319* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen1320* is too small to hold the encoded value.1321* @retval EFTYPE If value coercion from @p value to @p otype is1322* impossible.1323* @retval ERANGE If value coercion would overflow (or underflow) the1324* a @p otype representation.1325*/1326int1327bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen,1328bhnd_nvram_type otype)1329{1330/* Prefer format implementation */1331if (value->fmt->op_encode != NULL)1332return (value->fmt->op_encode(value, outp, olen, otype));13331334return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));1335}13361337/**1338* Encode the given @p value's element as @p otype, writing the result to1339* @p outp.1340*1341* @param inp The element to be encoded. Must be a value1342* previously returned by bhnd_nvram_val_next()1343* or bhnd_nvram_val_elem().1344* @param ilen The size of @p inp, as returned by1345* bhnd_nvram_val_next() or bhnd_nvram_val_elem().1346* @param[out] outp On success, the value will be written to this1347* buffer. This argment may be NULL if the value is1348* not desired.1349* @param[in,out] olen The capacity of @p outp. On success, will be set1350* to the actual size of the requested value.1351* @param otype The data type to be written to @p outp.1352*1353* @retval 0 success1354* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen1355* is too small to hold the encoded value.1356* @retval EFTYPE If value coercion from @p value to @p otype is1357* impossible.1358* @retval ERANGE If value coercion would overflow (or underflow) the1359* a @p otype representation.1360*/1361int1362bhnd_nvram_val_encode_elem(bhnd_nvram_val *value, const void *inp,1363size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)1364{1365/* Prefer format implementation */1366if (value->fmt->op_encode_elem != NULL) {1367return (value->fmt->op_encode_elem(value, inp, ilen, outp,1368olen, otype));1369}13701371return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp,1372olen, otype));1373}13741375/**1376* Return the type, size, and a pointer to the internal representation1377* of @p value.1378*1379* @param value The value to be queried.1380* @param[out] olen Size of the returned data, in bytes.1381* @param[out] otype Data type.1382*/1383const void *1384bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen,1385bhnd_nvram_type *otype)1386{1387/* Provide type and length */1388*otype = value->data_type;1389*olen = value->data_len;13901391switch (value->data_storage) {1392case BHND_NVRAM_VAL_DATA_EXT_ALLOC:1393case BHND_NVRAM_VAL_DATA_EXT_STATIC:1394case BHND_NVRAM_VAL_DATA_EXT_WEAK:1395/* Return a pointer to external storage */1396return (value->data.ptr);13971398case BHND_NVRAM_VAL_DATA_INLINE:1399/* Return a pointer to inline storage */1400return (&value->data);14011402case BHND_NVRAM_VAL_DATA_NONE:1403BHND_NV_PANIC("uninitialized value");1404}14051406BHND_NV_PANIC("unknown storage type: %d", value->data_storage);1407}14081409/**1410* Iterate over all array elements in @p value.1411*1412* @param value The value to be iterated1413* @param prev A value pointer previously returned by1414* bhnd_nvram_val_next() or bhnd_nvram_val_elem(),1415* or NULL to begin iteration at the first element.1416* @param[in,out] olen If @p prev is non-NULL, @p olen must be a1417* pointer to the length previously returned by1418* bhnd_nvram_val_next() or bhnd_nvram_val_elem().1419* On success, will be set to the next element's1420* length, in bytes.1421*1422* @retval non-NULL A borrowed reference to the element data.1423* @retval NULL If the end of the element array is reached.1424*/1425const void *1426bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen)1427{1428/* Prefer the format implementation */1429if (value->fmt->op_next != NULL)1430return (value->fmt->op_next(value, prev, olen));14311432return (bhnd_nvram_val_generic_next(value, prev, olen));1433}14341435/**1436* Return the value's data type.1437*1438* @param value The value to be queried.1439*/1440bhnd_nvram_type1441bhnd_nvram_val_type(bhnd_nvram_val *value)1442{1443return (value->data_type);1444}14451446/**1447* Return value's element data type.1448*1449* @param value The value to be queried.1450*/1451bhnd_nvram_type1452bhnd_nvram_val_elem_type(bhnd_nvram_val *value)1453{1454return (bhnd_nvram_base_type(value->data_type));1455}14561457/**1458* Return the total number of elements represented by @p value.1459*/1460size_t1461bhnd_nvram_val_nelem(bhnd_nvram_val *value)1462{1463const void *bytes;1464bhnd_nvram_type type;1465size_t nelem, len;1466int error;14671468/* Prefer format implementation */1469if (value->fmt->op_nelem != NULL)1470return (value->fmt->op_nelem(value));14711472/*1473* If a custom op_next() is defined, bhnd_nvram_value_nelem() almost1474* certainly cannot produce a valid element count; it assumes a standard1475* data format that may not apply when custom iteration is required.1476*1477* Instead, use bhnd_nvram_val_next() to parse the backing data and1478* produce a total count.1479*/1480if (value->fmt->op_next != NULL) {1481const void *next;14821483next = NULL;1484nelem = 0;1485while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL)1486nelem++;14871488return (nelem);1489}14901491/* Otherwise, compute the standard element count */1492bytes = bhnd_nvram_val_bytes(value, &len, &type);1493if ((error = bhnd_nvram_value_nelem(bytes, len, type, &nelem))) {1494/* Should always succeed */1495BHND_NV_PANIC("error calculating element count for type '%s' "1496"with length %zu: %d\n", bhnd_nvram_type_name(type), len,1497error);1498}14991500return (nelem);1501}15021503/**1504* Generic implementation of bhnd_nvram_val_op_encode(), compatible with1505* all supported NVRAM data types.1506*/1507int1508bhnd_nvram_val_generic_encode(bhnd_nvram_val *value, void *outp, size_t *olen,1509bhnd_nvram_type otype)1510{1511const void *inp;1512bhnd_nvram_type itype;1513size_t ilen;1514const void *next;1515bhnd_nvram_type otype_base;1516size_t limit, nelem, nbytes;1517size_t next_len;1518int error;15191520nbytes = 0;1521nelem = 0;1522otype_base = bhnd_nvram_base_type(otype);1523inp = bhnd_nvram_val_bytes(value, &ilen, &itype);15241525/*1526* Normally, an array type is not universally representable as1527* non-array type.1528*1529* As exceptions, we support conversion directly to/from:1530* - CHAR_ARRAY/STRING:1531* ->STRING Interpret the character array as a1532* non-NUL-terminated string.1533* ->CHAR_ARRAY Trim the trailing NUL from the string.1534*/1535#define BHND_NV_IS_ISO_CONV(_lhs, _rhs) \1536((itype == BHND_NVRAM_TYPE_ ## _lhs && \1537otype == BHND_NVRAM_TYPE_ ## _rhs) || \1538(itype == BHND_NVRAM_TYPE_ ## _rhs && \1539otype == BHND_NVRAM_TYPE_ ## _lhs))15401541if (BHND_NV_IS_ISO_CONV(CHAR_ARRAY, STRING)) {1542return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,1543otype));1544}15451546#undef BHND_NV_IS_ISO_CONV15471548/*1549* If both input and output are non-array types, try to encode them1550* without performing element iteration.1551*/1552if (!bhnd_nvram_is_array_type(itype) &&1553!bhnd_nvram_is_array_type(otype))1554{1555return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,1556otype));1557}15581559/* Determine output byte limit */1560if (outp != NULL)1561limit = *olen;1562else1563limit = 0;15641565/* Iterate over our array elements and encode as the requested1566* type */1567next = NULL;1568while ((next = bhnd_nvram_val_next(value, next, &next_len))) {1569void *elem_outp;1570size_t elem_nbytes;15711572/* If the output type is not an array type, we can only encode1573* one element */1574nelem++;1575if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) {1576return (EFTYPE);1577}15781579/* Determine output offset / limit */1580if (nbytes >= limit) {1581elem_nbytes = 0;1582elem_outp = NULL;1583} else {1584elem_nbytes = limit - nbytes;1585elem_outp = (uint8_t *)outp + nbytes;1586}15871588/* Attempt encode */1589error = bhnd_nvram_val_encode_elem(value, next, next_len,1590elem_outp, &elem_nbytes, otype_base);15911592/* If encoding failed for any reason other than ENOMEM (which1593* we'll detect and report below), return immediately */1594if (error && error != ENOMEM)1595return (error);15961597/* Add to total length */1598if (SIZE_MAX - nbytes < elem_nbytes)1599return (EFTYPE); /* would overflow size_t */16001601nbytes += elem_nbytes;1602}16031604/* Provide the actual length */1605*olen = nbytes;16061607/* If no output was requested, nothing left to do */1608if (outp == NULL)1609return (0);16101611/* Otherwise, report a memory error if the output buffer was too1612* small */1613if (limit < nbytes)1614return (ENOMEM);16151616return (0);1617}16181619/**1620* Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with1621* all supported NVRAM data types.1622*/1623int1624bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val *value, const void *inp,1625size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)1626{1627bhnd_nvram_type itype;16281629itype = bhnd_nvram_val_elem_type(value);1630switch (itype) {1631case BHND_NVRAM_TYPE_NULL:1632return (bhnd_nvram_val_encode_null(inp, ilen, itype, outp, olen,1633otype));16341635case BHND_NVRAM_TYPE_DATA:1636return (bhnd_nvram_val_encode_data(inp, ilen, itype, outp,1637olen, otype));16381639case BHND_NVRAM_TYPE_STRING:1640case BHND_NVRAM_TYPE_CHAR:1641return (bhnd_nvram_val_encode_string(inp, ilen, itype, outp,1642olen, otype));16431644case BHND_NVRAM_TYPE_BOOL:1645return (bhnd_nvram_val_encode_bool(inp, ilen, itype, outp, olen,1646otype));16471648case BHND_NVRAM_TYPE_UINT8:1649case BHND_NVRAM_TYPE_UINT16:1650case BHND_NVRAM_TYPE_UINT32:1651case BHND_NVRAM_TYPE_UINT64:1652case BHND_NVRAM_TYPE_INT8:1653case BHND_NVRAM_TYPE_INT16:1654case BHND_NVRAM_TYPE_INT32:1655case BHND_NVRAM_TYPE_INT64:1656return (bhnd_nvram_val_encode_int(inp, ilen, itype, outp, olen,1657otype));1658default:1659BHND_NV_PANIC("missing encode_elem() implementation");1660}1661}16621663/**1664* Generic implementation of bhnd_nvram_val_op_next(), compatible with1665* all supported NVRAM data types.1666*/1667const void *1668bhnd_nvram_val_generic_next(bhnd_nvram_val *value, const void *prev,1669size_t *olen)1670{1671const uint8_t *inp;1672bhnd_nvram_type itype;1673size_t ilen;16741675/* Iterate over the backing representation */1676inp = bhnd_nvram_val_bytes(value, &ilen, &itype);1677return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, olen));1678}16791680/**1681* Initialize the representation of @p value with @p ptr.1682*1683* @param value The value to be initialized.1684* @param inp The external representation.1685* @param ilen The external representation length, in bytes.1686* @param itype The external representation's data type.1687* @param flags Value flags.1688*1689* @retval 0 success.1690* @retval ENOMEM if allocation fails1691* @retval EFTYPE if @p itype is not an array type, and @p ilen is not1692* equal to the size of a single element of @p itype.1693* @retval EFAULT if @p ilen is not correctly aligned for elements of1694* @p itype.1695*/1696static int1697bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen,1698bhnd_nvram_type itype, uint32_t flags)1699{1700void *bytes;1701int error;17021703BHND_NVRAM_VAL_ASSERT_EMPTY(value);17041705/* Validate alignment */1706if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))1707return (error);17081709/* Reference the external data */1710if ((flags & BHND_NVRAM_VAL_BORROW_DATA) ||1711(flags & BHND_NVRAM_VAL_STATIC_DATA))1712{1713if (flags & BHND_NVRAM_VAL_STATIC_DATA)1714value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC;1715else1716value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK;17171718value->data.ptr = inp;1719value->data_type = itype;1720value->data_len = ilen;1721return (0);1722}17231724/* Fetch reference to (or allocate) an appropriately sized buffer */1725bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags);1726if (bytes == NULL)1727return (ENOMEM);17281729/* Copy data */1730memcpy(bytes, inp, ilen);17311732return (0);1733}17341735/**1736* Initialize the internal inline representation of @p value with a copy of1737* the data referenced by @p inp of @p itype.1738*1739* If @p inp is NULL, @p itype and @p ilen will be validated, but no data will1740* be copied.1741*1742* @param value The value to be initialized.1743* @param inp The input data to be copied, or NULL to verify1744* that data of @p ilen and @p itype can be represented1745* inline.1746* @param ilen The size of the external buffer to be allocated.1747* @param itype The type of the external buffer to be allocated.1748*1749* @retval 0 success1750* @retval ENOMEM if @p ilen is too large to be represented inline.1751* @retval EFAULT if @p ilen is not correctly aligned for elements of1752* @p itype.1753*/1754static int1755bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen,1756bhnd_nvram_type itype)1757{1758BHND_NVRAM_VAL_ASSERT_EMPTY(value);17591760#define NV_STORE_INIT_INLINE() do { \1761value->data_len = ilen; \1762value->data_type = itype; \1763} while(0)17641765#define NV_STORE_INLINE(_type, _dest) do { \1766if (ilen != sizeof(_type)) \1767return (EFAULT); \1768\1769if (inp != NULL) { \1770value->data._dest[0] = *(const _type *)inp; \1771NV_STORE_INIT_INLINE(); \1772} \1773} while (0)17741775#define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \1776if (ilen % sizeof(_type) != 0) \1777return (EFAULT); \1778\1779if (ilen > nitems(value->data. _dest)) \1780return (ENOMEM); \1781\1782if (inp == NULL) \1783return (0); \1784\1785memcpy(&value->data._dest, inp, ilen); \1786if (inp != NULL) { \1787memcpy(&value->data._dest, inp, ilen); \1788NV_STORE_INIT_INLINE(); \1789} \1790} while (0)17911792/* Attempt to copy to inline storage */1793switch (itype) {1794case BHND_NVRAM_TYPE_NULL:1795if (ilen != 0)1796return (EFAULT);17971798/* Nothing to copy */1799NV_STORE_INIT_INLINE();1800return (0);18011802case BHND_NVRAM_TYPE_CHAR:1803NV_STORE_INLINE(uint8_t, ch);1804return (0);18051806case BHND_NVRAM_TYPE_BOOL:1807NV_STORE_INLINE(bhnd_nvram_bool_t, b);1808return(0);18091810case BHND_NVRAM_TYPE_UINT8:1811case BHND_NVRAM_TYPE_INT8:1812NV_STORE_INLINE(uint8_t, u8);1813return (0);18141815case BHND_NVRAM_TYPE_UINT16:1816case BHND_NVRAM_TYPE_INT16:1817NV_STORE_INLINE(uint16_t, u16);1818return (0);18191820case BHND_NVRAM_TYPE_UINT32:1821case BHND_NVRAM_TYPE_INT32:1822NV_STORE_INLINE(uint32_t, u32);1823return (0);18241825case BHND_NVRAM_TYPE_UINT64:1826case BHND_NVRAM_TYPE_INT64:1827NV_STORE_INLINE(uint32_t, u32);1828return (0);18291830case BHND_NVRAM_TYPE_CHAR_ARRAY:1831NV_COPY_ARRRAY_INLINE(uint8_t, ch);1832return (0);18331834case BHND_NVRAM_TYPE_DATA:1835case BHND_NVRAM_TYPE_UINT8_ARRAY:1836case BHND_NVRAM_TYPE_INT8_ARRAY:1837NV_COPY_ARRRAY_INLINE(uint8_t, u8);1838return (0);18391840case BHND_NVRAM_TYPE_UINT16_ARRAY:1841case BHND_NVRAM_TYPE_INT16_ARRAY:1842NV_COPY_ARRRAY_INLINE(uint16_t, u16);1843return (0);18441845case BHND_NVRAM_TYPE_UINT32_ARRAY:1846case BHND_NVRAM_TYPE_INT32_ARRAY:1847NV_COPY_ARRRAY_INLINE(uint32_t, u32);1848return (0);18491850case BHND_NVRAM_TYPE_UINT64_ARRAY:1851case BHND_NVRAM_TYPE_INT64_ARRAY:1852NV_COPY_ARRRAY_INLINE(uint64_t, u64);1853return (0);18541855case BHND_NVRAM_TYPE_BOOL_ARRAY:1856NV_COPY_ARRRAY_INLINE(bhnd_nvram_bool_t, b);1857return(0);18581859case BHND_NVRAM_TYPE_STRING:1860case BHND_NVRAM_TYPE_STRING_ARRAY:1861if (ilen > sizeof(value->data.ch))1862return (ENOMEM);18631864if (inp != NULL) {1865memcpy(&value->data.ch, inp, ilen);1866NV_STORE_INIT_INLINE();1867}18681869return (0);1870}18711872#undef NV_STORE_INIT_INLINE1873#undef NV_STORE_INLINE1874#undef NV_COPY_ARRRAY_INLINE18751876BHND_NV_PANIC("unknown data type %d", itype);1877}18781879/**1880* Initialize the internal representation of @p value with a buffer allocation1881* of @p len and @p itype, returning a pointer to the allocated buffer.1882*1883* If a buffer of @p len and @p itype can be represented inline, no1884* external buffer will be allocated, and instead a pointer to the inline1885* data representation will be returned.1886*1887* @param value The value to be initialized.1888* @param ilen The size of the external buffer to be allocated.1889* @param itype The type of the external buffer to be allocated.1890* @param flags Value flags.1891*1892* @retval non-null The newly allocated buffer.1893* @retval NULL If allocation failed.1894* @retval NULL If @p value is an externally allocated instance.1895*/1896static void *1897bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,1898bhnd_nvram_type itype, uint32_t flags)1899{1900void *ptr;19011902BHND_NVRAM_VAL_ASSERT_EMPTY(value);19031904/* Can we use inline storage? */1905if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) {1906BHND_NV_ASSERT(sizeof(value->data) >= ilen,1907("ilen exceeds inline storage"));19081909value->data_type = itype;1910value->data_len = ilen;1911value->data_storage = BHND_NVRAM_VAL_DATA_INLINE;1912return (&value->data);1913}19141915/* Is allocation permitted? */1916if (!(flags & BHND_NVRAM_VAL_DYNAMIC))1917return (NULL);19181919/* Allocate external storage */1920if ((ptr = bhnd_nv_malloc(ilen)) == NULL)1921return (NULL);19221923value->data.ptr = ptr;1924value->data_len = ilen;1925value->data_type = itype;1926value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC;19271928return (ptr);1929}193019311932