bhnd(4): NVRAM device path support.

Implements bhnd_nvram_store support for parsing and operating over NVRAM
device paths, and device path aliases, as well as tracking per-path NVRAM
variable writes.

Approved by:	adrian (mentor)
Differential Revision:	https://reviews.freebsd.org/D8760
This commit is contained in:
Landon J. Fuller 2016-12-19 20:28:27 +00:00
parent f76db8de03
commit 19be09f31c
16 changed files with 2635 additions and 530 deletions

View File

@ -1244,6 +1244,7 @@ dev/bhnd/nvram/bhnd_nvram_ioptr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_plist.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_store_subr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd

View File

@ -350,7 +350,26 @@ bhnd_nvram_data_caps(struct bhnd_nvram_data *nv)
const char *
bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep)
{
return (nv->cls->op_next(nv, cookiep));
const char *name;
#ifdef BHND_NV_INVARIANTS
void *prev = *cookiep;
#endif
/* Fetch next */
if ((name = nv->cls->op_next(nv, cookiep)) == NULL)
return (NULL);
/* Enforce precedence ordering invariant between bhnd_nvram_data_next()
* and bhnd_nvram_data_getvar_order() */
#ifdef BHND_NV_INVARIANTS
if (prev != NULL &&
bhnd_nvram_data_getvar_order(nv, prev, *cookiep) > 0)
{
BHND_NV_PANIC("%s: returned out-of-order entry", __FUNCTION__);
}
#endif
return (name);
}
/**
@ -388,7 +407,7 @@ bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
cookiep = NULL;
while ((next = bhnd_nvram_data_next(nv, &cookiep))) {
if (strcasecmp(name, next) == 0)
if (strcmp(name, next) == 0)
return (cookiep);
}
@ -396,6 +415,37 @@ bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
return (NULL);
}
/**
* Compare the declaration order of two NVRAM variables.
*
* Variable declaration order is used to determine the current order of
* the variables in the source data, as well as to determine the precedence
* of variable declarations in data sources that define duplicate names.
*
* The comparison order will match the order of variables returned via
* bhnd_nvstore_path_data_next().
*
* @param nv The NVRAM data.
* @param cookiep1 An NVRAM variable cookie previously
* returned via bhnd_nvram_data_next() or
* bhnd_nvram_data_find().
* @param cookiep2 An NVRAM variable cookie previously
* returned via bhnd_nvram_data_next() or
* bhnd_nvram_data_find().
*
* @retval <= -1 If @p cookiep1 has an earlier declaration order than
* @p cookiep2.
* @retval 0 If @p cookiep1 and @p cookiep2 are identical.
* @retval >= 1 If @p cookiep has a later declaration order than
* @p cookiep2.
*/
int
bhnd_nvram_data_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
return (nv->cls->op_getvar_order(nv, cookiep1, cookiep2));
}
/**
* Read a variable and decode as @p type.
*
@ -423,6 +473,58 @@ bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (nv->cls->op_getvar(nv, cookiep, buf, len, type));
}
/*
* Common bhnd_nvram_data_getvar_ptr() wrapper used by
* bhnd_nvram_data_generic_rp_getvar() and
* bhnd_nvram_data_generic_rp_copy_val().
*
* If a variable definition for the requested variable is found via
* bhnd_nvram_find_vardefn(), the definition will be used to populate fmt.
*/
static const void *
bhnd_nvram_data_getvar_ptr_info(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type, const bhnd_nvram_val_fmt **fmt)
{
const struct bhnd_nvram_vardefn *vdefn;
const char *name;
const void *vptr;
BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,
("instance does not advertise READ_PTR support"));
/* Fetch pointer to variable data */
vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, len, type);
if (vptr == NULL)
return (NULL);
/* Select a default value format implementation */
/* Fetch the reference variable name */
name = bhnd_nvram_data_getvar_name(nv, cookiep);
/* Trim path prefix, if any; the Broadcom NVRAM format assumes a global
* namespace for all variable definitions */
if (bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_DEVPATHS)
name = bhnd_nvram_trim_path_name(name);
/* Check the variable definition table for a matching entry; if
* it exists, use it to populate the value format. */
vdefn = bhnd_nvram_find_vardefn(name);
if (vdefn != NULL) {
BHND_NV_ASSERT(vdefn->fmt != NULL,
("NULL format for %s", name));
*fmt = vdefn->fmt;
} else if (*type == BHND_NVRAM_TYPE_STRING) {
/* Default to Broadcom-specific string interpretation */
*fmt = &bhnd_nvram_val_bcm_string_fmt;
} else {
/* Fall back on native formatting */
*fmt = bhnd_nvram_val_default_fmt(*type);
}
return (vptr);
}
/**
* A generic implementation of bhnd_nvram_data_getvar().
@ -432,17 +534,15 @@ bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
* of the caller.
*
* If a variable definition for the requested variable is available via
* bhnd_nvram_find_vardefn(), the definition will be used to provide
* formatting hints to bhnd_nvram_coerce_value().
* bhnd_nvram_find_vardefn(), the definition will be used to provide a
* formatting instance to bhnd_nvram_val_init().
*/
int
bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
bhnd_nvram_val val;
const struct bhnd_nvram_vardefn *vdefn;
const bhnd_nvram_val_fmt *fmt;
const char *name;
const void *vptr;
bhnd_nvram_type vtype;
size_t vlen;
@ -451,28 +551,12 @@ bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,
("instance does not advertise READ_PTR support"));
/* Fetch pointer to our variable data */
vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype);
/* Fetch variable data and value format*/
vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype,
&fmt);
if (vptr == NULL)
return (EINVAL);
/* Use the NVRAM string support */
switch (vtype) {
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
fmt = &bhnd_nvram_val_bcm_string_fmt;
break;
default:
fmt = NULL;
}
/* Check the variable definition table for a matching entry; if
* it exists, use it to populate the value format. */
name = bhnd_nvram_data_getvar_name(nv, cookiep);
vdefn = bhnd_nvram_find_vardefn(name);
if (vdefn != NULL)
fmt = vdefn->fmt;
/* Attempt value coercion */
error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype,
BHND_NVRAM_VAL_BORROW_DATA);
@ -486,6 +570,63 @@ bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
return (error);
}
/**
* Return a caller-owned copy of an NVRAM entry's variable data.
*
* The caller is responsible for deallocating the returned value via
* bhnd_nvram_val_release().
*
* @param nv The NVRAM data.
* @param cookiep An NVRAM variable cookie previously returned
* via bhnd_nvram_data_next() or bhnd_nvram_data_find().
* @param[out] value On success, the caller-owned value instance.
*
* @retval 0 success
* @retval ENOMEM If allocation fails.
* @retval non-zero If initialization of the value otherwise fails, a
* regular unix error code will be returned.
*/
int
bhnd_nvram_data_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (nv->cls->op_copy_val(nv, cookiep, value));
}
/**
* A generic implementation of bhnd_nvram_data_copy_val().
*
* This implementation will call bhnd_nvram_data_getvar_ptr() to fetch
* a pointer to the variable data and perform data coercion on behalf
* of the caller.
*
* If a variable definition for the requested variable is available via
* bhnd_nvram_find_vardefn(), the definition will be used to provide a
* formatting instance to bhnd_nvram_val_init().
*/
int
bhnd_nvram_data_generic_rp_copy_val(struct bhnd_nvram_data *nv,
void *cookiep, bhnd_nvram_val **value)
{
const bhnd_nvram_val_fmt *fmt;
const void *vptr;
bhnd_nvram_type vtype;
size_t vlen;
BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,
("instance does not advertise READ_PTR support"));
/* Fetch variable data and value format*/
vptr = bhnd_nvram_data_getvar_ptr_info(nv, cookiep, &vlen, &vtype,
&fmt);
if (vptr == NULL)
return (EINVAL);
/* Allocate and return the new value instance */
return (bhnd_nvram_val_new(value, fmt, vptr, vlen, vtype,
BHND_NVRAM_VAL_DYNAMIC));
}
/**
* If available and supported by the NVRAM data instance, return a reference
* to the internal buffer containing an entry's variable data,
@ -526,3 +667,44 @@ bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
return (nv->cls->op_getvar_name(nv, cookiep));
}
/**
* Filter a request to set variable @p name with @p value.
*
* On success, the caller owns a reference to @p result, and must release
* any held resources via bhnd_nvram_val_release().
*
* @param nv The NVRAM data instance.
* @param name The name of the variable to be set.
* @param value The proposed value to be set.
* @param[out] result On success, a caller-owned reference to the filtered
* value to be set.
*
* @retval 0 success
* @retval ENOENT if @p name is unrecognized by @p nv.
* @retval EINVAL if @p name is read-only.
* @retval EINVAL if @p value cannot be converted to the required value
* type.
*/
int
bhnd_nvram_data_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
return (nv->cls->op_filter_setvar(nv, name, value, result));
}
/**
* Filter a request to delete variable @p name.
*
* @param nv The NVRAM data instance.
* @param name The name of the variable to be deleted.
*
* @retval 0 success
* @retval ENOENT if @p name is unrecognized by @p nv.
* @retval EINVAL if @p name is read-only.
*/
int
bhnd_nvram_data_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
return (nv->cls->op_filter_unsetvar(nv, name));
}

