6cffadf0f0
NULL (which we'll use to denote deleted values in bhnd_nvram_store), and opaque data (aka octet-strings). Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D8758
1057 lines
28 KiB
C
1057 lines
28 KiB
C
/*-
|
|
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
|
* redistribution must be conditioned upon including a substantially
|
|
* similar Disclaimer requirement for further binary redistribution.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#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 /* !_KERNEL */
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#endif /* _KERNEL */
|
|
|
|
#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);
|
|
|
|
/**
|
|
* Broadcom NVRAM MAC address format.
|
|
*/
|
|
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,
|
|
};
|
|
|
|
/** Broadcom NVRAM MAC address string format. */
|
|
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,
|
|
};
|
|
|
|
/**
|
|
* Broadcom NVRAM LED duty-cycle format.
|
|
*/
|
|
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,
|
|
};
|
|
|
|
/**
|
|
* Broadcom NVRAM decimal integer format.
|
|
*
|
|
* Extends standard integer handling, encoding the string representation of
|
|
* the integer value as a decimal string:
|
|
* - Positive values will be string-encoded without a prefix.
|
|
* - Negative values will be string-encoded with a leading '-' sign.
|
|
*/
|
|
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,
|
|
};
|
|
|
|
/**
|
|
* Broadcom NVRAM decimal integer format.
|
|
*
|
|
* Extends standard integer handling, encoding the string representation of
|
|
* unsigned and positive signed integer values as an 0x-prefixed hexadecimal
|
|
* string.
|
|
*
|
|
* For compatibility with standard Broadcom NVRAM parsing, if the integer is
|
|
* both signed and negative, it will be string encoded as a negative decimal
|
|
* value, not as a twos-complement hexadecimal value.
|
|
*/
|
|
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,
|
|
};
|
|
|
|
/**
|
|
* Broadcom NVRAM string format.
|
|
*
|
|
* Handles standard, comma-delimited, and octet-string values as used in
|
|
* Broadcom NVRAM data.
|
|
*/
|
|
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,
|
|
};
|
|
|
|
/** Broadcom comma-delimited string. */
|
|
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,
|
|
};
|
|
|
|
|
|
/* Built-in format definitions */
|
|
#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);
|
|
|
|
/**
|
|
* Common hex/decimal integer filter implementation.
|
|
*/
|
|
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:
|
|
/*
|
|
* If the input is a string, delegate to the Broadcom
|
|
* string format -- preserving the original string value
|
|
* takes priority over enforcing hexadecimal/integer string
|
|
* formatting.
|
|
*/
|
|
*fmt = &bhnd_nvram_val_bcm_string_fmt;
|
|
return (0);
|
|
|
|
default:
|
|
if (bhnd_nvram_is_int_type(itype_base))
|
|
return (0);
|
|
|
|
return (EFTYPE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Broadcom hex/decimal integer encode implementation.
|
|
*/
|
|
static int
|
|
bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
|
|
bhnd_nvram_type otype)
|
|
{
|
|
/* If encoding to a string, format multiple elements (if any) with a
|
|
* comma delimiter. */
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* Broadcom hex integer encode_elem implementation.
|
|
*/
|
|
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 not encoding as a string, perform generic value encoding */
|
|
if (otype != BHND_NVRAM_TYPE_STRING)
|
|
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
|
|
outp, olen, otype));
|
|
|
|
/* If the value is a signed, negative value, encode as a decimal
|
|
* string */
|
|
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);
|
|
|
|
/* Fetch 64-bit signed representation */
|
|
error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,
|
|
stype);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Decimal encoding required? */
|
|
if (sval < 0)
|
|
return (bhnd_nvram_value_printf("%I64d", &sval, slen,
|
|
stype, outp, olen, otype));
|
|
}
|
|
|
|
/*
|
|
* Encode the value as a hex string.
|
|
*
|
|
* Most producers of Broadcom NVRAM values zero-pad hex values out to
|
|
* their native width (width * two hex characters), and we do the same
|
|
* for compatibility
|
|
*/
|
|
width = bhnd_nvram_type_width(itype) * 2;
|
|
return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,
|
|
outp, olen, width));
|
|
}
|
|
|
|
/**
|
|
* Broadcom decimal integer encode_elem implementation.
|
|
*/
|
|
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 not encoding as a string, perform generic value encoding */
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* Broadcom LED duty-cycle filter.
|
|
*/
|
|
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:
|
|
/* Trim any whitespace */
|
|
p = inp;
|
|
plen = bhnd_nvram_trim_field(&p, ilen, '\0');
|
|
|
|
/* If the value is not a valid integer string, delegate to the
|
|
* Broadcom string format */
|
|
if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))
|
|
*fmt = &bhnd_nvram_val_bcm_string_fmt;
|
|
|
|
return (0);
|
|
default:
|
|
return (EFTYPE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Broadcom LED duty-cycle encode.
|
|
*/
|
|
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;
|
|
|
|
/*
|
|
* LED duty-cycle values represent the on/off periods as a 32-bit
|
|
* integer, with the top 16 bits representing on cycles, and the
|
|
* bottom 16 representing off cycles.
|
|
*
|
|
* LED duty cycle values have three different formats:
|
|
*
|
|
* - SPROM: A 16-bit unsigned integer, with on/off cycles encoded
|
|
* as 8-bit values.
|
|
* - NVRAM: A 16-bit decimal or hexadecimal string, with on/off
|
|
* cycles encoded as 8-bit values as per the SPROM format.
|
|
* - NVRAM: A 32-bit decimal or hexadecimal string, with on/off
|
|
* cycles encoded as 16-bit values.
|
|
*
|
|
* To convert from a 16-bit representation to a 32-bit representation:
|
|
* ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8)
|
|
*
|
|
* To convert from a 32-bit representation to a 16-bit representation,
|
|
* perform the same operation in reverse, discarding the lower 8-bits
|
|
* of each half of the 32-bit representation:
|
|
* ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF)
|
|
*/
|
|
|
|
itype = bhnd_nvram_val_elem_type(value);
|
|
nbytes = 0;
|
|
led16_lossy = false;
|
|
|
|
/* Determine output byte limit */
|
|
if (outp != NULL)
|
|
limit = *olen;
|
|
else
|
|
limit = 0;
|
|
|
|
/* If the input/output types match, just delegate to standard value
|
|
* encoding support */
|
|
if (otype == itype) {
|
|
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
|
|
otype));
|
|
}
|
|
|
|
/* If our value is a string, it may either be a 16-bit or a 32-bit
|
|
* representation of the duty cycle */
|
|
if (itype == BHND_NVRAM_TYPE_STRING) {
|
|
const char *p;
|
|
uint32_t ival;
|
|
size_t nlen, parsed;
|
|
|
|
/* Parse integer value */
|
|
p = inp;
|
|
nlen = sizeof(ival);
|
|
error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,
|
|
BHND_NVRAM_TYPE_UINT32);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* Trailing garbage? */
|
|
if (parsed < ilen && *(p+parsed) != '\0')
|
|
return (EFTYPE);
|
|
|
|
/* Point inp and itype to either our parsed 32-bit or 16-bit
|
|
* value */
|
|
inp = &strval;
|
|
if (ival & 0xFFFF0000) {
|
|
strval.u32 = ival;
|
|
itype = BHND_NVRAM_TYPE_UINT32;
|
|
} else {
|
|
strval.u16 = ival;
|
|
itype = BHND_NVRAM_TYPE_UINT16;
|
|
}
|
|
}
|
|
|
|
/* Populate both u32 and (possibly lossy) u16 LEDDC representations */
|
|
switch (itype) {
|
|
case BHND_NVRAM_TYPE_UINT16: {
|
|
led16 = *(const uint16_t *)inp;
|
|
led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);
|
|
|
|
/* If all bits are set in the 16-bit value (indicating that
|
|
* the value is 'unset' in SPROM), we must update the 32-bit
|
|
* representation to match. */
|
|
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);
|
|
|
|
/*
|
|
* Determine whether the led16 conversion is lossy:
|
|
*
|
|
* - If the lower 8 bits of each half of the 32-bit value
|
|
* aren't set, we can safely use the 16-bit representation
|
|
* without losing data.
|
|
* - If all bits in the 32-bit value are set, the variable is
|
|
* treated as unset in SPROM. We can safely use the 16-bit
|
|
* representation without losing data.
|
|
*/
|
|
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));
|
|
}
|
|
|
|
/*
|
|
* Encode as requested output type.
|
|
*/
|
|
switch (otype) {
|
|
case BHND_NVRAM_TYPE_STRING:
|
|
/*
|
|
* Prefer 16-bit format.
|
|
*/
|
|
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: {
|
|
/* Can we encode as uint16 without losing data? */
|
|
if (led16_lossy)
|
|
return (ERANGE);
|
|
|
|
/* Write led16 format */
|
|
nbytes += sizeof(uint16_t);
|
|
if (limit >= nbytes)
|
|
*(uint16_t *)outp = led16;
|
|
|
|
break;
|
|
}
|
|
|
|
case BHND_NVRAM_TYPE_UINT32:
|
|
/* Write led32 format */
|
|
nbytes += sizeof(uint32_t);
|
|
if (limit >= nbytes)
|
|
*(uint32_t *)outp = led32;
|
|
break;
|
|
|
|
default:
|
|
/* No other output formats are supported */
|
|
return (EFTYPE);
|
|
}
|
|
|
|
/* Provide the actual length */
|
|
*olen = nbytes;
|
|
|
|
/* Report insufficient space (if output was requested) */
|
|
if (limit < nbytes && outp != NULL)
|
|
return (ENOMEM);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* Broadcom NVRAM string encoding.
|
|
*/
|
|
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 the output is not an array type (or if it's a character array),
|
|
* we can fall back on standard string encoding */
|
|
if (!bhnd_nvram_is_array_type(otype) ||
|
|
otype == BHND_NVRAM_TYPE_CHAR_ARRAY)
|
|
{
|
|
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
|
|
otype));
|
|
}
|
|
|
|
/* Otherwise, we need to interpret our value as either a macaddr
|
|
* string, or a comma-delimited string. */
|
|
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;
|
|
|
|
/* Wrap in array-typed representation */
|
|
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);
|
|
}
|
|
|
|
/* Ask the array-typed value to perform the encode */
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Broadcom NVRAM comma-delimited string filter.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Broadcom NVRAM comma-delimited string iteration.
|
|
*/
|
|
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;
|
|
|
|
/* Fetch backing representation */
|
|
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
|
|
|
|
/* Fetch next value */
|
|
switch (itype) {
|
|
case BHND_NVRAM_TYPE_STRING:
|
|
/* Zero-length array? */
|
|
if (ilen == 0)
|
|
return (NULL);
|
|
|
|
if (prev == NULL) {
|
|
/* First element */
|
|
next = inp;
|
|
remain = ilen;
|
|
delim = ',';
|
|
} else {
|
|
/* Advance to the previous element's delimiter */
|
|
next = (const char *)prev + *len;
|
|
|
|
/* Did we hit the end of the string? */
|
|
if ((size_t)(next - inp) >= ilen)
|
|
return (NULL);
|
|
|
|
/* Fetch (and skip past) the delimiter */
|
|
delim = *next;
|
|
next++;
|
|
remain = ilen - (size_t)(next - inp);
|
|
|
|
/* Was the delimiter the final character? */
|
|
if (remain == 0)
|
|
return (NULL);
|
|
}
|
|
|
|
/* Parse the field value, up to the next delimiter */
|
|
*len = bhnd_nvram_parse_field(&next, remain, delim);
|
|
|
|
return (next);
|
|
|
|
case BHND_NVRAM_TYPE_STRING_ARRAY:
|
|
/* Delegate to default array iteration */
|
|
return (bhnd_nvram_value_array_next(inp, ilen, itype, prev,
|
|
len));
|
|
default:
|
|
BHND_NV_PANIC("unsupported type: %d", itype);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MAC address filter.
|
|
*/
|
|
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:
|
|
/* Let bcm_macaddr_string format handle it */
|
|
*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
|
|
return (0);
|
|
default:
|
|
return (EFTYPE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MAC address encoding.
|
|
*/
|
|
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 converting to a string (or a single-element string array),
|
|
* produce an octet string (00:00:...).
|
|
*/
|
|
if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {
|
|
return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,
|
|
":"));
|
|
}
|
|
|
|
/* Otherwise, use standard encoding support */
|
|
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
|
|
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}
|
|
|
|
/**
|
|
* MAC address string filter.
|
|
*/
|
|
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:
|
|
/* Use the standard Broadcom string format implementation if
|
|
* the input is not an octet string. */
|
|
if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
|
|
*fmt = &bhnd_nvram_val_bcm_string_fmt;
|
|
|
|
return (0);
|
|
default:
|
|
return (EFTYPE);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* MAC address string octet encoding.
|
|
*/
|
|
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 integer encoding is requested, explicitly parse our
|
|
* non-0x-prefixed as a base 16 integer value */
|
|
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);
|
|
}
|
|
|
|
/* Otherwise, use standard encoding support */
|
|
return (bhnd_nvram_value_coerce(inp, ilen,
|
|
bhnd_nvram_val_elem_type(value), outp, olen, otype));
|
|
}
|
|
|
|
/**
|
|
* MAC address string octet iteration.
|
|
*/
|
|
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;
|
|
|
|
/* Fetch backing string */
|
|
str = bhnd_nvram_val_bytes(value, &slen, &stype);
|
|
BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,
|
|
("unsupported type: %d", stype));
|
|
|
|
/* Zero-length array? */
|
|
if (slen == 0)
|
|
return (NULL);
|
|
|
|
if (prev == NULL) {
|
|
/* First element */
|
|
|
|
/* Determine delimiter */
|
|
if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {
|
|
/* Default to comma-delimited parsing */
|
|
delim = ',';
|
|
}
|
|
|
|
/* Parsing will start at the base string pointer */
|
|
next = str;
|
|
remain = slen;
|
|
} else {
|
|
/* Advance to the previous element's delimiter */
|
|
next = (const char *)prev + *len;
|
|
|
|
/* Did we hit the end of the string? */
|
|
if ((size_t)(next - str) >= slen)
|
|
return (NULL);
|
|
|
|
/* Fetch (and skip past) the delimiter */
|
|
delim = *next;
|
|
next++;
|
|
remain = slen - (size_t)(next - str);
|
|
|
|
/* Was the delimiter the final character? */
|
|
if (remain == 0)
|
|
return (NULL);
|
|
}
|
|
|
|
/* Parse the field value, up to the next delimiter */
|
|
*len = bhnd_nvram_parse_field(&next, remain, delim);
|
|
|
|
return (next);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine whether @p inp is in octet string format, consisting of a
|
|
* fields of two hex characters, separated with ':' or '-' delimiters.
|
|
*
|
|
* This may be used to identify MAC address octet strings
|
|
* (BHND_NVRAM_SFMT_MACADDR).
|
|
*
|
|
* @param inp The string to be parsed.
|
|
* @param ilen The length of @p inp, in bytes.
|
|
* @param[out] delim On success, the delimiter used by this octet
|
|
* string. May be set to NULL if the field
|
|
* delimiter is not desired.
|
|
* @param[out] nelem On success, the number of fields in this
|
|
* octet string. May be set to NULL if the field
|
|
* count is not desired.
|
|
*
|
|
*
|
|
* @retval true if @p inp is a valid octet string
|
|
* @retval false if @p inp is not a valid octet string.
|
|
*/
|
|
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;
|
|
|
|
/* Require exactly two digits. If we relax this, there is room
|
|
* for ambiguity with signed integers and the '-' delimiter */
|
|
min_elem_count = 2;
|
|
max_elem_count = 2;
|
|
|
|
/* Identify the delimiter used. The standard delimiter for MAC
|
|
* addresses is ':', but some earlier NVRAM formats may use '-' */
|
|
for (const char *d = ":-";; d++) {
|
|
const char *loc;
|
|
|
|
/* No delimiter found, not an octet string */
|
|
if (*d == '\0')
|
|
return (false);
|
|
|
|
/* Look for the delimiter */
|
|
if ((loc = memchr(inp, *d, ilen)) == NULL)
|
|
continue;
|
|
|
|
/* Delimiter found */
|
|
idelim = *loc;
|
|
break;
|
|
}
|
|
|
|
/* To disambiguate from signed integers, if the delimiter is "-",
|
|
* the octets must be exactly 2 chars each */
|
|
if (idelim == '-')
|
|
min_elem_count = 2;
|
|
|
|
/* String must be composed of individual octets (zero or more hex
|
|
* digits) separated by our delimiter. */
|
|
elem_count = 0;
|
|
for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {
|
|
switch (*p) {
|
|
case ':':
|
|
case '-':
|
|
case '\0':
|
|
/* Hit a delim character; all delims must match
|
|
* the first delimiter used */
|
|
if (*p != '\0' && *p != idelim)
|
|
return (false);
|
|
|
|
/* Must have parsed at least min_elem_count digits */
|
|
if (elem_count < min_elem_count)
|
|
return (false);
|
|
|
|
/* Reset element count */
|
|
elem_count = 0;
|
|
|
|
/* Bump field count */
|
|
field_count++;
|
|
break;
|
|
default:
|
|
/* More than maximum number of hex digits? */
|
|
if (elem_count >= max_elem_count)
|
|
return (false);
|
|
|
|
/* Octet values must be hex digits */
|
|
if (!bhnd_nv_isxdigit(*p))
|
|
return (false);
|
|
|
|
elem_count++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (delim != NULL)
|
|
*delim = idelim;
|
|
|
|
if (nelem != NULL)
|
|
*nelem = field_count;
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine whether @p inp is in hexadecimal, octal, or decimal string
|
|
* format.
|
|
*
|
|
* - A @p str may be prefixed with a single optional '+' or '-' sign denoting
|
|
* signedness.
|
|
* - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
|
|
* base 16 integer follows.
|
|
* - An octal @p str may include a '0' prefix, denoting that an octal integer
|
|
* follows.
|
|
*
|
|
* @param inp The string to be parsed.
|
|
* @param ilen The length of @p inp, in bytes.
|
|
* @param base The input string's base (2-36), or 0.
|
|
* @param[out] obase On success, will be set to the base of the parsed
|
|
* integer. May be set to NULL if the base is not
|
|
* desired.
|
|
*
|
|
* @retval true if @p inp is a valid number string
|
|
* @retval false if @p inp is not a valid number string.
|
|
* @retval false if @p base is invalid.
|
|
*/
|
|
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;
|
|
|
|
/* Parse and skip sign */
|
|
if (nbytes >= ilen)
|
|
return (false);
|
|
|
|
if (inp[nbytes] == '-' || inp[nbytes] == '+')
|
|
nbytes++;
|
|
|
|
/* Truncated after sign character? */
|
|
if (nbytes == ilen)
|
|
return (false);
|
|
|
|
/* Identify (or validate) hex base, skipping 0x/0X prefix */
|
|
if (base == 16 || base == 0) {
|
|
/* Check for (and skip) 0x/0X prefix */
|
|
if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
|
|
(inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
|
|
{
|
|
base = 16;
|
|
nbytes += 2;
|
|
}
|
|
}
|
|
|
|
/* Truncated after hex prefix? */
|
|
if (nbytes == ilen)
|
|
return (false);
|
|
|
|
/* Differentiate decimal/octal by looking for a leading 0 */
|
|
if (base == 0) {
|
|
if (inp[nbytes] == '0') {
|
|
base = 8;
|
|
} else {
|
|
base = 10;
|
|
}
|
|
}
|
|
|
|
/* Consume and validate all remaining digit characters */
|
|
for (; nbytes < ilen; nbytes++) {
|
|
u_int carry;
|
|
char c;
|
|
|
|
/* Parse carry value */
|
|
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 {
|
|
/* Hit a non-digit character */
|
|
return (false);
|
|
}
|
|
|
|
/* If carry is outside the base, it's not a valid digit
|
|
* in the current parse context; consider it a non-digit
|
|
* character */
|
|
if (carry >= base)
|
|
return (false);
|
|
|
|
/* Increment parsed digit count */
|
|
ndigits++;
|
|
}
|
|
|
|
/* Empty integer string? */
|
|
if (ndigits == 0)
|
|
return (false);
|
|
|
|
/* Valid integer -- provide the base and return */
|
|
if (obase != NULL)
|
|
*obase = base;
|
|
return (true);
|
|
}
|