#include <sys/param.h>
#include <net/ethernet.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 <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#endif
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_valuevar.h"
static bool bhnd_nvram_ident_octet_string(const char *inp,
size_t ilen, char *delim, size_t *nelem);
static bool bhnd_nvram_ident_num_string(const char *inp,
size_t ilen, u_int base, u_int *obase);
static int bhnd_nvram_val_bcm_macaddr_filter(
const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype);
static int bhnd_nvram_val_bcm_macaddr_encode(
bhnd_nvram_val *value, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_bcm_macaddr_string_filter(
const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype);
static int bhnd_nvram_val_bcm_macaddr_string_encode_elem(
bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
static const void *bhnd_nvram_val_bcm_macaddr_string_next(
bhnd_nvram_val *value, const void *prev,
size_t *len);
static int bhnd_nvram_val_bcm_int_filter(
const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype);
static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value,
void *outp, size_t *olen, bhnd_nvram_type otype);
static int bhnd_nvram_val_bcm_decimal_encode_elem(
bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_bcm_hex_encode_elem(
bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_bcm_leddc_filter(
const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype);
static int bhnd_nvram_val_bcm_leddc_encode_elem(
bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value,
void *outp, size_t *olen, bhnd_nvram_type otype);
static int bhnd_nvram_val_bcmstr_csv_filter(
const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype);
static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value,
const void *prev, size_t *len);
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_fmt = {
.name = "bcm-macaddr",
.native_type = BHND_NVRAM_TYPE_UINT8_ARRAY,
.op_filter = bhnd_nvram_val_bcm_macaddr_filter,
.op_encode = bhnd_nvram_val_bcm_macaddr_encode,
};
static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_string_fmt = {
.name = "bcm-macaddr-string",
.native_type = BHND_NVRAM_TYPE_STRING,
.op_filter = bhnd_nvram_val_bcm_macaddr_string_filter,
.op_encode_elem = bhnd_nvram_val_bcm_macaddr_string_encode_elem,
.op_next = bhnd_nvram_val_bcm_macaddr_string_next,
};
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_leddc_fmt = {
.name = "bcm-leddc",
.native_type = BHND_NVRAM_TYPE_UINT32,
.op_filter = bhnd_nvram_val_bcm_leddc_filter,
.op_encode_elem = bhnd_nvram_val_bcm_leddc_encode_elem,
};
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_decimal_fmt = {
.name = "bcm-decimal",
.native_type = BHND_NVRAM_TYPE_UINT64,
.op_filter = bhnd_nvram_val_bcm_int_filter,
.op_encode = bhnd_nvram_val_bcm_int_encode,
.op_encode_elem = bhnd_nvram_val_bcm_decimal_encode_elem,
};
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_hex_fmt = {
.name = "bcm-hex",
.native_type = BHND_NVRAM_TYPE_UINT64,
.op_filter = bhnd_nvram_val_bcm_int_filter,
.op_encode = bhnd_nvram_val_bcm_int_encode,
.op_encode_elem = bhnd_nvram_val_bcm_hex_encode_elem,
};
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_fmt = {
.name = "bcm-string",
.native_type = BHND_NVRAM_TYPE_STRING,
.op_encode = bhnd_nvram_val_bcmstr_encode,
};
static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_csv_fmt = {
.name = "bcm-string[]",
.native_type = BHND_NVRAM_TYPE_STRING,
.op_filter = bhnd_nvram_val_bcmstr_csv_filter,
.op_next = bhnd_nvram_val_bcmstr_csv_next,
};
#define BHND_NVRAM_VAL_FMT_NATIVE(_n, _type) \
const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt = { \
.name = __STRING(_n), \
.native_type = BHND_NVRAM_TYPE_ ## _type, \
}
BHND_NVRAM_VAL_FMT_NATIVE(uint8, UINT8);
BHND_NVRAM_VAL_FMT_NATIVE(uint16, UINT16);
BHND_NVRAM_VAL_FMT_NATIVE(uint32, UINT32);
BHND_NVRAM_VAL_FMT_NATIVE(uint64, UINT64);
BHND_NVRAM_VAL_FMT_NATIVE(int8, INT8);
BHND_NVRAM_VAL_FMT_NATIVE(int16, INT16);
BHND_NVRAM_VAL_FMT_NATIVE(int32, INT32);
BHND_NVRAM_VAL_FMT_NATIVE(int64, INT64);
BHND_NVRAM_VAL_FMT_NATIVE(char, CHAR);
BHND_NVRAM_VAL_FMT_NATIVE(bool, BOOL);
BHND_NVRAM_VAL_FMT_NATIVE(string, STRING);
BHND_NVRAM_VAL_FMT_NATIVE(data, DATA);
BHND_NVRAM_VAL_FMT_NATIVE(null, NULL);
BHND_NVRAM_VAL_FMT_NATIVE(uint8_array, UINT8_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(uint16_array, UINT16_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(uint32_array, UINT32_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(uint64_array, UINT64_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(int8_array, INT8_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(int16_array, INT16_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(int32_array, INT32_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(int64_array, INT64_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(char_array, CHAR_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(bool_array, BOOL_ARRAY);
BHND_NVRAM_VAL_FMT_NATIVE(string_array, STRING_ARRAY);
static int
bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,
size_t ilen, bhnd_nvram_type itype)
{
bhnd_nvram_type itype_base;
itype_base = bhnd_nvram_base_type(itype);
switch (itype_base) {
case BHND_NVRAM_TYPE_STRING:
*fmt = &bhnd_nvram_val_bcm_string_fmt;
return (0);
default:
if (bhnd_nvram_is_int_type(itype_base))
return (0);
return (EFTYPE);
}
}
static int
bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
bhnd_nvram_type otype)
{
if (otype == BHND_NVRAM_TYPE_STRING)
return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ","));
return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
}
static int
bhnd_nvram_val_bcm_hex_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;
ssize_t width;
int error;
itype = bhnd_nvram_val_elem_type(value);
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
if (otype != BHND_NVRAM_TYPE_STRING)
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
outp, olen, otype));
if (bhnd_nvram_is_signed_type(itype)) {
int64_t sval;
size_t slen;
bhnd_nvram_type stype;
stype = BHND_NVRAM_TYPE_INT64;
slen = sizeof(sval);
error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,
stype);
if (error)
return (error);
if (sval < 0)
return (bhnd_nvram_value_printf("%I64d", &sval, slen,
stype, outp, olen, otype));
}
width = bhnd_nvram_type_width(itype) * 2;
return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,
outp, olen, width));
}
static int
bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val *value, const void *inp,
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
{
const char *sfmt;
bhnd_nvram_type itype;
itype = bhnd_nvram_val_elem_type(value);
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
if (otype != BHND_NVRAM_TYPE_STRING)
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
outp, olen, otype));
sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u";
return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen));
}
static int
bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype)
{
const char *p;
size_t plen;
switch (itype) {
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_UINT32:
return (0);
case BHND_NVRAM_TYPE_STRING:
p = inp;
plen = bhnd_nvram_trim_field(&p, ilen, '\0');
if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))
*fmt = &bhnd_nvram_val_bcm_string_fmt;
return (0);
default:
return (EFTYPE);
}
}
static int
bhnd_nvram_val_bcm_leddc_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;
size_t limit, nbytes;
int error;
uint16_t led16;
uint32_t led32;
bool led16_lossy;
union {
uint16_t u16;
uint32_t u32;
} strval;
itype = bhnd_nvram_val_elem_type(value);
nbytes = 0;
led16_lossy = false;
if (outp != NULL)
limit = *olen;
else
limit = 0;
if (otype == itype) {
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
otype));
}
if (itype == BHND_NVRAM_TYPE_STRING) {
const char *p;
uint32_t ival;
size_t nlen, parsed;
p = inp;
nlen = sizeof(ival);
error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,
BHND_NVRAM_TYPE_UINT32);
if (error)
return (error);
if (parsed < ilen && *(p+parsed) != '\0')
return (EFTYPE);
inp = &strval;
if (ival & 0xFFFF0000) {
strval.u32 = ival;
itype = BHND_NVRAM_TYPE_UINT32;
} else {
strval.u16 = ival;
itype = BHND_NVRAM_TYPE_UINT16;
}
}
switch (itype) {
case BHND_NVRAM_TYPE_UINT16: {
led16 = *(const uint16_t *)inp;
led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);
if (led16 == UINT16_MAX)
led32 = UINT32_MAX;
break;
}
case BHND_NVRAM_TYPE_UINT32:
led32 = *(const uint32_t *)inp;
led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF);
if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX)
led16_lossy = true;
break;
default:
BHND_NV_PANIC("unsupported backing data type: %s",
bhnd_nvram_type_name(itype));
}
switch (otype) {
case BHND_NVRAM_TYPE_STRING:
if (!led16_lossy) {
return (bhnd_nvram_value_printf("0x%04hX", &led16,
sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen));
} else {
return (bhnd_nvram_value_printf("0x%04X", &led32,
sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen));
}
break;
case BHND_NVRAM_TYPE_UINT16: {
if (led16_lossy)
return (ERANGE);
nbytes += sizeof(uint16_t);
if (limit >= nbytes)
*(uint16_t *)outp = led16;
break;
}
case BHND_NVRAM_TYPE_UINT32:
nbytes += sizeof(uint32_t);
if (limit >= nbytes)
*(uint32_t *)outp = led32;
break;
default:
return (EFTYPE);
}
*olen = nbytes;
if (limit < nbytes && outp != NULL)
return (ENOMEM);
return (0);
}
static int
bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
bhnd_nvram_type otype)
{
bhnd_nvram_val array;
const bhnd_nvram_val_fmt *array_fmt;
const void *inp;
bhnd_nvram_type itype;
size_t ilen;
int error;
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
if (!bhnd_nvram_is_array_type(otype) ||
otype == BHND_NVRAM_TYPE_CHAR_ARRAY)
{
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
otype));
}
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
else
array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt;
error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype,
BHND_NVRAM_VAL_BORROW_DATA);
if (error) {
BHND_NV_LOG("error initializing array representation: %d\n",
error);
return (error);
}
error = bhnd_nvram_val_encode(&array, outp, olen, otype);
if (error)
BHND_NV_LOG("error encoding array representation: %d\n", error);
bhnd_nvram_val_release(&array);
return (error);
}
static int
bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype)
{
switch (itype) {
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
return (0);
default:
return (EFTYPE);
}
}
static const void *
bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev,
size_t *len)
{
const char *next;
const char *inp;
bhnd_nvram_type itype;
size_t ilen, remain;
char delim;
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
switch (itype) {
case BHND_NVRAM_TYPE_STRING:
if (ilen == 0)
return (NULL);
if (prev == NULL) {
next = inp;
remain = ilen;
delim = ',';
} else {
next = (const char *)prev + *len;
if ((size_t)(next - inp) >= ilen)
return (NULL);
delim = *next;
next++;
remain = ilen - (size_t)(next - inp);
if (remain == 0)
return (NULL);
}
*len = bhnd_nvram_parse_field(&next, remain, delim);
return (next);
case BHND_NVRAM_TYPE_STRING_ARRAY:
return (bhnd_nvram_value_array_next(inp, ilen, itype, prev,
len));
default:
BHND_NV_PANIC("unsupported type: %d", itype);
}
}
static int
bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype)
{
switch (itype) {
case BHND_NVRAM_TYPE_UINT8_ARRAY:
return (0);
case BHND_NVRAM_TYPE_STRING:
*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
return (0);
default:
return (EFTYPE);
}
}
static int
bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val *value, void *outp,
size_t *olen, bhnd_nvram_type otype)
{
const void *inp;
bhnd_nvram_type itype;
size_t ilen;
if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {
return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,
":"));
}
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}
static int
bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype)
{
switch (itype) {
case BHND_NVRAM_TYPE_STRING:
if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
*fmt = &bhnd_nvram_val_bcm_string_fmt;
return (0);
default:
return (EFTYPE);
}
}
static int
bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val *value,
const void *inp, size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype)
{
size_t nparsed;
int error;
if (bhnd_nvram_is_int_type(otype)) {
error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp,
olen, otype);
if (error)
return (error);
if (nparsed != ilen)
return (EFTYPE);
return (0);
}
return (bhnd_nvram_value_coerce(inp, ilen,
bhnd_nvram_val_elem_type(value), outp, olen, otype));
}
static const void *
bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val *value, const void *prev,
size_t *len)
{
const char *next;
const char *str;
bhnd_nvram_type stype;
size_t slen, remain;
char delim;
str = bhnd_nvram_val_bytes(value, &slen, &stype);
BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,
("unsupported type: %d", stype));
if (slen == 0)
return (NULL);
if (prev == NULL) {
if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {
delim = ',';
}
next = str;
remain = slen;
} else {
next = (const char *)prev + *len;
if ((size_t)(next - str) >= slen)
return (NULL);
delim = *next;
next++;
remain = slen - (size_t)(next - str);
if (remain == 0)
return (NULL);
}
*len = bhnd_nvram_parse_field(&next, remain, delim);
return (next);
}
static bool
bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim,
size_t *nelem)
{
size_t elem_count;
size_t max_elem_count, min_elem_count;
size_t field_count;
char idelim;
field_count = 0;
min_elem_count = 2;
max_elem_count = 2;
for (const char *d = ":-";; d++) {
const char *loc;
if (*d == '\0')
return (false);
if ((loc = memchr(inp, *d, ilen)) == NULL)
continue;
idelim = *loc;
break;
}
if (idelim == '-')
min_elem_count = 2;
elem_count = 0;
for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {
switch (*p) {
case ':':
case '-':
case '\0':
if (*p != '\0' && *p != idelim)
return (false);
if (elem_count < min_elem_count)
return (false);
elem_count = 0;
field_count++;
break;
default:
if (elem_count >= max_elem_count)
return (false);
if (!bhnd_nv_isxdigit(*p))
return (false);
elem_count++;
break;
}
}
if (delim != NULL)
*delim = idelim;
if (nelem != NULL)
*nelem = field_count;
return (true);
}
static bool
bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,
u_int *obase)
{
size_t nbytes, ndigits;
nbytes = 0;
ndigits = 0;
if (nbytes >= ilen)
return (false);
if (inp[nbytes] == '-' || inp[nbytes] == '+')
nbytes++;
if (nbytes == ilen)
return (false);
if (base == 16 || base == 0) {
if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
(inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
{
base = 16;
nbytes += 2;
}
}
if (nbytes == ilen)
return (false);
if (base == 0) {
if (inp[nbytes] == '0') {
base = 8;
} else {
base = 10;
}
}
for (; nbytes < ilen; nbytes++) {
u_int carry;
char c;
c = inp[nbytes];
if (bhnd_nv_isdigit(c)) {
carry = c - '0';
} else if (bhnd_nv_isxdigit(c)) {
if (bhnd_nv_isupper(c))
carry = (c - 'A') + 10;
else
carry = (c - 'a') + 10;
} else {
return (false);
}
if (carry >= base)
return (false);
ndigits++;
}
if (ndigits == 0)
return (false);
if (obase != NULL)
*obase = base;
return (true);
}