View File

@ -44,6 +44,7 @@
#include "bhnd_nvram.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_value.h"
/* NVRAM data class */
typedef struct bhnd_nvram_data_class bhnd_nvram_data_class;
@ -108,7 +109,6 @@ void bhnd_nvram_data_release(struct bhnd_nvram_data *nv);
bhnd_nvram_data_class *bhnd_nvram_data_get_class(struct bhnd_nvram_data *nv);
size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv);
int bhnd_nvram_data_size(struct bhnd_nvram_data *nv,
size_t *size);
@ -119,10 +119,13 @@ uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv);
const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv,
void **cookiep);
void *bhnd_nvram_data_find(struct bhnd_nvram_data *nv,
const char *name);
int bhnd_nvram_data_getvar_order(
struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2);
int bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv,
void *cookiep, void *buf, size_t *len,
bhnd_nvram_type type);
@ -133,4 +136,13 @@ const void *bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv,
const char *bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv,
void *cookiep);
int bhnd_nvram_data_copy_val(struct bhnd_nvram_data *nv,
void *cookiep, bhnd_nvram_val **val);
int bhnd_nvram_data_filter_setvar(
struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result);
int bhnd_nvram_data_filter_unsetvar(
struct bhnd_nvram_data *nv, const char *name);
#endif /* _BHND_NVRAM_BHND_NVRAM_DATA_H_ */

View File

@ -618,7 +618,7 @@ bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
return (NULL);
}
*cookiep = (void *)(uintptr_t)envp;
*cookiep = __DECONST(void *, envp);
return (envp);
}
@ -628,6 +628,39 @@ bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
struct bhnd_nvram_bcm *bcm;
struct bhnd_nvram_bcm_hvar *hvar1, *hvar2;
bcm = (struct bhnd_nvram_bcm *)nv;
hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
/* Header variables are always ordered below any variables defined
* in the BCM data */
if (hvar1 != NULL && hvar2 == NULL) {
return (1); /* hvar follows non-hvar */
} else if (hvar1 == NULL && hvar2 != NULL) {
return (-1); /* non-hvar precedes hvar */
}
/* Otherwise, both cookies are either hvars or non-hvars. We can
* safely fall back on pointer order, which will provide a correct
* ordering matching the behavior of bhnd_nvram_data_next() for
* both cases */
if (cookiep1 < cookiep2)
return (-1);
if (cookiep1 > cookiep2)
return (1);
return (0);
}
static int
bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
@ -635,6 +668,13 @@ bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static int
bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
}
static const void *
bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
@ -683,6 +723,35 @@ bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
return (cookiep);
}
static int
bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
bhnd_nvram_val *str;
int error;
/* Name (trimmed of any path prefix) must be valid */
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
return (EINVAL);
/* Value must be bcm-formatted string */
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
value, BHND_NVRAM_VAL_DYNAMIC);
if (error)
return (error);
/* Success. Transfer result ownership to the caller. */
*result = str;
return (0);
}
static int
bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
/* We permit deletion of any variable */
return (0);
}
/**
* Return the internal BCM data reference for a header-defined variable
* with @p name, or NULL if none exists.

View File

@ -350,6 +350,19 @@ bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
if (cookiep1 < cookiep2)
return (-1);
if (cookiep1 > cookiep2)
return (1);
return (0);
}
static int
bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
@ -357,6 +370,13 @@ bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static int
bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
}
static const void *
bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
@ -378,3 +398,32 @@ bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
/* Cookie points to key\0value\0 */
return (cookiep);
}
static int
bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
bhnd_nvram_val *str;
int error;
/* Name (trimmed of any path prefix) must be valid */
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
return (EINVAL);
/* Value must be bcm-formatted string */
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
value, BHND_NVRAM_VAL_DYNAMIC);
if (error)
return (error);
/* Success. Transfer result ownership to the caller. */
*result = str;
return (0);
}
static int
bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
/* We permit deletion of any variable */
return (0);
}

View File

@ -77,8 +77,10 @@ union bhnd_nvram_btxt_ident {
char btxt[8];
};
static size_t bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt,
void *cookiep);
static void *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
size_t io_offset);
static size_t bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt,
void *cookiep);
static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io,
size_t offset, size_t *line_len, size_t *env_len);
@ -322,35 +324,40 @@ bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, *cookiep);
if (*cookiep == NULL) {
/* Start search at initial file offset */
io_offset = 0x0;
} else {
/* Start search after the current entry */
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep);
/* Scan past the current entry by finding the next newline */
error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset);
if (error) {
BHND_NV_LOG("unexpected error in seek_eol(): %d\n",
error);
return (NULL);
}
}
/* Already at EOF? */
if (io_offset == io_size)
return (NULL);
/* Seek to the next entry (if any) */
if ((error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset))) {
BHND_NV_LOG("unexpected error in seek_eol(): %d\n", error);
return (NULL);
}
/* Seek to the first valid entry, or EOF */
if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) {
BHND_NV_LOG("unexpected error in seek_next(): %d\n", error);
return (NULL);
}
/* Provide the new cookie for this offset */
if (io_offset > UINTPTR_MAX) {
BHND_NV_LOG("io_offset > UINPTR_MAX!\n");
return (NULL);
}
*cookiep = (void *)(uintptr_t)io_offset;
/* Hit EOF? */
if (io_offset == io_size)
return (NULL);
/* Provide the new cookie for this offset */
*cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset);
/* Fetch the name pointer; it must be at least 1 byte long */
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL);
if (error) {
@ -362,6 +369,19 @@ bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
return (nptr);
}
static int
bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
if (cookiep1 < cookiep2)
return (-1);
if (cookiep1 > cookiep2)
return (1);
return (0);
}
static int
bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
@ -369,6 +389,13 @@ bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static int
bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
}
const void *
bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
@ -383,7 +410,7 @@ bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep);
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
/* At EOF? */
if (io_offset == io_size)
@ -429,7 +456,7 @@ bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep);
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
/* At EOF? */
if (io_offset == io_size)
@ -444,20 +471,51 @@ bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
return (ptr);
}
/* Convert cookie back to an I/O offset */
static size_t
bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
/**
* Return a cookiep for the given I/O offset.
*/
static void *
bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
size_t io_offset)
{
size_t io_size;
uintptr_t cval;
const void *ptr;
int error;
BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data),
("io_offset %zu out-of-range", io_offset));
BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL);
if (error)
BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
ptr = (const uint8_t *)ptr + io_offset;
return (__DECONST(void *, ptr));
}
/* Convert a cookiep back to an I/O offset */
static size_t
bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
{
const void *ptr;
intptr_t offset;
size_t io_size;
int error;
BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
io_size = bhnd_nvram_io_getsize(btxt->data);
cval = (uintptr_t)cookiep;
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
if (error)
BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)"));
BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)"));
offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
return ((size_t)cval);
return ((size_t)offset);
}
/* Determine the entry length and env 'key=value' string length of the entry
@ -584,3 +642,50 @@ bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
*offset += (p - baseptr);
return (0);
}
static int
bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
bhnd_nvram_val *str;
const char *inp;
bhnd_nvram_type itype;
size_t ilen;
int error;
/* Name (trimmed of any path prefix) must be valid */
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
return (EINVAL);
/* Value must be bcm-formatted string */
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
value, BHND_NVRAM_VAL_DYNAMIC);
if (error)
return (error);
/* Value string must not contain our record delimiter character ('\n'),
* or our comment character ('#') */
inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value"));
for (size_t i = 0; i < ilen; i++) {
switch (inp[i]) {
case '\n':
case '#':
BHND_NV_LOG("invalid character (%#hhx) in value\n",
inp[i]);
bhnd_nvram_val_release(str);
return (EINVAL);
}
}
/* Success. Transfer result ownership to the caller. */
*result = str;
return (0);
}
static int
bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
/* We permit deletion of any variable */
return (0);
}

