#include <sys/param.h>
#include <sys/limits.h>
#include <sys/sbuf.h>
#ifdef _KERNEL
#include <sys/ctype.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#else
#include <ctype.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#endif
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_valuevar.h"
static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype);
static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,
bhnd_nvram_type itype, uint32_t flags);
static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp,
size_t ilen, bhnd_nvram_type itype, uint32_t flags);
static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value,
const void *inp, size_t ilen, bhnd_nvram_type itype);
static int bhnd_nvram_val_encode_data(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_encode_null(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_encode_bool(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen,
bhnd_nvram_type otype);
#define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \
(bhnd_nvram_val) { \
.refs = 1, \
.val_storage = _storage, \
.fmt = _fmt, \
.data_storage = BHND_NVRAM_VAL_DATA_NONE, \
};
#define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \
BHND_NV_ASSERT( \
value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \
value->data_len == 0 && \
value->data.ptr == NULL, \
("previously initialized value"))
#define BHND_NVRAM_VAL_EXTREF_BORROWED_DATA(_flags) \
(((_flags) & BHND_NVRAM_VAL_BORROW_DATA) || \
((_flags) & BHND_NVRAM_VAL_STATIC_DATA))
#define BHND_NVRAM_VALID_CONV_FLAGS \
(BHND_NVRAM_VAL_FIXED | \
BHND_NVRAM_VAL_DYNAMIC | \
BHND_NVRAM_VAL_COPY_DATA)
#define BHND_NVRAM_VAL_NEED_COPY(_val) \
((_val)->val_storage == BHND_NVRAM_VAL_STORAGE_AUTO || \
(_val)->data_storage == BHND_NVRAM_VAL_DATA_EXT_WEAK)
volatile u_int refs;
bhnd_nvram_val_storage val_storage;
const bhnd_nvram_val_fmt *fmt;
bhnd_nvram_val_data_storage data_storage;
bhnd_nvram_type data_type;
size_t data_len;
bhnd_nvram_val bhnd_nvram_val_null = {
.refs = 1,
.val_storage = BHND_NVRAM_VAL_STORAGE_STATIC,
.fmt = &bhnd_nvram_val_null_fmt,
.data_storage = BHND_NVRAM_VAL_DATA_INLINE,
.data_type = BHND_NVRAM_TYPE_NULL,
.data_len = 0,
};
const char *
bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt *fmt)
{
return (fmt->name);
}
const bhnd_nvram_val_fmt *
bhnd_nvram_val_default_fmt(bhnd_nvram_type type)
{
switch (type) {
case BHND_NVRAM_TYPE_UINT8:
return (&bhnd_nvram_val_uint8_fmt);
case BHND_NVRAM_TYPE_UINT16:
return (&bhnd_nvram_val_uint16_fmt);
case BHND_NVRAM_TYPE_UINT32:
return (&bhnd_nvram_val_uint32_fmt);
case BHND_NVRAM_TYPE_UINT64:
return (&bhnd_nvram_val_uint64_fmt);
case BHND_NVRAM_TYPE_INT8:
return (&bhnd_nvram_val_int8_fmt);
case BHND_NVRAM_TYPE_INT16:
return (&bhnd_nvram_val_int16_fmt);
case BHND_NVRAM_TYPE_INT32:
return (&bhnd_nvram_val_int32_fmt);
case BHND_NVRAM_TYPE_INT64:
return (&bhnd_nvram_val_int64_fmt);
case BHND_NVRAM_TYPE_CHAR:
return (&bhnd_nvram_val_char_fmt);
case BHND_NVRAM_TYPE_STRING:
return (&bhnd_nvram_val_string_fmt);
case BHND_NVRAM_TYPE_BOOL:
return (&bhnd_nvram_val_bool_fmt);
case BHND_NVRAM_TYPE_NULL:
return (&bhnd_nvram_val_null_fmt);
case BHND_NVRAM_TYPE_DATA:
return (&bhnd_nvram_val_data_fmt);
case BHND_NVRAM_TYPE_UINT8_ARRAY:
return (&bhnd_nvram_val_uint8_array_fmt);
case BHND_NVRAM_TYPE_UINT16_ARRAY:
return (&bhnd_nvram_val_uint16_array_fmt);
case BHND_NVRAM_TYPE_UINT32_ARRAY:
return (&bhnd_nvram_val_uint32_array_fmt);
case BHND_NVRAM_TYPE_UINT64_ARRAY:
return (&bhnd_nvram_val_uint64_array_fmt);
case BHND_NVRAM_TYPE_INT8_ARRAY:
return (&bhnd_nvram_val_int8_array_fmt);
case BHND_NVRAM_TYPE_INT16_ARRAY:
return (&bhnd_nvram_val_int16_array_fmt);
case BHND_NVRAM_TYPE_INT32_ARRAY:
return (&bhnd_nvram_val_int32_array_fmt);
case BHND_NVRAM_TYPE_INT64_ARRAY:
return (&bhnd_nvram_val_int64_array_fmt);
case BHND_NVRAM_TYPE_CHAR_ARRAY:
return (&bhnd_nvram_val_char_array_fmt);
case BHND_NVRAM_TYPE_STRING_ARRAY:
return (&bhnd_nvram_val_string_array_fmt);
case BHND_NVRAM_TYPE_BOOL_ARRAY:
return (&bhnd_nvram_val_bool_array_fmt);
}
BHND_NV_PANIC("bhnd nvram type %u unknown", type);
}
static int
bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype)
{
const bhnd_nvram_val_fmt *ofmt, *nfmt;
int error;
nfmt = ofmt = *fmt;
if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
return (error);
if (ofmt->op_filter == NULL) {
if (itype == ofmt->native_type)
return (0);
return (EFTYPE);
}
error = ofmt->op_filter(&nfmt, inp, ilen, itype);
if (error)
return (error);
if (ofmt != nfmt) {
error = bhnd_nvram_val_fmt_filter(&nfmt, inp, ilen, itype);
if (error)
return (error);
*fmt = nfmt;
}
return (0);
}
static int
bhnd_nvram_val_init_common(bhnd_nvram_val *value,
bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
{
void *outp;
bhnd_nvram_type otype;
size_t olen;
int error;
if (fmt == NULL)
fmt = bhnd_nvram_val_default_fmt(itype);
if ((error = bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype))) {
otype = fmt->native_type;
} else {
otype = itype;
}
*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);
if (otype == itype) {
error = bhnd_nvram_val_set(value, inp, ilen, itype, flags);
if (error)
return (error);
return (0);
}
error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype);
if (error)
return (error);
outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);
if (outp == NULL)
return (ENOMEM);
error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype);
if (error)
return (error);
return (0);
}
int
bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
{
int error;
error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO,
fmt, inp, ilen, itype, flags);
if (error)
bhnd_nvram_val_release(value);
return (error);
}
int
bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
{
int error;
if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)
return (ENOMEM);
error = bhnd_nvram_val_init_common(*value,
BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags);
if (error) {
bhnd_nvram_val_release(*value);
}
return (error);
}
static int
bhnd_nvram_val_convert_common(bhnd_nvram_val *value,
bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,
bhnd_nvram_val *src, uint32_t flags)
{
const void *inp;
void *outp;
bhnd_nvram_type itype, otype;
size_t ilen, olen;
int error;
inp = bhnd_nvram_val_bytes(src, &ilen, &itype);
if (bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype) == 0) {
switch (src->data_storage) {
case BHND_NVRAM_VAL_DATA_NONE:
case BHND_NVRAM_VAL_DATA_INLINE:
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
break;
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
if (flags & BHND_NVRAM_VAL_BORROW_DATA)
flags |= BHND_NVRAM_VAL_STATIC_DATA;
break;
}
return (bhnd_nvram_val_init_common(value, val_storage, fmt, inp,
ilen, itype, flags));
}
otype = fmt->native_type;
*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);
if ((error = bhnd_nvram_val_encode(src, NULL, &olen, otype)))
return (error);
outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);
if (outp == NULL)
return (ENOMEM);
if ((error = bhnd_nvram_val_encode(src, outp, &olen, otype)))
return (error);
return (0);
}
int
bhnd_nvram_val_convert_init(bhnd_nvram_val *value,
const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)
{
int error;
error = bhnd_nvram_val_convert_common(value,
BHND_NVRAM_VAL_STORAGE_AUTO, fmt, src, flags);
if (error)
bhnd_nvram_val_release(value);
return (error);
}
int
bhnd_nvram_val_convert_new(bhnd_nvram_val **value,
const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)
{
int error;
if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)
return (ENOMEM);
error = bhnd_nvram_val_convert_common(*value,
BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, src, flags);
if (error) {
bhnd_nvram_val_release(*value);
}
return (error);
}
bhnd_nvram_val *
bhnd_nvram_val_copy(bhnd_nvram_val *value)
{
bhnd_nvram_val *result;
const void *bytes;
bhnd_nvram_type type;
size_t len;
uint32_t flags;
int error;
switch (value->val_storage) {
case BHND_NVRAM_VAL_STORAGE_STATIC:
return (value);
case BHND_NVRAM_VAL_STORAGE_DYNAMIC:
if (!BHND_NVRAM_VAL_NEED_COPY(value)) {
refcount_acquire(&value->refs);
return (value);
}
break;
case BHND_NVRAM_VAL_STORAGE_AUTO:
BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has "
"active refcount (%u)", value->refs));
break;
}
switch (value->data_storage) {
case BHND_NVRAM_VAL_DATA_NONE:
case BHND_NVRAM_VAL_DATA_INLINE:
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC;
break;
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
flags = BHND_NVRAM_VAL_STATIC_DATA;
break;
default:
BHND_NV_PANIC("invalid storage type: %d", value->data_storage);
}
bytes = bhnd_nvram_val_bytes(value, &len, &type);
error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type,
flags);
if (error) {
BHND_NV_LOG("copy failed: %d", error);
return (NULL);
}
return (result);
}
void
bhnd_nvram_val_release(bhnd_nvram_val *value)
{
BHND_NV_ASSERT(value->refs >= 1, ("value over-released"));
if (value->val_storage == BHND_NVRAM_VAL_STORAGE_STATIC)
return;
if (!refcount_release(&value->refs))
return;
switch (value->data_storage) {
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
bhnd_nv_free(__DECONST(void *, value->data.ptr));
break;
case BHND_NVRAM_VAL_DATA_NONE:
case BHND_NVRAM_VAL_DATA_INLINE:
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
break;
}
if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC)
bhnd_nv_free(value);
}
static int
bhnd_nvram_val_encode_null(const void *inp, size_t ilen, bhnd_nvram_type itype,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
size_t limit, nbytes;
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_NULL,
("unsupported type: %d", itype));
if (outp != NULL)
limit = *olen;
else
limit = 0;
nbytes = 0;
switch (otype) {
case BHND_NVRAM_TYPE_NULL:
nbytes = 0;
break;
default:
return (EFTYPE);
}
*olen = nbytes;
if (limit < *olen) {
if (outp == NULL)
return (0);
return (ENOMEM);
}
return (0);
}
static int
bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, bhnd_nvram_type itype,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
bhnd_nvram_bool_t bval;
size_t limit, nbytes, nelem;
int error;
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_BOOL,
("unsupported type: %d", itype));
if (outp != NULL)
limit = *olen;
else
limit = 0;
if ((error = bhnd_nvram_value_nelem(inp, ilen, itype, &nelem)))
return (error);
if (nelem != 1)
return (EFTYPE);
bval = (*(const bhnd_nvram_bool_t *)inp != 0) ? true : false;
switch (otype) {
case BHND_NVRAM_TYPE_NULL:
if (bval != false)
return (EFTYPE);
nbytes = 0;
break;
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY: {
const char *str = bval ? "true" : "false";
nbytes = strlen(str) + 1;
if (limit > nbytes)
strcpy(outp, str);
break;
}
default:
if (bhnd_nvram_is_int_type(otype)) {
uint8_t ival = bval ? 1 : 0;
return (bhnd_nvram_val_encode_int(&ival, sizeof(ival),
BHND_NVRAM_TYPE_UINT8, outp, olen, otype));
}
return (EFTYPE);
}
*olen = nbytes;
if (limit < *olen) {
if (outp == NULL)
return (0);
return (ENOMEM);
}
return (0);
}
static int
bhnd_nvram_val_encode_data(const void *inp, size_t ilen, bhnd_nvram_type itype,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_DATA,
("unsupported type: %d", itype));
switch (otype) {
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
return (bhnd_nvram_value_printf("H%[]02hhX", inp, ilen,
BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, ""));
default:
return (bhnd_nvram_value_coerce(inp, ilen,
BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, otype));
}
}
static int
bhnd_nvram_val_encode_string(const void *inp, size_t ilen,
bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype)
{
const char *cstr;
bhnd_nvram_type otype_base;
size_t cstr_size, cstr_len;
size_t limit, nbytes;
BHND_NV_ASSERT(
itype == BHND_NVRAM_TYPE_STRING ||
itype == BHND_NVRAM_TYPE_CHAR ||
itype == BHND_NVRAM_TYPE_CHAR_ARRAY,
("unsupported type: %d", itype));
cstr = inp;
cstr_size = ilen;
nbytes = 0;
otype_base = bhnd_nvram_base_type(otype);
if (outp != NULL)
limit = *olen;
else
limit = 0;
cstr_len = strnlen(cstr, cstr_size);
switch (otype) {
case BHND_NVRAM_TYPE_NULL:
if (cstr_len != 0)
return (EFTYPE);
*olen = 0;
return (0);
case BHND_NVRAM_TYPE_CHAR:
case BHND_NVRAM_TYPE_CHAR_ARRAY:
if (!bhnd_nvram_is_array_type(otype)) {
if (cstr_len != 1)
return (EFTYPE);
}
for (size_t i = 0; i < cstr_len; i++) {
if (limit > nbytes)
*((uint8_t *)outp + nbytes) = cstr[i];
nbytes++;
}
*olen = nbytes;
if (limit < *olen && outp != NULL)
return (ENOMEM);
return (0);
case BHND_NVRAM_TYPE_BOOL:
case BHND_NVRAM_TYPE_BOOL_ARRAY: {
const char *p;
size_t plen;
bhnd_nvram_bool_t bval;
p = cstr;
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
if (strncasecmp(p, "true", plen) == 0 ||
strncasecmp(p, "yes", plen) == 0 ||
strncmp(p, "1", plen) == 0)
{
bval = true;
} else if (strncasecmp(p, "false", plen) == 0 ||
strncasecmp(p, "no", plen) == 0 ||
strncmp(p, "0", plen) == 0)
{
bval = false;
} else {
return (EFTYPE);
}
nbytes = sizeof(bhnd_nvram_bool_t);
if (limit >= nbytes)
*((bhnd_nvram_bool_t *)outp) = bval;
*olen = nbytes;
if (limit < *olen && outp != NULL)
return (ENOMEM);
return (0);
}
case BHND_NVRAM_TYPE_DATA: {
const char *p;
size_t plen, parsed_len;
int error;
p = cstr;
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
if (plen < 1 || bhnd_nv_toupper(*p) != 'H')
return (EFTYPE);
p++;
plen--;
while (plen > 0) {
uint8_t byte;
size_t byte_len = sizeof(byte);
error = bhnd_nvram_parse_int(p, bhnd_nv_ummin(plen, 2),
16, &parsed_len, &byte, &byte_len, otype_base);
if (error) {
BHND_NV_DEBUG("error parsing '%.*s' as "
"integer: %d\n", BHND_NV_PRINT_WIDTH(plen),
p, error);
return (error);
}
if (limit > nbytes)
*((uint8_t *)outp + nbytes) = byte;
nbytes++;
p += parsed_len;
plen -= parsed_len;
}
*olen = nbytes;
if (limit < *olen && outp != NULL)
return (ENOMEM);
return (0);
}
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_UINT8_ARRAY:
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_UINT16_ARRAY:
case BHND_NVRAM_TYPE_UINT32:
case BHND_NVRAM_TYPE_UINT32_ARRAY:
case BHND_NVRAM_TYPE_UINT64:
case BHND_NVRAM_TYPE_UINT64_ARRAY:
case BHND_NVRAM_TYPE_INT8:
case BHND_NVRAM_TYPE_INT8_ARRAY:
case BHND_NVRAM_TYPE_INT16:
case BHND_NVRAM_TYPE_INT16_ARRAY:
case BHND_NVRAM_TYPE_INT32:
case BHND_NVRAM_TYPE_INT32_ARRAY:
case BHND_NVRAM_TYPE_INT64:
case BHND_NVRAM_TYPE_INT64_ARRAY: {
const char *p;
size_t plen, parsed_len;
int error;
p = cstr;
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp,
olen, otype_base);
if (error) {
BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n",
BHND_NV_PRINT_WIDTH(plen), p, error);
return (error);
}
if (plen != parsed_len) {
BHND_NV_DEBUG("error parsing '%.*s' as a single "
"integer value; trailing garbage '%.*s'\n",
BHND_NV_PRINT_WIDTH(plen), p,
BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len);
return (EFTYPE);
}
return (0);
}
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
*olen = cstr_size;
if (cstr_len == cstr_size)
(*olen)++;
if (outp == NULL)
return (0);
if (limit < *olen)
return (ENOMEM);
strncpy(outp, cstr, cstr_len);
*((char *)outp + cstr_len) = '\0';
return (0);
}
BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype));
}
static int
bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
bhnd_nvram_type otype_base;
size_t limit, nbytes;
bool itype_signed, otype_signed, otype_int;
union {
uint64_t u64;
int64_t i64;
} intv;
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type"));
if (outp != NULL)
limit = *olen;
else
limit = 0;
otype_base = bhnd_nvram_base_type(otype);
otype_int = bhnd_nvram_is_int_type(otype);
otype_signed = bhnd_nvram_is_signed_type(otype_base);
switch (itype) {
case BHND_NVRAM_TYPE_UINT8:
if (ilen != sizeof(uint8_t))
return (EFAULT);
itype_signed = false;
intv.u64 = *(const uint8_t *)inp;
break;
case BHND_NVRAM_TYPE_UINT16:
if (ilen != sizeof(uint16_t))
return (EFAULT);
itype_signed = false;
intv.u64 = *(const uint16_t *)inp;
break;
case BHND_NVRAM_TYPE_UINT32:
if (ilen != sizeof(uint32_t))
return (EFAULT);
itype_signed = false;
intv.u64 = *(const uint32_t *)inp;
break;
case BHND_NVRAM_TYPE_UINT64:
if (ilen != sizeof(uint64_t))
return (EFAULT);
itype_signed = false;
intv.u64 = *(const uint64_t *)inp;
break;
case BHND_NVRAM_TYPE_INT8:
if (ilen != sizeof(int8_t))
return (EFAULT);
itype_signed = true;
intv.i64 = *(const int8_t *)inp;
break;
case BHND_NVRAM_TYPE_INT16:
if (ilen != sizeof(int16_t))
return (EFAULT);
itype_signed = true;
intv.i64 = *(const int16_t *)inp;
break;
case BHND_NVRAM_TYPE_INT32:
if (ilen != sizeof(int32_t))
return (EFAULT);
itype_signed = true;
intv.i64 = *(const int32_t *)inp;
break;
case BHND_NVRAM_TYPE_INT64:
if (ilen != sizeof(int32_t))
return (EFAULT);
itype_signed = true;
intv.i64 = *(const int32_t *)inp;
break;
default:
BHND_NV_PANIC("invalid type %d\n", itype);
}
if (itype_signed && otype_int && !otype_signed) {
if (intv.i64 < 0) {
BHND_NV_LOG("cannot represent %" PRId64 " as %s\n",
intv.i64, bhnd_nvram_type_name(otype));
return (ERANGE);
}
intv.u64 = intv.i64;
} else if (!itype_signed && otype_int && otype_signed) {
if (intv.u64 > INT64_MAX) {
BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n",
intv.u64, bhnd_nvram_type_name(otype));
return (ERANGE);
}
intv.i64 = intv.u64;
}
switch (otype) {
case BHND_NVRAM_TYPE_NULL:
return (EFTYPE);
case BHND_NVRAM_TYPE_BOOL: {
bhnd_nvram_bool_t bval;
if (intv.u64 == 0 || intv.u64 == 1) {
bval = intv.u64;
} else {
return (ERANGE);
}
nbytes = sizeof(bhnd_nvram_bool_t);
if (limit >= nbytes)
*((bhnd_nvram_bool_t *)outp) = bval;
break;
}
case BHND_NVRAM_TYPE_CHAR:
case BHND_NVRAM_TYPE_CHAR_ARRAY:
case BHND_NVRAM_TYPE_DATA:
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_UINT8_ARRAY:
if (intv.u64 > UINT8_MAX)
return (ERANGE);
nbytes = sizeof(uint8_t);
if (limit >= nbytes)
*((uint8_t *)outp) = (uint8_t)intv.u64;
break;
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_UINT16_ARRAY:
if (intv.u64 > UINT16_MAX)
return (ERANGE);
nbytes = sizeof(uint16_t);
if (limit >= nbytes)
*((uint16_t *)outp) = (uint16_t)intv.u64;
break;
case BHND_NVRAM_TYPE_UINT32:
case BHND_NVRAM_TYPE_UINT32_ARRAY:
if (intv.u64 > UINT32_MAX)
return (ERANGE);
nbytes = sizeof(uint32_t);
if (limit >= nbytes)
*((uint32_t *)outp) = (uint32_t)intv.u64;
break;
case BHND_NVRAM_TYPE_UINT64:
case BHND_NVRAM_TYPE_UINT64_ARRAY:
nbytes = sizeof(uint64_t);
if (limit >= nbytes)
*((uint64_t *)outp) = intv.u64;
break;
case BHND_NVRAM_TYPE_INT8:
case BHND_NVRAM_TYPE_INT8_ARRAY:
if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX)
return (ERANGE);
nbytes = sizeof(int8_t);
if (limit >= nbytes)
*((int8_t *)outp) = (int8_t)intv.i64;
break;
case BHND_NVRAM_TYPE_INT16:
case BHND_NVRAM_TYPE_INT16_ARRAY:
if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX)
return (ERANGE);
nbytes = sizeof(int16_t);
if (limit >= nbytes)
*((int16_t *)outp) = (int16_t)intv.i64;
break;
case BHND_NVRAM_TYPE_INT32:
case BHND_NVRAM_TYPE_INT32_ARRAY:
if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX)
return (ERANGE);
nbytes = sizeof(int32_t);
if (limit >= nbytes)
*((int32_t *)outp) = (int32_t)intv.i64;
break;
case BHND_NVRAM_TYPE_INT64:
case BHND_NVRAM_TYPE_INT64_ARRAY:
nbytes = sizeof(int64_t);
if (limit >= nbytes)
*((int64_t *)outp) = intv.i64;
break;
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY: {
ssize_t len;
if (otype_signed) {
len = snprintf(outp, limit, "%" PRId64, intv.i64);
} else {
len = snprintf(outp, limit, "%" PRIu64, intv.u64);
}
if (len < 0) {
BHND_NV_LOG("snprintf() failed: %zd\n", len);
return (EFTYPE);
}
nbytes = len + 1;
break;
}
default:
BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype));
return (EFTYPE);
}
*olen = nbytes;
if (limit < *olen) {
if (outp == NULL)
return (0);
return (ENOMEM);
}
return (0);
}
int
bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
bhnd_nvram_type otype)
{
if (value->fmt->op_encode != NULL)
return (value->fmt->op_encode(value, outp, olen, otype));
return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
}
int
bhnd_nvram_val_encode_elem(bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
{
if (value->fmt->op_encode_elem != NULL) {
return (value->fmt->op_encode_elem(value, inp, ilen, outp,
olen, otype));
}
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp,
olen, otype));
}
const void *
bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen,
bhnd_nvram_type *otype)
{
*otype = value->data_type;
*olen = value->data_len;
switch (value->data_storage) {
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
return (value->data.ptr);
case BHND_NVRAM_VAL_DATA_INLINE:
return (&value->data);
case BHND_NVRAM_VAL_DATA_NONE:
BHND_NV_PANIC("uninitialized value");
}
BHND_NV_PANIC("unknown storage type: %d", value->data_storage);
}
const void *
bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen)
{
if (value->fmt->op_next != NULL)
return (value->fmt->op_next(value, prev, olen));
return (bhnd_nvram_val_generic_next(value, prev, olen));
}
bhnd_nvram_type
bhnd_nvram_val_type(bhnd_nvram_val *value)
{
return (value->data_type);
}
bhnd_nvram_type
bhnd_nvram_val_elem_type(bhnd_nvram_val *value)
{
return (bhnd_nvram_base_type(value->data_type));
}
size_t
bhnd_nvram_val_nelem(bhnd_nvram_val *value)
{
const void *bytes;
bhnd_nvram_type type;
size_t nelem, len;
int error;
if (value->fmt->op_nelem != NULL)
return (value->fmt->op_nelem(value));
if (value->fmt->op_next != NULL) {
const void *next;
next = NULL;
nelem = 0;
while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL)
nelem++;
return (nelem);
}
bytes = bhnd_nvram_val_bytes(value, &len, &type);
if ((error = bhnd_nvram_value_nelem(bytes, len, type, &nelem))) {
BHND_NV_PANIC("error calculating element count for type '%s' "
"with length %zu: %d\n", bhnd_nvram_type_name(type), len,
error);
}
return (nelem);
}
int
bhnd_nvram_val_generic_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
bhnd_nvram_type otype)
{
const void *inp;
bhnd_nvram_type itype;
size_t ilen;
const void *next;
bhnd_nvram_type otype_base;
size_t limit, nelem, nbytes;
size_t next_len;
int error;
nbytes = 0;
nelem = 0;
otype_base = bhnd_nvram_base_type(otype);
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
#define BHND_NV_IS_ISO_CONV(_lhs, _rhs) \
((itype == BHND_NVRAM_TYPE_ ## _lhs && \
otype == BHND_NVRAM_TYPE_ ## _rhs) || \
(itype == BHND_NVRAM_TYPE_ ## _rhs && \
otype == BHND_NVRAM_TYPE_ ## _lhs))
if (BHND_NV_IS_ISO_CONV(CHAR_ARRAY, STRING)) {
return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
otype));
}
#undef BHND_NV_IS_ISO_CONV
if (!bhnd_nvram_is_array_type(itype) &&
!bhnd_nvram_is_array_type(otype))
{
return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
otype));
}
if (outp != NULL)
limit = *olen;
else
limit = 0;
next = NULL;
while ((next = bhnd_nvram_val_next(value, next, &next_len))) {
void *elem_outp;
size_t elem_nbytes;
nelem++;
if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) {
return (EFTYPE);
}
if (nbytes >= limit) {
elem_nbytes = 0;
elem_outp = NULL;
} else {
elem_nbytes = limit - nbytes;
elem_outp = (uint8_t *)outp + nbytes;
}
error = bhnd_nvram_val_encode_elem(value, next, next_len,
elem_outp, &elem_nbytes, otype_base);
if (error && error != ENOMEM)
return (error);
if (SIZE_MAX - nbytes < elem_nbytes)
return (EFTYPE);
nbytes += elem_nbytes;
}
*olen = nbytes;
if (outp == NULL)
return (0);
if (limit < nbytes)
return (ENOMEM);
return (0);
}
int
bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
{
bhnd_nvram_type itype;
itype = bhnd_nvram_val_elem_type(value);
switch (itype) {
case BHND_NVRAM_TYPE_NULL:
return (bhnd_nvram_val_encode_null(inp, ilen, itype, outp, olen,
otype));
case BHND_NVRAM_TYPE_DATA:
return (bhnd_nvram_val_encode_data(inp, ilen, itype, outp,
olen, otype));
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_CHAR:
return (bhnd_nvram_val_encode_string(inp, ilen, itype, outp,
olen, otype));
case BHND_NVRAM_TYPE_BOOL:
return (bhnd_nvram_val_encode_bool(inp, ilen, itype, outp, olen,
otype));
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_UINT32:
case BHND_NVRAM_TYPE_UINT64:
case BHND_NVRAM_TYPE_INT8:
case BHND_NVRAM_TYPE_INT16:
case BHND_NVRAM_TYPE_INT32:
case BHND_NVRAM_TYPE_INT64:
return (bhnd_nvram_val_encode_int(inp, ilen, itype, outp, olen,
otype));
default:
BHND_NV_PANIC("missing encode_elem() implementation");
}
}
const void *
bhnd_nvram_val_generic_next(bhnd_nvram_val *value, const void *prev,
size_t *olen)
{
const uint8_t *inp;
bhnd_nvram_type itype;
size_t ilen;
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, olen));
}
static int
bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen,
bhnd_nvram_type itype, uint32_t flags)
{
void *bytes;
int error;
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
return (error);
if ((flags & BHND_NVRAM_VAL_BORROW_DATA) ||
(flags & BHND_NVRAM_VAL_STATIC_DATA))
{
if (flags & BHND_NVRAM_VAL_STATIC_DATA)
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC;
else
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK;
value->data.ptr = inp;
value->data_type = itype;
value->data_len = ilen;
return (0);
}
bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags);
if (bytes == NULL)
return (ENOMEM);
memcpy(bytes, inp, ilen);
return (0);
}
static int
bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen,
bhnd_nvram_type itype)
{
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
#define NV_STORE_INIT_INLINE() do { \
value->data_len = ilen; \
value->data_type = itype; \
} while(0)
#define NV_STORE_INLINE(_type, _dest) do { \
if (ilen != sizeof(_type)) \
return (EFAULT); \
\
if (inp != NULL) { \
value->data._dest[0] = *(const _type *)inp; \
NV_STORE_INIT_INLINE(); \
} \
} while (0)
#define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \
if (ilen % sizeof(_type) != 0) \
return (EFAULT); \
\
if (ilen > nitems(value->data. _dest)) \
return (ENOMEM); \
\
if (inp == NULL) \
return (0); \
\
memcpy(&value->data._dest, inp, ilen); \
if (inp != NULL) { \
memcpy(&value->data._dest, inp, ilen); \
NV_STORE_INIT_INLINE(); \
} \
} while (0)
switch (itype) {
case BHND_NVRAM_TYPE_NULL:
if (ilen != 0)
return (EFAULT);
NV_STORE_INIT_INLINE();
return (0);
case BHND_NVRAM_TYPE_CHAR:
NV_STORE_INLINE(uint8_t, ch);
return (0);
case BHND_NVRAM_TYPE_BOOL:
NV_STORE_INLINE(bhnd_nvram_bool_t, b);
return(0);
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_INT8:
NV_STORE_INLINE(uint8_t, u8);
return (0);
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_INT16:
NV_STORE_INLINE(uint16_t, u16);
return (0);
case BHND_NVRAM_TYPE_UINT32:
case BHND_NVRAM_TYPE_INT32:
NV_STORE_INLINE(uint32_t, u32);
return (0);
case BHND_NVRAM_TYPE_UINT64:
case BHND_NVRAM_TYPE_INT64:
NV_STORE_INLINE(uint32_t, u32);
return (0);
case BHND_NVRAM_TYPE_CHAR_ARRAY:
NV_COPY_ARRRAY_INLINE(uint8_t, ch);
return (0);
case BHND_NVRAM_TYPE_DATA:
case BHND_NVRAM_TYPE_UINT8_ARRAY:
case BHND_NVRAM_TYPE_INT8_ARRAY:
NV_COPY_ARRRAY_INLINE(uint8_t, u8);
return (0);
case BHND_NVRAM_TYPE_UINT16_ARRAY:
case BHND_NVRAM_TYPE_INT16_ARRAY:
NV_COPY_ARRRAY_INLINE(uint16_t, u16);
return (0);
case BHND_NVRAM_TYPE_UINT32_ARRAY:
case BHND_NVRAM_TYPE_INT32_ARRAY:
NV_COPY_ARRRAY_INLINE(uint32_t, u32);
return (0);
case BHND_NVRAM_TYPE_UINT64_ARRAY:
case BHND_NVRAM_TYPE_INT64_ARRAY:
NV_COPY_ARRRAY_INLINE(uint64_t, u64);
return (0);
case BHND_NVRAM_TYPE_BOOL_ARRAY:
NV_COPY_ARRRAY_INLINE(bhnd_nvram_bool_t, b);
return(0);
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
if (ilen > sizeof(value->data.ch))
return (ENOMEM);
if (inp != NULL) {
memcpy(&value->data.ch, inp, ilen);
NV_STORE_INIT_INLINE();
}
return (0);
}
#undef NV_STORE_INIT_INLINE
#undef NV_STORE_INLINE
#undef NV_COPY_ARRRAY_INLINE
BHND_NV_PANIC("unknown data type %d", itype);
}
static void *
bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,
bhnd_nvram_type itype, uint32_t flags)
{
void *ptr;
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) {
BHND_NV_ASSERT(sizeof(value->data) >= ilen,
("ilen exceeds inline storage"));
value->data_type = itype;
value->data_len = ilen;
value->data_storage = BHND_NVRAM_VAL_DATA_INLINE;
return (&value->data);
}
if (!(flags & BHND_NVRAM_VAL_DYNAMIC))
return (NULL);
if ((ptr = bhnd_nv_malloc(ilen)) == NULL)
return (NULL);
value->data.ptr = ptr;
value->data_len = ilen;
value->data_type = itype;
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC;
return (ptr);
}