View File

@ -666,25 +666,34 @@ bhnd_nvram_sprom_read_offset(struct bhnd_nvram_sprom *sp,
return (0);
}
/**
* Common variable decoding; fetches and decodes variable to @p val,
* using @p storage for actual data storage.
*
* The returned @p val instance will hold a borrowed reference to @p storage,
* and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
* the lifetime of @p storage.
*
* The caller is responsible for releasing any allocated value state
* via bhnd_nvram_val_release().
*/
static int
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type otype)
bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
{
bhnd_nvram_val val;
struct bhnd_nvram_sprom *sp;
struct sprom_opcode_idx *idx;
struct sprom_opcode_idx *entry;
const struct bhnd_nvram_vardefn *var;
union bhnd_nvram_sprom_storage storage;
union bhnd_nvram_sprom_storage *inp;
union bhnd_nvram_sprom_intv intv;
bhnd_nvram_type var_btype;
union bhnd_nvram_sprom_intv intv;
size_t ilen, ipos, iwidth;
size_t nelem;
bool all_bits_set;
int error;
sp = (struct bhnd_nvram_sprom *)nv;
idx = cookiep;
entry = cookiep;
BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
@ -699,7 +708,7 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
* canonical NVRAM variable definition, but some SPROM layouts may
* define a smaller element count.
*/
if ((error = sprom_opcode_parse_var(&sp->state, idx))) {
if ((error = sprom_opcode_parse_var(&sp->state, entry))) {
BHND_NV_LOG("variable evaluation failed: %d\n", error);
return (error);
}
@ -724,9 +733,9 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
}
ilen = nelem * iwidth;
/* Decode into our own local storage. */
inp = &storage;
if (ilen > sizeof(storage)) {
/* Decode into our caller's local storage */
inp = storage;
if (ilen > sizeof(*storage)) {
BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
"incorrect\n", var->name);
return (EFTYPE);
@ -739,7 +748,7 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
/*
* Decode the SPROM data, iteratively decoding up to nelem values.
*/
if ((error = sprom_opcode_state_seek(&sp->state, idx))) {
if ((error = sprom_opcode_state_seek(&sp->state, entry))) {
BHND_NV_LOG("variable seek failed: %d\n", error);
return (error);
}
@ -840,8 +849,9 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
/* Perform coercion of the array element */
nbyte = iwidth;
error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
intv_type, ptr, &nbyte, var_btype);
error = bhnd_nvram_value_coerce(&intv.u32,
sizeof(intv.u32), intv_type, ptr, &nbyte,
var_btype);
if (error)
return (error);
@ -871,13 +881,45 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
return (ENOENT);
/* Provide value wrapper */
return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
BHND_NVRAM_VAL_BORROW_DATA));
return (error);
}
/* Perform value coercion from our local representation */
error = bhnd_nvram_val_init(&val, var->fmt, inp, ilen, var->type,
BHND_NVRAM_VAL_BORROW_DATA);
static int
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
struct sprom_opcode_idx_entry *e1, *e2;
e1 = cookiep1;
e2 = cookiep2;
/* Use the index entry order; this matches the order of variables
* returned via bhnd_nvram_sprom_next() */
if (e1 < e2)
return (-1);
else if (e1 > e2)
return (1);
return (0);
}
static int
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type otype)
{
bhnd_nvram_val val;
union bhnd_nvram_sprom_storage storage;
int error;
/* Decode variable to a new value instance */
error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
if (error)
return (error);
/* Perform value coercion */
error = bhnd_nvram_val_encode(&val, buf, len, otype);
/* Clean up */
@ -885,6 +927,29 @@ bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (error);
}
static int
bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
bhnd_nvram_val val;
union bhnd_nvram_sprom_storage storage;
int error;
/* Decode variable to a new value instance */
error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
if (error)
return (error);
/* Attempt to copy to heap */
*value = bhnd_nvram_val_copy(&val);
bhnd_nvram_val_release(&val);
if (*value == NULL)
return (ENOMEM);
return (0);
}
static const void *
bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
@ -906,6 +971,21 @@ bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
return (var->name);
}
static int
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
// XXX TODO
return (ENXIO);
}
static int
bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
// XXX TODO
return (ENXIO);
}
/**
* Initialize SPROM opcode evaluation state.
*

View File

@ -82,6 +82,10 @@ struct bhnd_nvram_tlv_env {
(((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \
((_env)->hdr.size - sizeof((_env)->flags)))
/* Maximum supported length of the envp data field, in bytes */
#define NVRAM_TLV_ENVP_DATA_MAX_LEN \
(UINT8_MAX - sizeof(uint8_t) /* flags */)
static int bhnd_nvram_tlv_parse_size(
struct bhnd_nvram_io *io,
@ -367,6 +371,19 @@ bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
if (cookiep1 < cookiep2)
return (-1);
if (cookiep1 > cookiep2)
return (1);
return (0);
}
static int
bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
@ -374,6 +391,13 @@ bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static int
bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
}
static const void *
bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
@ -417,6 +441,61 @@ bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
return (&env->envp[0]);
}
static int
bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
bhnd_nvram_val *str;
const char *inp;
bhnd_nvram_type itype;
size_t ilen;
size_t name_len, tlv_nremain;
int error;
tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;
/* Name (trimmed of any path prefix) must be valid */
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
return (EINVAL);
/* 'name=' must fit within the maximum TLV_ENV record length */
name_len = strlen(name) + 1; /* '=' */
if (tlv_nremain < name_len) {
BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",
name);
return (EINVAL);
}
tlv_nremain -= name_len;
/* Convert value to a (bcm-formatted) string */
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
value, BHND_NVRAM_VAL_DYNAMIC);
if (error)
return (error);
/* The string value must fit within remaining TLV_ENV record length */
inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
if (tlv_nremain < ilen) {
BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n",
BHND_NV_PRINT_WIDTH(ilen), inp);
bhnd_nvram_val_release(str);
return (EINVAL);
}
tlv_nremain -= name_len;
/* Success. Transfer result ownership to the caller. */
*result = str;
return (0);
}
static int
bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
/* We permit deletion of any variable */
return (0);
}
/**
* Iterate over the records starting at @p next, returning the parsed
* record's @p tag, @p size, and @p offset.
@ -637,26 +716,43 @@ bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
static void *
bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
{
const void *ptr;
int error;
BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
("io_offset %zu out-of-range", io_offset));
BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
return ((void *)(uintptr_t)(io_offset));
error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);
if (error)
BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
ptr = (const uint8_t *)ptr + io_offset;
return (__DECONST(void *, ptr));
}
/* Convert a cookiep back to an I/O offset */
static size_t
bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
{
size_t io_size;
uintptr_t cval;
const void *ptr;
intptr_t offset;
size_t io_size;
int error;
BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
io_size = bhnd_nvram_io_getsize(tlv->data);
cval = (uintptr_t)cookiep;
BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)"));
BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)"));
error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);
if (error)
BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
return ((size_t)cval);
offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
return ((size_t)offset);
}

View File

@ -43,20 +43,21 @@
/** Registered NVRAM parser class instances. */
SET_DECLARE(bhnd_nvram_data_class_set, bhnd_nvram_data_class);
void *bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv,
const char *name);
int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv,
void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype);
/** @see bhnd_nvram_data_class_desc() */
typedef const char *(bhnd_nvram_data_op_class_desc)(void);
void *bhnd_nvram_data_generic_find(
struct bhnd_nvram_data *nv, const char *name);
int bhnd_nvram_data_generic_rp_getvar(
struct bhnd_nvram_data *nv, void *cookiep,
void *outp, size_t *olen, bhnd_nvram_type otype);
int bhnd_nvram_data_generic_rp_copy_val(
struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **val);
/** @see bhnd_nvram_data_probe() */
typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io);
/** @see bhnd_nvram_data_new() */
typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv,
struct bhnd_nvram_io *io);
struct bhnd_nvram_io *io);
/** Free all resources associated with @p nv. Called by
* bhnd_nvram_data_release() when the reference count reaches zero. */
@ -79,25 +80,45 @@ typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv);
/** @see bhnd_nvram_data_next() */
typedef const char *(bhnd_nvram_data_op_next)(struct bhnd_nvram_data *nv,
void **cookiep);
void **cookiep);
/** @see bhnd_nvram_data_find() */
typedef void *(bhnd_nvram_data_op_find)(struct bhnd_nvram_data *nv,
const char *name);
const char *name);
/** @see bhnd_nvram_data_copy_val() */
typedef int (bhnd_nvram_data_op_copy_val)(
struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value);
/** @see bhnd_nvram_data_getvar_order() */
typedef int (bhnd_nvram_data_op_getvar_order)(
struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2);
/** @see bhnd_nvram_data_getvar_name() */
typedef const char *(bhnd_nvram_data_op_getvar_name)(
struct bhnd_nvram_data *nv, void *cookiep);
struct bhnd_nvram_data *nv,
void *cookiep);
/** @see bhnd_nvram_data_getvar() */
typedef int (bhnd_nvram_data_op_getvar)(struct bhnd_nvram_data *nv,
void *cookiep, void *buf, size_t *len,
bhnd_nvram_type type);
void *cookiep, void *buf, size_t *len,
bhnd_nvram_type type);
/** @see bhnd_nvram_data_getvar_ptr() */
typedef const void *(bhnd_nvram_data_op_getvar_ptr)(
struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type);
struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type);
/** @see bhnd_nvram_data_filter_setvar() */
typedef int (bhnd_nvram_data_op_filter_setvar)(
struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result);
/** @see bhnd_nvram_data_filter_unsetvar() */
typedef int (bhnd_nvram_data_op_filter_unsetvar)(
struct bhnd_nvram_data *nv, const char *name);
/**
* NVRAM data class.
@ -105,18 +126,23 @@ typedef const void *(bhnd_nvram_data_op_getvar_ptr)(
struct bhnd_nvram_data_class {
const char *desc; /**< description */
size_t size; /**< instance size */
bhnd_nvram_data_op_probe *op_probe;
bhnd_nvram_data_op_new *op_new;
bhnd_nvram_data_op_free *op_free;
bhnd_nvram_data_op_count *op_count;
bhnd_nvram_data_op_size *op_size;
bhnd_nvram_data_op_serialize *op_serialize;
bhnd_nvram_data_op_caps *op_caps;
bhnd_nvram_data_op_next *op_next;
bhnd_nvram_data_op_find *op_find;
bhnd_nvram_data_op_getvar *op_getvar;
bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr;
bhnd_nvram_data_op_getvar_name *op_getvar_name;
bhnd_nvram_data_op_probe *op_probe;
bhnd_nvram_data_op_new *op_new;
bhnd_nvram_data_op_free *op_free;
bhnd_nvram_data_op_count *op_count;
bhnd_nvram_data_op_size *op_size;
bhnd_nvram_data_op_serialize *op_serialize;
bhnd_nvram_data_op_caps *op_caps;
bhnd_nvram_data_op_next *op_next;
bhnd_nvram_data_op_find *op_find;
bhnd_nvram_data_op_copy_val *op_copy_val;
bhnd_nvram_data_op_getvar_order *op_getvar_order;
bhnd_nvram_data_op_getvar *op_getvar;
bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr;
bhnd_nvram_data_op_getvar_name *op_getvar_name;
bhnd_nvram_data_op_filter_setvar *op_filter_setvar;
bhnd_nvram_data_op_filter_unsetvar *op_filter_unsetvar;
};
/**
@ -163,9 +189,13 @@ struct bhnd_nvram_data {
_macro(_cname, caps) \
_macro(_cname, next) \
_macro(_cname, find) \
_macro(_cname, copy_val) \
_macro(_cname, getvar_order) \
_macro(_cname, getvar) \
_macro(_cname, getvar_ptr) \
_macro(_cname, getvar_name)
_macro(_cname, getvar_name) \
_macro(_cname, filter_setvar) \
_macro(_cname, filter_unsetvar)
/**
* Define a bhnd_nvram_data_class with class name @p _n and description

View File

@ -73,14 +73,43 @@ MALLOC_DECLARE(M_BHND_NVRAM);
#define bhnd_nv_isxdigit(c) isxdigit(c)
#define bhnd_nv_toupper(c) toupper(c)
#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_WAITOK)
#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_NOWAIT)
#define bhnd_nv_calloc(n, size) malloc((n) * (size), M_BHND_NVRAM, \
M_WAITOK | M_ZERO)
M_NOWAIT | M_ZERO)
#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size), M_BHND_NVRAM, \
M_WAITOK)
M_NOWAIT)
#define bhnd_nv_free(buf) free((buf), M_BHND_NVRAM)
#define bhnd_nv_strdup(str) strdup(str, M_BHND_NVRAM)
#define bhnd_nv_strndup(str, len) strndup(str, len, M_BHND_NVRAM)
#define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), M_BHND_NVRAM, \
fmt, ## __VA_ARGS__)
/* We need our own strdup() implementation to pass required M_NOWAIT */
static inline char *
bhnd_nv_strdup(const char *str)
{
char *dest;
size_t len;
len = strlen(str);
dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT);
memcpy(dest, str, len);
dest[len] = '\0';
return (dest);
}
/* We need our own strndup() implementation to pass required M_NOWAIT */
static inline char *
bhnd_nv_strndup(const char *str, size_t len)
{
char *dest;
len = strnlen(str, len);
dest = malloc(len + 1, M_BHND_NVRAM, M_NOWAIT);
memcpy(dest, str, len);
dest[len] = '\0';
return (dest);
}
#ifdef INVARIANTS
#define BHND_NV_INVARIANTS
@ -121,12 +150,28 @@ MALLOC_DECLARE(M_BHND_NVRAM);
#define bhnd_nv_free(buf) free((buf))
#define bhnd_nv_strdup(str) strdup(str)
#define bhnd_nv_strndup(str, len) strndup(str, len)
#define bhnd_nv_asprintf(buf, fmt, ...) asprintf((buf), fmt, ## __VA_ARGS__)
#ifndef NDEBUG
#define BHND_NV_INVARIANTS
#endif
#define BHND_NV_ASSERT(expr, ...) assert(expr)
#ifdef BHND_NV_INVARIANTS
#define BHND_NV_ASSERT(expr, msg) do { \
if (!(expr)) { \
fprintf(stderr, "Assertion failed: %s, function %s, " \
"file %s, line %u\n", __STRING(expr), __FUNCTION__, \
__FILE__, __LINE__); \
BHND_NV_PANIC msg; \
} \
} while(0)
#else /* !BHND_NV_INVARIANTS */
#define BHND_NV_ASSERT(expr, msg)
#endif /* BHND_NV_INVARIANTS */
#define BHND_NV_VERBOSE (0)
#define BHND_NV_PANIC(fmt, ...) do { \
@ -211,8 +256,9 @@ size_t bhnd_nvram_parse_field(const char **inp,
size_t bhnd_nvram_trim_field(const char **inp,
size_t ilen, char delim);
bool bhnd_nvram_validate_name(const char *name,
size_t name_len);
const char *bhnd_nvram_trim_path_name(const char *name);
bool bhnd_nvram_validate_name(const char *name);
/**
* Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8

View File

@ -30,16 +30,22 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/hash.h>
#include <sys/queue.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/ctype.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
@ -59,11 +65,11 @@ __FBSDID("$FreeBSD$");
* Manages in-memory and persistent representations of NVRAM data.
*/
static int bhnd_nvram_sort_idx(void *ctx, const void *lhs,
const void *rhs);
static int bhnd_nvram_generate_index(struct bhnd_nvram_store *sc);
static void *bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc,
const char *name);
static int bhnd_nvstore_parse_data(
struct bhnd_nvram_store *sc);
static int bhnd_nvstore_parse_path_entries(
struct bhnd_nvram_store *sc);
/**
* Allocate and initialize a new NVRAM data store instance.
@ -92,28 +98,45 @@ bhnd_nvram_store_new(struct bhnd_nvram_store **store,
if (sc == NULL)
return (ENOMEM);
LIST_INIT(&sc->paths);
BHND_NVSTORE_LOCK_INIT(sc);
BHND_NVSTORE_LOCK(sc);
/* Initialize path hash table */
sc->num_paths = 0;
for (size_t i = 0; i < nitems(sc->paths); i++)
LIST_INIT(&sc->paths[i]);
/* Initialize alias hash table */
sc->num_aliases = 0;
for (size_t i = 0; i < nitems(sc->aliases); i++)
LIST_INIT(&sc->aliases[i]);
/* Retain the NVRAM data */
sc->nv = bhnd_nvram_data_retain(data);
sc->data = bhnd_nvram_data_retain(data);
sc->data_caps = bhnd_nvram_data_caps(data);
/* Allocate uncommitted change list */
sc->pending = nvlist_create(NV_FLAG_IGNORE_CASE);
if (sc->pending == NULL) {
error = ENOMEM;
goto cleanup;
}
/* Generate all indices */
if ((error = bhnd_nvram_generate_index(sc)))
/* Register required root path */
error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
BHND_NVSTORE_ROOT_PATH_LEN);
if (error)
goto cleanup;
BHND_NVSTORE_LOCK_INIT(sc);
sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,
BHND_NVSTORE_ROOT_PATH_LEN);
BHND_NV_ASSERT(sc->root_path, ("missing root path"));
/* Parse all variables vended by our backing NVRAM data instance,
* generating all path entries, alias entries, and variable indexes */
if ((error = bhnd_nvstore_parse_data(sc)))
goto cleanup;
*store = sc;
BHND_NVSTORE_UNLOCK(sc);
return (0);
cleanup:
BHND_NVSTORE_UNLOCK(sc);
bhnd_nvram_store_free(sc);
return (error);
}
@ -166,97 +189,339 @@ bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
void
bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
{
struct bhnd_nvstore_path *dpath, *dnext;
/* Clean up alias hash table */
for (size_t i = 0; i < nitems(sc->aliases); i++) {
bhnd_nvstore_alias *alias, *anext;
LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)
bhnd_nv_free(alias);
}
LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) {
bhnd_nv_free(dpath->path);
bhnd_nv_free(dpath);
}
/* Clean up path hash table */
for (size_t i = 0; i < nitems(sc->paths); i++) {
bhnd_nvstore_path *path, *pnext;
LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)
bhnd_nvstore_path_free(path);
}
if (sc->pending != NULL)
nvlist_destroy(sc->pending);
if (sc->data != NULL)
bhnd_nvram_data_release(sc->data);
if (sc->idx != NULL)
bhnd_nv_free(sc->idx);
if (sc->nv != NULL)
bhnd_nvram_data_release(sc->nv);
BHND_NVSTORE_LOCK_DESTROY(sc);
bhnd_nv_free(sc);
}
/**
* Parse all variables vended by our backing NVRAM data instance,
* generating all path entries, alias entries, and variable indexes.
*
* @param sc The NVRAM store instance to be initialized with
* paths, aliases, and data parsed from its backing
* data.
*
* @retval 0 success
* @retval non-zero if an error occurs during parsing, a regular unix error
* code will be returned.
*/
static int
bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
{
const char *name;
void *cookiep;
int error;
/* Parse and register all device paths and path aliases. This enables
* resolution of _forward_ references to device paths aliases when
* scanning variable entries below */
if ((error = bhnd_nvstore_parse_path_entries(sc)))
return (error);
/* Calculate the per-path variable counts, and report dangling alias
* references as an error. */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
bhnd_nvstore_path *path;
bhnd_nvstore_name_info info;
/* Parse the name info */
error = bhnd_nvstore_parse_name_info(name,
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
if (error)
return (error);
switch (info.type) {
case BHND_NVSTORE_VAR:
/* Fetch referenced path */
path = bhnd_nvstore_var_get_path(sc, &info);
if (path == NULL) {
BHND_NV_LOG("variable '%s' has dangling "
"path reference\n", name);
return (EFTYPE);
}
/* Increment path variable count */
if (path->num_vars == SIZE_MAX) {
BHND_NV_LOG("more than SIZE_MAX variables in "
"path %s\n", path->path_str);
return (EFTYPE);
}
path->num_vars++;
break;
case BHND_NVSTORE_ALIAS_DECL:
/* Skip -- path alias already parsed and recorded */
break;
}
}
/* If the backing NVRAM data instance vends only a single root ("/")
* path, we may be able to skip generating an index for the root
* path */
if (sc->num_paths == 1) {
bhnd_nvstore_path *path;
/* If the backing instance provides its own name-based lookup
* indexing, we can skip generating a duplicate here */
if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)
return (0);
/* If the sole root path contains fewer variables than the
* minimum indexing threshhold, we do not need to generate an
* index */
path = bhnd_nvstore_get_root_path(sc);
if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
return (0);
}
/* Allocate per-path index instances */
for (size_t i = 0; i < nitems(sc->paths); i++) {
bhnd_nvstore_path *path;
LIST_FOREACH(path, &sc->paths[i], np_link) {
path->index = bhnd_nvstore_index_new(path->num_vars);
if (path->index == NULL)
return (ENOMEM);
}
}
/* Populate per-path indexes */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
bhnd_nvstore_name_info info;
bhnd_nvstore_path *path;
/* Parse the name info */
error = bhnd_nvstore_parse_name_info(name,
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
if (error)
return (error);
switch (info.type) {
case BHND_NVSTORE_VAR:
/* Fetch referenced path */
path = bhnd_nvstore_var_get_path(sc, &info);
BHND_NV_ASSERT(path != NULL,
("dangling path reference"));
/* Append to index */
error = bhnd_nvstore_index_append(sc, path->index,
cookiep);
if (error)
return (error);
break;
case BHND_NVSTORE_ALIAS_DECL:
/* Skip */
break;
}
}
/* Prepare indexes for querying */
for (size_t i = 0; i < nitems(sc->paths); i++) {
bhnd_nvstore_path *path;
LIST_FOREACH(path, &sc->paths[i], np_link) {
error = bhnd_nvstore_index_prepare(sc, path->index);
if (error)
return (error);
}
}
return (0);
}
/**
* Parse and register path and path alias entries for all declarations found in
* the NVRAM data backing @p nvram.
*
* @param sc The NVRAM store instance.
*
* @retval 0 success
* @retval non-zero If parsing fails, a regular unix error code will be
* returned.
*/
static int
bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
{
const char *name;
void *cookiep;
int error;
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
/* Skip path registration if the data source does not support device
* paths. */
if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
return (0);
}
/* Otherwise, parse and register all paths and path aliases */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
bhnd_nvstore_name_info info;
/* Parse the name info */
error = bhnd_nvstore_parse_name_info(name,
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
if (error)
return (error);
/* Register the path */
error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
if (error) {
BHND_NV_LOG("failed to register path for %s: %d\n",
name, error);
return (error);
}
}
return (0);
}
/**
* Read an NVRAM variable.
*
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param[out] buf On success, the requested value will be written
* to this buffer. This argment may be NULL if
* the value is not desired.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
* @param type The requested data type to be written to @p buf.
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param[out] outp On success, the requested value will be written
* to this buffer. This argment may be NULL if
* the value is not desired.
* @param[in,out] olen The capacity of @p outp. On success, will be set
* to the actual size of the requested value.
* @param otype The requested data type to be written to
* @p outp.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
* small to hold the requested value.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
* small to hold the requested value.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
int
bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
void *buf, size_t *len, bhnd_nvram_type type)
void *outp, size_t *olen, bhnd_nvram_type otype)
{
void *cookiep;
const void *inp;
size_t ilen;
bhnd_nvram_type itype;
int error;
/*
* Search order:
*
* - uncommitted changes
* - index lookup OR buffer scan
*/
bhnd_nvstore_name_info info;
bhnd_nvstore_path *path;
bhnd_nvram_prop *prop;
void *cookiep;
int error;
BHND_NVSTORE_LOCK(sc);
/* Is variable marked for deletion? */
if (nvlist_exists_null(sc->pending, name)) {
BHND_NVSTORE_UNLOCK(sc);
return (ENOENT);
/* Parse the variable name */
error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
sc->data_caps, &info);
if (error)
goto finished;
/* Fetch the variable's enclosing path entry */
if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
error = ENOENT;
goto finished;
}
/* Does an uncommitted value exist? */
if (nvlist_exists_string(sc->pending, name)) {
/* Uncommited value exists, is not a deletion */
inp = nvlist_get_string(sc->pending, name);
ilen = strlen(inp) + 1;
itype = BHND_NVRAM_TYPE_STRING;
/* Search uncommitted updates first */
prop = bhnd_nvstore_path_get_update(sc, path, info.name);
if (prop != NULL) {
if (bhnd_nvram_prop_is_null(prop)) {
/* NULL denotes a pending deletion */
error = ENOENT;
} else {
error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
}
goto finished;
}
/* Coerce borrowed data reference before releasing
* our lock. */
error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len,
type);
/* Search the backing NVRAM data */
cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);
if (cookiep != NULL) {
/* Found in backing store */
error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,
otype);
goto finished;
}
BHND_NVSTORE_UNLOCK(sc);
/* Not found */
error = ENOENT;
finished:
BHND_NVSTORE_UNLOCK(sc);
return (error);
}
/**
* Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
* implementation.
*
* If @p value is NULL, the variable will be marked for deletion.
*/
static int
bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
bhnd_nvram_val *value)
{
bhnd_nvstore_path *path;
bhnd_nvstore_name_info info;
int error;
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
/* Parse the variable name */
error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
sc->data_caps, &info);
if (error)
return (error);
} else if (nvlist_exists(sc->pending, name)) {
BHND_NV_PANIC("invalid value type for pending change %s", name);
}
/* Fetch variable from parsed NVRAM data. */
if ((cookiep = bhnd_nvram_index_lookup(sc, name)) == NULL) {
BHND_NVSTORE_UNLOCK(sc);
return (ENOENT);
}
/* Fetch the variable's enclosing path entry */
if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
return (error);
/* Let the parser itself perform value coercion */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type);
/* Register the update entry */
return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
}
/**
* Set an NVRAM variable.
*
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param value The new value.
*
* @retval 0 success
* @retval ENOENT The requested variable @p name was not found.
* @retval EINVAL If @p value is invalid.
*/
int
bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
bhnd_nvram_val *value)
{
int error;
BHND_NVSTORE_LOCK(sc);
error = bhnd_nvram_store_setval_common(sc, name, value);
BHND_NVSTORE_UNLOCK(sc);
return (error);
@ -267,310 +532,56 @@ bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
*
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param[out] buf The new value.
* @param[in,out] len The size of @p buf.
* @param type The data type of @p buf.
* @param[out] inp The new value.
* @param[in,out] ilen The size of @p inp.
* @param itype The data type of @p inp.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval EINVAL If @p len does not match the expected variable size.
* @retval ENOENT The requested variable @p name was not found.
* @retval EINVAL If the new value is invalid.
* @retval EINVAL If @p name is read-only.
*/
int
bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type)
const void *inp, size_t ilen, bhnd_nvram_type itype)
{
const char *inp;
char vbuf[512];
bhnd_nvram_val val;
int error;
/* Verify name validity */
if (!bhnd_nvram_validate_name(name, strlen(name)))
error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
if (error) {
BHND_NV_LOG("error initializing value: %d\n", error);
return (EINVAL);
/* Verify buffer size alignment for the given type. If this is a
* variable width type, a width of 0 will always pass this check */
if (len % bhnd_nvram_value_size(buf, len, type, 1) != 0)
return (EINVAL);
/* Determine string format (or directly add variable, if a C string) */
switch (type) {
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:
case BHND_NVRAM_TYPE_NULL:
case BHND_NVRAM_TYPE_DATA:
case BHND_NVRAM_TYPE_BOOL:
case BHND_NVRAM_TYPE_UINT8_ARRAY:
case BHND_NVRAM_TYPE_UINT16_ARRAY:
case BHND_NVRAM_TYPE_UINT32_ARRAY:
case BHND_NVRAM_TYPE_UINT64_ARRAY:
case BHND_NVRAM_TYPE_INT8_ARRAY:
case BHND_NVRAM_TYPE_INT16_ARRAY:
case BHND_NVRAM_TYPE_INT32_ARRAY:
case BHND_NVRAM_TYPE_INT64_ARRAY:
case BHND_NVRAM_TYPE_CHAR_ARRAY:
case BHND_NVRAM_TYPE_STRING_ARRAY:
case BHND_NVRAM_TYPE_BOOL_ARRAY:
// TODO: non-char/string value support
return (EOPNOTSUPP);
case BHND_NVRAM_TYPE_CHAR:
case BHND_NVRAM_TYPE_STRING:
inp = buf;
/* Must not exceed buffer size */
if (len > sizeof(vbuf))
return (EINVAL);
/* Must have room for a trailing NUL */
if (len == sizeof(vbuf) && inp[len-1] != '\0')
return (EINVAL);
/* Copy out the string value and append trailing NUL */
strlcpy(vbuf, buf, len);
/* Add to pending change list */
BHND_NVSTORE_LOCK(sc);
nvlist_add_string(sc->pending, name, vbuf);
BHND_NVSTORE_UNLOCK(sc);
}
return (0);
}
BHND_NVSTORE_LOCK(sc);
error = bhnd_nvram_store_setval_common(sc, name, &val);
BHND_NVSTORE_UNLOCK(sc);
/* sort function for bhnd_nvstore_index cookie values */
static int
bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs)
{
struct bhnd_nvram_store *sc;
const char *l_str, *r_str;
bhnd_nvram_val_release(&val);
sc = ctx;
/* Fetch string pointers from the cookiep values */
l_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)lhs);
r_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)rhs);
/* Perform comparison */
return (strcasecmp(l_str, r_str));
return (error);
}
/**
* Parse and register all device paths and path aliases in @p nvram.
* Unset an NVRAM variable.
*
* @param sc The NVRAM parser state.
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
*
* @retval 0 success
* @retval non-zero If registering device paths fails, a regular unix
* error code will be returned.
* @retval ENOENT The requested variable @p name was not found.
* @retval EINVAL If @p name is read-only.
*/
static int
bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc)
int
bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
{
const char *name;
void *cookiep;
int error;
int error;
/* Skip if backing parser does not support device paths */
if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS))
return (0);
BHND_NVSTORE_LOCK(sc);
error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
BHND_NVSTORE_UNLOCK(sc);
/* Parse and register all device path aliases */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(sc->nv, &cookiep))) {
struct bhnd_nvstore_path *devpath;
const char *suffix;
char *eptr;
char *path;
size_t path_len;
u_long index;
path = NULL;
/* Check for devpath prefix */
if (strncmp(name, "devpath", strlen("devpath")) != 0)
continue;
/* Parse index value that should follow a 'devpath' prefix */
suffix = name + strlen("devpath");
index = strtoul(suffix, &eptr, 10);
if (eptr == suffix || *eptr != '\0') {
BHND_NV_LOG("invalid devpath variable '%s'\n", name);
continue;
}
/* Determine path value length */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, NULL, &path_len,
BHND_NVRAM_TYPE_STRING);
if (error)
return (error);
/* Allocate path buffer */
if ((path = bhnd_nv_malloc(path_len)) == NULL)
return (ENOMEM);
/* Decode to our new buffer */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, path, &path_len,
BHND_NVRAM_TYPE_STRING);
if (error) {
bhnd_nv_free(path);
return (error);
}
/* Register path alias */
devpath = bhnd_nv_malloc(sizeof(*devpath));
if (devpath == NULL) {
bhnd_nv_free(path);
return (ENOMEM);
}
devpath->index = index;
devpath->path = path;
LIST_INSERT_HEAD(&sc->paths, devpath, dp_link);
}
return (0);
}
/**
* Generate all indices for the NVRAM data backing @p nvram.
*
* @param sc The NVRAM parser state.
*
* @retval 0 success
* @retval non-zero If indexing @p nvram fails, a regular unix
* error code will be returned.
*/
static int
bhnd_nvram_generate_index(struct bhnd_nvram_store *sc)
{
const char *name;
void *cookiep;
size_t idx_bytes;
size_t num_vars;
int error;
/* Parse and register all device path aliases */
if ((error = bhnd_nvram_register_devpaths(sc)))
return (error);
/* Skip generating a variable index if threshold is not met ... */
num_vars = bhnd_nvram_data_count(sc->nv);
if (num_vars < NVRAM_IDX_VAR_THRESH)
return (0);
/* ... or if the backing data instance implements indexed lookup
* internally */
if (bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_INDEXED)
return (0);
/* Allocate and populate variable index */
idx_bytes = sizeof(struct bhnd_nvstore_index) +
(sizeof(void *) * num_vars);
sc->idx = bhnd_nv_malloc(idx_bytes);
if (sc->idx == NULL) {
BHND_NV_LOG("error allocating %zu byte index\n", idx_bytes);
goto bad_index;
}
sc->idx->num_cookiep = num_vars;
#ifdef _KERNEL
if (bootverbose) {
BHND_NV_LOG("allocated %zu byte index for %zu variables\n",
idx_bytes, num_vars);
}
#endif /* _KERNEL */
cookiep = NULL;
for (size_t i = 0; i < sc->idx->num_cookiep; i++) {
/* Fetch next entry */
name = bhnd_nvram_data_next(sc->nv, &cookiep);
/* Early EOF */
if (name == NULL) {
BHND_NV_LOG("indexing failed, expected %zu records "
"(got %zu)\n", sc->idx->num_cookiep, i+1);
goto bad_index;
}
/* Save the variable's cookiep */
sc->idx->cookiep[i] = cookiep;
}
/* Sort the index table */
qsort_r(sc->idx->cookiep, sc->idx->num_cookiep,
sizeof(sc->idx->cookiep[0]), sc, bhnd_nvram_sort_idx);
return (0);
bad_index:
/* Fall back on non-indexed access */
BHND_NV_LOG("reverting to non-indexed variable lookup\n");
if (sc->idx != NULL) {
bhnd_nv_free(sc->idx);
sc->idx = NULL;
}
return (0);
}
/**
* Perform an index lookup of @p name, returning the associated cookie
* value, or NULL if the variable does not exist.
*
* @param sc The NVRAM parser state.
* @param name The variable to search for.
*/
static void *
bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, const char *name)
{
void *cookiep;
const char *indexed_name;
size_t min, mid, max;
int order;
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
if (sc->idx == NULL || sc->idx->num_cookiep == 0)
return (bhnd_nvram_data_find(sc->nv, name));
/*
* Locate the requested variable using a binary search.
*/
BHND_NV_ASSERT(sc->idx->num_cookiep > 0,
("empty array causes underflow"));
min = 0;
max = sc->idx->num_cookiep - 1;
while (max >= min) {
/* Select midpoint */
mid = (min + max) / 2;
cookiep = sc->idx->cookiep[mid];
/* Determine which side of the partition to search */
indexed_name = bhnd_nvram_data_getvar_name(sc->nv, cookiep);
order = strcasecmp(indexed_name, name);
if (order < 0) {
/* Search upper partition */
min = mid + 1;
} else if (order > 0) {
/* Search (non-empty) lower partition */
if (mid == 0)
break;
max = mid - 1;
} else if (order == 0) {
/* Match found */
return (cookiep);
}
}
/* Not found */
return (NULL);
return (error);
}

View File

@ -35,12 +35,8 @@
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/nv.h>
#else /* !_KERNEL */
#include <errno.h>
#include <nv.h>
#include <stdint.h>
#include <stdlib.h>
#endif
@ -49,6 +45,7 @@
#include "bhnd_nvram_data.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_value.h"
struct bhnd_nvram_store;
@ -60,9 +57,15 @@ int bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
void bhnd_nvram_store_free(struct bhnd_nvram_store *store);
int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
void *buf, size_t *len, bhnd_nvram_type type);
void *outp, size_t *olen, bhnd_nvram_type otype);
int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type);
const void *inp, size_t ilen, bhnd_nvram_type itype);
int bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc,
const char *name);
int bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
bhnd_nvram_val *value);
#endif /* _BHND_NVRAM_BHND_NVRAM_STORE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -38,49 +38,246 @@
#include <pthread.h>
#endif
#include "bhnd_nvram_plist.h"
#include "bhnd_nvram_store.h"
/** Index is only generated if minimum variable count is met */
#define NVRAM_IDX_VAR_THRESH 15
#define BHND_NV_IDX_VAR_THRESHOLD 15
#define BHND_NVSTORE_PATH_ALIAS_NONE ULONG_MAX
#define BHND_NVSTORE_ROOT_PATH "/"
#define BHND_NVSTORE_ROOT_PATH_LEN sizeof(BHND_NVSTORE_ROOT_PATH)
LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_path);
#define BHND_NVSTORE_GET_FLAG(_value, _flag) \
(((_value) & BHND_NVSTORE_ ## _flag) != 0)
#define BHND_NVSTORE_GET_BITS(_value, _field) \
((_value) & BHND_NVSTORE_ ## _field ## _MASK)
/* Forward declarations */
typedef struct bhnd_nvstore_name_info bhnd_nvstore_name_info;
typedef struct bhnd_nvstore_index bhnd_nvstore_index;
typedef struct bhnd_nvstore_path bhnd_nvstore_path;
typedef struct bhnd_nvstore_alias bhnd_nvstore_alias;
typedef struct bhnd_nvstore_alias_list bhnd_nvstore_alias_list;
typedef struct bhnd_nvstore_update_list bhnd_nvstore_update_list;
typedef struct bhnd_nvstore_path_list bhnd_nvstore_path_list;
LIST_HEAD(bhnd_nvstore_alias_list, bhnd_nvstore_alias);
LIST_HEAD(bhnd_nvstore_update_list, bhnd_nvstore_update);
LIST_HEAD(bhnd_nvstore_path_list, bhnd_nvstore_path);
/**
* NVRAM store path.
* NVRAM store variable entry types.
*/
struct bhnd_nvstore_path {
char *path; /** relative path */
u_long index; /** aliased path index, or
BHND_NVSTORE_PATH_IDX_INVALID */
typedef enum {
BHND_NVSTORE_VAR = 0, /**< simple variable (var=...) */
BHND_NVSTORE_ALIAS_DECL = 1, /**< alias declaration ('devpath0=pci/1/1') */
} bhnd_nvstore_var_type;
LIST_ENTRY(bhnd_nvstore_path) dp_link;
/**
* NVRAM path descriptor types.
*/
typedef enum {
BHND_NVSTORE_PATH_STRING = 0, /**< path is a string value */
BHND_NVSTORE_PATH_ALIAS = 1 /**< path is an alias reference */
} bhnd_nvstore_path_type;
/**
* NVRAM variable namespaces.
*/
typedef enum {
BHND_NVSTORE_NAME_INTERNAL = 1, /**< internal namespace. permits
use of reserved devpath and
alias name prefixes. */
BHND_NVSTORE_NAME_EXTERNAL = 2, /**< external namespace. forbids
use of name prefixes used
for device path handling */
} bhnd_nvstore_name_type;
bhnd_nvstore_path *bhnd_nvstore_path_new(const char *path_str,
size_t path_len);
void bhnd_nvstore_path_free(struct bhnd_nvstore_path *path);
bhnd_nvstore_index *bhnd_nvstore_index_new(size_t capacity);
void bhnd_nvstore_index_free(bhnd_nvstore_index *index);
int bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,
bhnd_nvstore_index *index,
void *cookiep);
int bhnd_nvstore_index_prepare(
struct bhnd_nvram_store *sc,
bhnd_nvstore_index *index);
void *bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc,
bhnd_nvstore_index *index, const char *name);
bhnd_nvstore_path *bhnd_nvstore_get_root_path(
struct bhnd_nvram_store *sc);
bool bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc,
bhnd_nvstore_path *path);
void *bhnd_nvstore_path_data_next(
struct bhnd_nvram_store *sc,
bhnd_nvstore_path *path, void **indexp);
void *bhnd_nvstore_path_data_lookup(
struct bhnd_nvram_store *sc,
bhnd_nvstore_path *path, const char *name);
bhnd_nvram_prop *bhnd_nvstore_path_get_update(
struct bhnd_nvram_store *sc,
bhnd_nvstore_path *path, const char *name);
int bhnd_nvstore_path_register_update(
struct bhnd_nvram_store *sc,
bhnd_nvstore_path *path, const char *name,
bhnd_nvram_val *value);
bhnd_nvstore_alias *bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc,
const char *path);
bhnd_nvstore_alias *bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc,
u_long alias_val);
bhnd_nvstore_path *bhnd_nvstore_get_path(struct bhnd_nvram_store *sc,
const char *path, size_t path_len);
bhnd_nvstore_path *bhnd_nvstore_resolve_path_alias(
struct bhnd_nvram_store *sc, u_long aval);
bhnd_nvstore_path *bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc,
bhnd_nvstore_name_info *info);
int bhnd_nvstore_var_register_path(
struct bhnd_nvram_store *sc,
bhnd_nvstore_name_info *info, void *cookiep);
int bhnd_nvstore_register_path(struct bhnd_nvram_store *sc,
const char *path, size_t path_len);
int bhnd_nvstore_register_alias(
struct bhnd_nvram_store *sc,
const bhnd_nvstore_name_info *info, void *cookiep);
const char *bhnd_nvstore_parse_relpath(const char *parent,
const char *child);
int bhnd_nvstore_parse_name_info(const char *name,
bhnd_nvstore_name_type name_type,
uint32_t data_caps, bhnd_nvstore_name_info *info);
/**
* NVRAM variable name descriptor.
*
* For NVRAM data instances supporting BHND_NVRAM_DATA_CAP_DEVPATHS, the
* NVRAM-vended variable name will be in one of four formats:
*
* - Simple Variable:
* 'variable'
* - Device Variable:
* 'pci/1/1/variable'
* - Device Alias Variable:
* '0:variable'
* - Device Path Alias Definition:
* 'devpath0=pci/1/1/variable'
*
* Device Paths:
*
* The device path format is device class-specific; the known supported device
* classes are:
* - sb: BCMA/SIBA SoC core device path.
* - pci: PCI device path (and PCIe on some earlier devices).
* - pcie: PCIe device path.
* - usb: USB device path.
*
* The device path format is loosely defined as '[class]/[domain]/[bus]/[slot]',
* with missing values either assumed to be zero, a value specific to the
* device class, or irrelevant to the device class in question.
*
* Examples:
* sb/1 BCMA/SIBA backplane 0, core 1.
* pc/1/1 PCMCIA bus 1, slot 1
* pci/1/1 PCI/PCIe domain 0, bus 1, device 1
* pcie/1/1 PCIe domain 0, bus 1, device 1
* usb/0xbd17 USB PID 0xbd17 (VID defaults to Broadcom 0x0a5c)
*
* Device Path Aliases:
*
* Device path aliases reduce duplication of device paths in the flash encoding
* of NVRAM data; a single devpath[alias]=[devpath] variable entry is defined,
* and then later variables may reference the device path via its alias:
* devpath1=usb/0xbd17
* 1:mcs5gpo0=0x1100
*
* Alias values are always positive, base 10 integers.
*/
struct bhnd_nvstore_name_info {
const char *name; /**< variable name */
bhnd_nvstore_var_type type; /**< variable type */
bhnd_nvstore_path_type path_type; /**< path type */
/** Path information */
union {
/* BHND_NVSTORE_PATH_STRING */
struct {
const char *value; /**< device path */
size_t value_len; /**< device path length */
} str;
/** BHND_NVSTORE_PATH_ALIAS */
struct {
u_long value; /**< device alias */
} alias;
} path;
};
/**
* NVRAM store index.
* NVRAM variable index.
*
* Provides effecient name-based lookup by maintaining an array of cached
* cookiep values, sorted lexicographically by variable name.
* cookiep values, sorted lexicographically by relative variable name.
*/
struct bhnd_nvstore_index {
size_t num_cookiep; /**< cookiep count */
void *cookiep[]; /**< cookiep values */
size_t count; /**< entry count */
size_t capacity; /**< entry capacity */
void *cookiep[]; /**< cookiep values */
};
/**
* NVRAM device path.
*/
struct bhnd_nvstore_path {
char *path_str; /**< canonical path string */
size_t num_vars; /**< per-path count of committed
(non-pending) variables */
bhnd_nvstore_index *index; /**< per-path index, or NULL if
this is a root path for
which the data source
may be queried directly. */
bhnd_nvram_plist *pending; /**< pending changes */
LIST_ENTRY(bhnd_nvstore_path) np_link;
};
/**
* NVRAM device path alias.
*/
struct bhnd_nvstore_alias {
bhnd_nvstore_path *path; /**< borrowed path reference */
void *cookiep; /**< NVRAM variable's cookiep value */
u_long alias; /**< alias value */
LIST_ENTRY(bhnd_nvstore_alias) na_link;
};
/** bhnd nvram store instance state */
struct bhnd_nvram_store {
#ifdef _KERNEL
struct mtx mtx;
struct mtx mtx;
#else
pthread_mutex_t mtx;
pthread_mutex_t mtx;
#endif
struct bhnd_nvram_data *nv; /**< backing data */
struct bhnd_nvstore_index *idx; /**< index, or NULL */
struct bhnd_nvstore_paths paths; /**< paths */
nvlist_t *pending; /**< uncommitted writes */
struct bhnd_nvram_data *data; /**< backing data */
uint32_t data_caps; /**< data capability flags */
bhnd_nvstore_alias_list aliases[4]; /**< path alias hash table */
size_t num_aliases; /**< alias count */
bhnd_nvstore_path *root_path; /**< root path instance */
bhnd_nvstore_path_list paths[4]; /**< path hash table */
size_t num_paths; /**< path count */
};
#ifdef _KERNEL

View File

@ -614,44 +614,45 @@ bhnd_nvram_get_vardefn(size_t id)
* Scans for special characters (path delimiters, value delimiters, path
* alias prefixes), returning false if the given name cannot be used
* as a relative NVRAM key.
*
*
* @param name A relative NVRAM variable name to validate.
* @param name_len The length of @p name, in bytes.
*
* @retval true If @p name is a valid relative NVRAM key.
* @retval false If @p name should not be used as a relative NVRAM key.
*/
bool
bhnd_nvram_validate_name(const char *name, size_t name_len)
bhnd_nvram_validate_name(const char *name)
{
size_t limit;
limit = strnlen(name, name_len);
if (limit == 0)
/* Reject path-prefixed variable names */
if (bhnd_nvram_trim_path_name(name) != name)
return (false);
/* Disallow path alias prefixes ([0-9]+:.*) */
if (limit >= 2 && bhnd_nv_isdigit(*name)) {
for (const char *p = name; (size_t)(p - name) < limit; p++) {
if (bhnd_nv_isdigit(*p))
continue;
else if (*p == ':')
return (false);
else
break;
}
/* Reject device path alias declarations (devpath[1-9][0-9]*.*\0) */
if (strncmp(name, "devpath", strlen("devpath")) == 0) {
const char *p;
char *endp;
/* Check for trailing [1-9][0-9]* */
p = name + strlen("devpath");
strtoul(p, &endp, 10);
if (endp != p)
return (false);
}
/* Scan for special characters */
for (const char *p = name; (size_t)(p - name) < limit; p++) {
/* Scan for [^A-Za-z_0-9] */
for (const char *p = name; *p != '\0'; p++) {
switch (*p) {
case '/': /* path delimiter */
case '=': /* key=value delimiter */
return (false);
/* [0-9_] */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '_':
break;
/* [A-Za-z] */
default:
if (!isascii(*p) || bhnd_nv_isspace(*p))
if (!bhnd_nv_isalpha(*p))
return (false);
break;
}
}
@ -949,6 +950,46 @@ bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base,
return (0);
}
/**
* Trim leading path (pci/1/1) or path alias (0:) prefix from @p name, if any,
* returning a pointer to the start of the relative variable name.
*
* @par Examples
*
* - "/foo" -> "foo"
* - "dev/pci/foo" -> "foo"
* - "0:foo" -> "foo"
* - "foo" -> "foo"
*
* @param name The string to be trimmed.
*
* @return A pointer to the start of the relative variable name in @p name.
*/
const char *
bhnd_nvram_trim_path_name(const char *name)
{
char *endp;
/* path alias prefix? (0:varname) */
if (bhnd_nv_isdigit(*name)) {
/* Parse '0...:' alias prefix, if it exists */
strtoul(name, &endp, 10);
if (endp != name && *endp == ':') {
/* Variable name follows 0: prefix */
return (endp+1);
}
}
/* device path prefix? (pci/1/1/varname) */
if ((endp = strrchr(name, '/')) != NULL) {
/* Variable name follows the final path separator '/' */
return (endp+1);
}
/* variable name is not prefixed */
return (name);
}
/**
* Parse a 'name=value' string.
*

View File

@ -39,6 +39,7 @@ SRCS+= bhnd_nvram_data.c \
bhnd_nvram_iores.c \
bhnd_nvram_plist.c \
bhnd_nvram_store.c \
bhnd_nvram_store_subr.c \
bhnd_nvram_subr.c \
bhnd_nvram_value.c \
bhnd_nvram_value_fmts.c \